Java-Gaming.org Hi !
Featured games (83)
games approved by the League of Dukes
Games in Showcase (541)
Games in Android Showcase (133)
games submitted by our members
Games in WIP (603)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Sending advanced data over network -Using Serializable?  (Read 5422 times)
0 Members and 1 Guest are viewing this topic.
Offline hejfelix

Junior Devvie





« Posted 2008-12-20 17:19:31 »

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 Smiley In advance I ask of you to read the entire post before replying.

<i8b4uUnderground> d-_-b
<BonyNoMore> how u make that inverted b?
<BonyNoMore> wait
<BonyNoMore> never mind
Offline princec

« JGO Spiffy Duke »


Medals: 439
Projects: 3
Exp: 16 years


Eh? Who? What? ... Me?


« Reply #1 - Posted 2008-12-20 19:57:13 »

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 Smiley

Offline hejfelix

Junior Devvie





« Reply #2 - Posted 2008-12-20 20:09:50 »

To start with, just have one class, and use it for both client and server...

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 Cheesy. (it's better to be lazy than sloppy!)

<i8b4uUnderground> d-_-b
<BonyNoMore> how u make that inverted b?
<BonyNoMore> wait
<BonyNoMore> never mind
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Online cylab

JGO Ninja


Medals: 55



« Reply #3 - Posted 2008-12-21 01:07:09 »

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" Smiley). 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.

Mathias - I Know What [you] Did Last Summer!
Offline hejfelix

Junior Devvie





« Reply #4 - Posted 2008-12-21 08:51:17 »

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..?

<i8b4uUnderground> d-_-b
<BonyNoMore> how u make that inverted b?
<BonyNoMore> wait
<BonyNoMore> never mind
Online cylab

JGO Ninja


Medals: 55



« Reply #5 - Posted 2008-12-21 10:22:36 »

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.

Mathias - I Know What [you] Did Last Summer!
Offline hejfelix

Junior Devvie





« Reply #6 - Posted 2008-12-21 14:10:44 »

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! Smiley

Soo....I'm using serializable on this class:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
/*
 * 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:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
         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?

<i8b4uUnderground> d-_-b
<BonyNoMore> how u make that inverted b?
<BonyNoMore> wait
<BonyNoMore> never mind
Offline princec

« JGO Spiffy Duke »


Medals: 439
Projects: 3
Exp: 16 years


Eh? Who? What? ... Me?


« Reply #7 - Posted 2008-12-21 14:24:31 »

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 Smiley

Offline hejfelix

Junior Devvie





« Reply #8 - Posted 2008-12-21 14:30:11 »

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 Smiley

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?

<i8b4uUnderground> d-_-b
<BonyNoMore> how u make that inverted b?
<BonyNoMore> wait
<BonyNoMore> never mind
Online cylab

JGO Ninja


Medals: 55



« Reply #9 - Posted 2008-12-21 16:28:24 »

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.

Mathias - I Know What [you] Did Last Summer!
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline sunsett

Senior Devvie




ribbit!


« Reply #10 - Posted 2008-12-22 13:37:21 »

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.
Offline princec

« JGO Spiffy Duke »


Medals: 439
Projects: 3
Exp: 16 years


Eh? Who? What? ... Me?


« Reply #11 - Posted 2008-12-22 16:51:28 »

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

Cas Smiley

Offline hejfelix

Junior Devvie





« Reply #12 - Posted 2008-12-22 17:45:25 »

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.

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.... Smiley 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... Sad

<i8b4uUnderground> d-_-b
<BonyNoMore> how u make that inverted b?
<BonyNoMore> wait
<BonyNoMore> never mind
Offline sunsett

Senior Devvie




ribbit!


« Reply #13 - Posted 2008-12-22 19:02:38 »

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
Offline hejfelix

Junior Devvie





« Reply #14 - Posted 2008-12-22 19:31:44 »

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.

<i8b4uUnderground> d-_-b
<BonyNoMore> how u make that inverted b?
<BonyNoMore> wait
<BonyNoMore> never mind
Offline Renoria

Junior Devvie




...


« Reply #15 - Posted 2008-12-23 09:10:30 »

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

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
Offline hejfelix

Junior Devvie





« Reply #16 - Posted 2008-12-23 11:43:03 »

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

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 Smiley

<i8b4uUnderground> d-_-b
<BonyNoMore> how u make that inverted b?
<BonyNoMore> wait
<BonyNoMore> never mind
Offline Renoria

Junior Devvie




...


« Reply #17 - Posted 2008-12-23 11:46:48 »

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 Smiley

Its not hard for me :

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
        
       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.
Offline hejfelix

Junior Devvie





« Reply #18 - Posted 2008-12-23 12:06:43 »

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.

<i8b4uUnderground> d-_-b
<BonyNoMore> how u make that inverted b?
<BonyNoMore> wait
<BonyNoMore> never mind
Offline sunsett

Senior Devvie




ribbit!


« Reply #19 - Posted 2008-12-23 15:02:43 »

That's sort of the idea of JGN except without putting the burden on the developer JGN does it all auto-magically for you.  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.
Offline Renoria

Junior Devvie




...


« Reply #20 - Posted 2008-12-23 23:08:44 »

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.

Do what you like, just keep in mind that ObjectInput/OutputStream are much slower than using byte streams due to unessecary data being sent.
Offline sunsett

Senior Devvie




ribbit!


« Reply #21 - Posted 2008-12-24 03:19:39 »

Renoria, I totally agree that it's sending a lot of excess information, but what you suggested is a lot of manual conversion for the developer where Reflection can take all the pain away for you.
Offline Renoria

Junior Devvie




...


« Reply #22 - Posted 2008-12-24 05:51:28 »

Renoria, I totally agree that it's sending a lot of excess information, but what you suggested is a lot of manual conversion for the developer where Reflection can take all the pain away for you.

Yeah, but sending all that other crap (package names, method names) etc is a big waste of bandwidth, while using a simple byte stream writer doesn't use nearly as much, and you can actually control what is being sent/recieved. I would probably wrap the InputStream into a java.io.DataInput/OutputStream to use methods such as writeInt, writeByte, writeShort, readInt, readShort etc.

You could also use Apache MINA, xSocket or Riven's NIO Wrapper, or any other NIO/IO Lib.
Offline hejfelix

Junior Devvie





« Reply #23 - Posted 2008-12-24 10:58:56 »

Ok, guys I have a serious problem now. I really can't seem to solve this mystery.

Whenever I send strings, string-arrays or the like via ObjectOutputStream.writeObject() it goes super smooth(0-1 ms delay on localhost), but then whenever I send byte[]'s with the write() command, I get a delay of 80 ms. It is only delayed when the server sends back another byte[] to the client. I have 1 server and one client right now, and I have spent hours without finding the problem...
1  
2  
3  
4  
5  
6  
7  
8  
9  
    private void handleCommon() {
        sendObject("ok");
        ActorState as = new ActorState();
        as.bUnSerialize(getBytes());

        store.putState(name, as);      
//        sendBytes(store.getState(name).bSerialize());
}
    }


in the above code, I have commented out the sendBytes command, and the delay is 1 ms for sending and receiving the array. When I uncomment it so it is called I get 80 ms of delay. Can someone tell me what could be wrong? It shouldnt take longer for my server to send the bytes than for my client right?

What makes this weird is the fact that it works for String[] arrays which in my world should be MUUUCH bigger than byte-arrays... The data in the byte[] arrays is just 4 doubles an 2 ints.

Here is some more code specific to this problem(I'm sorry its really nasty cuz im debugging it right now):

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
    private void sendBytes(byte[] b) {
        try {
//            out.flush();
            out.write(b);
            out.flush();
//            System.out.println("server>" + msg);
        } catch (IOException ioException) {
            ioException.printStackTrace();
        }
    }

    private byte[] getBytes() {
        try {
            byte[] b = new byte[800];
            in.read(b);
            return b;
        } catch (Exception e) {
            System.out.println(e);
            System.exit(1);
        }
        return null;
    }

<i8b4uUnderground> d-_-b
<BonyNoMore> how u make that inverted b?
<BonyNoMore> wait
<BonyNoMore> never mind
Offline princec

« JGO Spiffy Duke »


Medals: 439
Projects: 3
Exp: 16 years


Eh? Who? What? ... Me?


« Reply #24 - Posted 2008-12-24 12:18:25 »

Perhaps the problem is the way you read the bytes... you're asking the inputstream for up to 800 bytes, maybe it's sitting there waiting for a tiny bit to see if there are any more to come. See if using read(byte[], int, int) fixes it by specifying exactly how many bytes you're expecting.

Cas Smiley

Offline hejfelix

Junior Devvie





« Reply #25 - Posted 2008-12-24 13:11:53 »

Perhaps the problem is the way you read the bytes... you're asking the inputstream for up to 800 bytes, maybe it's sitting there waiting for a tiny bit to see if there are any more to come. See if using read(byte[], int, int) fixes it by specifying exactly how many bytes you're expecting.

Cas Smiley

pretty sure this isnt the problem. I used to do it using "redObject" and "writeObject" casting them for whatever I was sending. This gave me the exact same delay(80 ms). And I dont think i'm asking for anything by handing in the 800 size aray, because it just edits the first 0-n spots in the array(one would think), so as long as the array is big enough, i'm safe.

<i8b4uUnderground> d-_-b
<BonyNoMore> how u make that inverted b?
<BonyNoMore> wait
<BonyNoMore> never mind
Offline Renoria

Junior Devvie




...


« Reply #26 - Posted 2008-12-24 15:01:27 »

How about you write the length of the array as a byte/short then write the elements using for.

Write using:

writeByte/Short(length)
for (int i = 0; i<thing.length; i++) {
 writeByte/whatever(thing);
}

Read using:

byte/short num = readByte()/Short();
byte[] thing = new byte[num];

for (int i =0; i<num; i++) {
  thing = readByte()/whatever();
}

OR you could :

    private byte[] getBytes() {
        try {
            byte[] b = new byte[in.available()]; //how much data available?
            in.read(b);
            return b;
        } catch (Exception e) {
            System.out.println(e);
            System.exit(1);
        }
        return null;
    }

OR if you wanna be newbie:

private byte[] getBytes() throws Exception {
 byte[] r = new byte[2048];
 byte haha;
 int i = 0;
 while ((haha = in.read()) != -1) { //-1 delimiter
  r = haha;
  i++;
 }
 byte ret[] = new byte[r.length];
 for (int k = 0; k < r.length; k++) {
  ret[k] = r[k];
 }
 return ret;
}
Online Riven
« League of Dukes »

« JGO Overlord »


Medals: 848
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #27 - Posted 2008-12-24 16:57:11 »

Object{Input|Output}Stream is pretty highlevel, which means there can be a lot of things happening when you write(byte[]).

You could use Data{Input|Output}Stream (with readUTF/writeUTF) and see how that performs.


Did you turn OFF Nagle's Algorithm? ( Socket.setTcpNoDelay(true) )
If so, every out.write(...) is 1 packet, so turn it ON: Socket.setTcpNoDelay(false), and flush() after the write.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social
Offline Renoria

Junior Devvie




...


« Reply #28 - Posted 2008-12-25 01:43:21 »

If you want to write a string, use this:

1  
2  
3  
4  
writeShort/Byte(str.length());
for (int i = 0; i < str.length(); i++) {
  writeByte(str.charAt(i));
}


Then to read:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
char[] read;

int len = readShort/Byte();
 
read = new char[len];

for (int i = 0; i < len; i++) {
  read[i] = (char) (readByte());
}

return String.valueOf(read);


That reads the string.
Offline sunsett

Senior Devvie




ribbit!


« Reply #29 - Posted 2008-12-25 02:45:56 »

Yeah, but sending all that other crap (package names, method names) etc is a big waste of bandwidth, while using a simple byte stream writer doesn't use nearly as much, and you can actually control what is being sent/recieved. I would probably wrap the InputStream into a java.io.DataInput/OutputStream to use methods such as writeInt, writeByte, writeShort, readInt, readShort etc.

You could also use Apache MINA, xSocket or Riven's NIO Wrapper, or any other NIO/IO Lib.

My point was in reference back to Reflection, not using Java's built-in Java Object Serialization crap.  JGN uses custom reflection code so you don't deal with any of the extra garbage that get sent across and it's really a very simple concept.
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

Mr.CodeIt (10 views)
2014-12-27 04:03:04

TheDudeFromCI (13 views)
2014-12-27 02:14:49

Mr.CodeIt (25 views)
2014-12-23 03:34:11

rwatson462 (56 views)
2014-12-15 09:26:44

Mr.CodeIt (46 views)
2014-12-14 19:50:38

BurntPizza (92 views)
2014-12-09 22:41:13

BurntPizza (113 views)
2014-12-08 04:46:31

JscottyBieshaar (83 views)
2014-12-05 12:39:02

SHC (94 views)
2014-12-03 16:27:13

CopyableCougar4 (102 views)
2014-11-29 21:32:03
Resources for WIP games
by kpars
2014-12-18 10:26:14

Understanding relations between setOrigin, setScale and setPosition in libGdx
by mbabuskov
2014-10-09 22:35:00

Definite guide to supporting multiple device resolutions on Android (2014)
by mbabuskov
2014-10-02 22:36:02

List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!