Sending advanced data over network -Using Serializable?

Hey guys,

im making a…GASP game, and I need to send advanced info through the network. For example I would like to send an object of the type “Player” so that the client only needs to draw it and that’s that. This would mean sending a position-vector, velocity vector, current frame of animation and/or other things.

How would I go around doing this in a way so I wont have to write tons of code to convert between serverside and clientside information?

I have some kind of idea that the Serializable interface might come in handy, meaning I would have to implement it and find a way to serialize the information I need for my server/client communication.

Another problem I have is: How do you avoid having a duplicate class for the server and client when sharing objects of the same type? One solution would be to only send primitives or standard java classes, then convert them at clientside…this will result in lots of coding work with boring conversion stuff…

Please consider these TWO questions :slight_smile: In advance I ask of you to read the entire post before replying.

To start with, just have one class, and use it for both client and server. Secondly, simply implement the Serializable interface - it’s got no methods - and ensure all your member variables are themselves also Serializable, or marked transient. And it’ll just magically work.

Later on you can get clever with Externalizable and so on to optimise performance.

Cas :slight_smile:

Can you elaborate on this part? I don’t understand what you mean by this. And thanks for your insight on Serializable, im a lazy coder so that sounds very neat not having to do any extra implementing hehe :D. (it’s better to be lazy than sloppy!)

Create a jar containing the classes you want to use on the server and the client and include that one in the classpath of both.

Said in another way: create 3 projects in your IDE. A library jar, say “mygame-common” a server project “mygame-server” and the game itself (“mygame” :)). Then you declare “mygame-common” in the dependencies of your other two projects.

This way, you can use the same classes for both, your server and your client, and when you create your deployment distribution, the IDE copies the generated “mygame-common.jar” to the distribution directories of both projects.

So in practise, I would just compile the “common” project and then dont do anything else. Then for the other projects I would just add the jar from the standard build-folder of the common project? This way I wouldnt have to do anything else than building the common classes and the running my other projects for them to see the new classes of the common package…?

Yes. But normally you can directly add projects as dependencies in IDEs (I use NetBeans). This way you don’t have to bother with recompilation and jar copying.

Ahh, thanks for the tip. I use NetBeans myself! :slight_smile:

Soo…I’m using serializable on this class:


/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package common.actor;

import common.utils.Vector2D;
import java.io.Serializable;

/**
 *
 * @author felix
 */
public class ActorState implements Serializable {
    
    public Vector2D pos = new Vector2D(0,0);
    public Vector2D vel = new Vector2D(0,0);
    public int state = 0;
    public int currentFrame = 0;
    
    public ActorState()
    {
        
    }
    
    public ActorState(Vector2D pos, Vector2D vel, int state, int currentFrame)
    {
        this.pos = pos.clone();
        this.vel = vel;
        this.state = state;
        this.currentFrame = currentFrame;
    }

}


But for some reason, this is slow as hell… Some insight please?

The cod seems to stall at this bit:


         public Object getObject() 
        {
            try
            {
            return in.readObject();
            }catch(Exception e)
            {
                System.out.println(e);
                return "0,0,0";
            }
        }     

according to my profiler. Same thing for the serverside, so I’m guessing it takes a long time sending the ActorState-object between the client and server. I don’t see why this would be so slow?

There’s a lot of stuff going on under the hood with buffers and the like…

make sure you flush() your output stream when you’re ready to send a bunch of data to the client. That ensures that everything gets sent.

Cas :slight_smile:

I am flushing after each call to send.

*edit

I just did my own testing. It seems it takes 80 milliseconds to transfer the serialized object from client to server and the other way too…Thats 160 miliseconds for one flow of communication…wtf?

Achtually you shouldn’t call flush() after every object sent. Just after you are finished with a state update. Also dont close and reopen connections after sending an object. Keep the connection open and just reconnect, if it is lost or the game quits.

Sending Serialized objects with the standard ObjectOutputStream is really inefficient since it sends across the complete package name and a lot of extraneous information. If you care about real-time performance at all I would recommend against it. I ended up writing my own solution in JGN using Reflection that ended up being light-years faster than Java’s built-in functionality because it first negotiates all the classes that will be communicated and assigns short values to them so from then on it doesn’t have to send anything but a short referencing what the class is and the actual field data of the object.

Yeah but… first make it work eh? Then worry about how fast it is.

Cas :slight_smile:

Would you feel like sharing your code for your own serializing method? I used Reflection for some database-persistance library, and I must say I don’t feel like going through hell AGAIN… :slight_smile: I hope you will share it with us/me.

edit

just looked at the JGN package, it seems like its a shitload to swallow when all I need is a small system to serialize an object in a compact manner… :frowning:

Fair enough. JGN does contain a lot of functionality, but at its core it’s just a glorified bean serializer/deserializer.

It was recently re-worked by another developer, but take a look at this:
http://jgn.googlecode.com/svn/core/trunk/src/com/captiveimagination/jgn/convert/BeanConverter.java

ok, this thread has been very rewarding for me. I will consider using the JGN and possibly the reworked code you posted. On the other hand, I see some benefits from implementing some specific network-communication, due to the added control of exactly what data is being sent. Sending a String-arrays or int-arrays and then in turn parsing them on the other side would give me total control and minimal overhead of what data is being sent. On the other hand, this is a heavy burden for the programmer, meaning implementing new functions could be a pain. However, I might be able to create some generalized state-objects and create a dedicated serialization scheme for this particular object. This would be reusable for both particles, actors and what not.

Does anyone have experience with the “coding specific serialization” plan? I think this is what I will try since I have tried it for a simple application, and the performance was perfect. It supported at least 50 updates on 10 clients each second.

Why don’t you just take apart your object’s properties, write them to a ByteBuffer, then reassemble it on the client/server? :smiley:

That makes sure you won’t send any unessecary stuff.

for example:

writeInt(object.getX());
writeInt(object.getY())’
writeByte(object.getFrame());

Then to reassemble

Thing t = new Thing();
t.setX(readInt());
t.setY(readInt());
t.setFrame(readByte());

or something like that

Actually that is what I mean by specific serialization, just implementing each case of the transmission. Just like your example, but that could be hard work for the programmer in the long run. It IS fast though, so I guess ill go with that idea :slight_smile:

Its not hard for me :

        
       plew.writeInt(chr.getId()); // character id

        plew.writeRenoriaAsciiString(chr.getName());

        plew.write(chr.getGender()); // gender (0 = male, 1 = female)

        plew.write(chr.getSkinColor().getId()); // skin color

        plew.writeInt(chr.getFace()); // face

        plew.writeInt(chr.getHair()); // hair

        plew.writeShort(chr.getLevel()); // level

        plew.writeShort(chr.getProfession().getId()); // profession

        plew.writeShort(chr.getStr()); // str

        plew.writeShort(chr.getDex()); // dex

        plew.writeShort(chr.getInt()); // int

        plew.writeShort(chr.getLuk()); // luk

        plew.writeShort(chr.getEnd()); //endure

        plew.writeShort(chr.getAgl()); //agility

        plew.writeShort(chr.getHp()); // hp

        plew.writeShort(chr.getMaxHp()); // maxhp

        plew.writeShort(chr.getMp()); // mp

        plew.writeShort(chr.getMaxMp()); // maxmp

        plew.writeShort(chr.getAp()); // remaining ap

        plew.writeShort(chr.getSp()); // remaining sp

        plew.writeInt(chr.getExp()); // current exp

        plew.writeShort(chr.getReputation()); // rep

        plew.writeInt(1); // current map id

        plew.write(chr.getSpawnPoint()); // spawnpoint

Very simple and easy.

XD I’m not meaning its difficult, but look at how many lines you have. Plus you need an equal amount of function calls at the client-side. I’d rather serialize the data, send it once and then unserialize it at clientside for example. I will post some code once I have thought through my idea.

That’s sort of the idea of JGN except without putting the burden on the developer JGN does it all auto-magically for you. :stuck_out_tongue:

The preferred way is to create beans that extend the Message class and simply contains getters / setters for data that you want to transfer across and it handles all the serialization/deserialization for you. The idea is to remove clutter and specific networking crap from game development to let the developers focus on the rest of the game instead of wasting time writing networking code. On the other end you simply add MessageListeners to listen for specific messages and that’s it.