Java-Gaming.org    
Featured games (79)
games approved by the League of Dukes
Games in Showcase (475)
Games in Android Showcase (106)
games submitted by our members
Games in WIP (530)
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  
  [Kryonet] Advantages to simple packets  (Read 2228 times)
0 Members and 1 Guest are viewing this topic.
Offline Mads

JGO Ninja


Medals: 26
Projects: 3
Exp: 6 years


One for all!


« Posted 2012-06-08 05:53:28 »

Hey everyone!

I don't know how Kryonet works under the hood, so I don't know if this is wasting/double-doing something. That's what I am trying to
find out.

If I make my packets simple, then I don't need to register as many classes. I'm thinking that I can simplify until it's just a byte[buffer_size],
and then handle those bytes appropiately when they come in. This way I can use Kryonet as low-level as any other library, but it's way easier to set up, and I still enjoy how easy it is to work with.

Now, if I do that, will I be wasting resources? Is there any reason for me not to do this? How big is the overhead on a class, and am I avoiding any overhead when doing this?

Thank you for your insight.

Cheers Smiley

Offline sproingie

JGO Kernel


Medals: 201



« Reply #1 - Posted 2012-06-08 06:15:55 »

If all you're doing is transferring raw bytes, then you don't need KryoNet, any TCP socket will do.  Kryo's serialization is quite efficient, so I suggest you just try it as you would ideally use it at a high level and see if it's actually a problem.

Offline Nate

JGO Kernel


Medals: 145
Projects: 4
Exp: 14 years


Esoteric Software


« Reply #2 - Posted 2012-06-08 07:01:26 »

Kryo does serialization very similar to what you would write by hand. Eg, take this class:

1  
2  
3  
4  
public class Moo {
   public int damage;
   public String name;
}

Kryo will write an int ID that represents the class using variable length encoding, so it will be 1 byte. Then it writes an int for "damage" using variable length encoding, so numbers closer to zero will be 1 byte, 2 bytes further away, etc. If you know the damage is always positive, you can tell Kryo and it will write the int more efficiently for positive numbers. Next it writes the name string. If the string is short and it detects ASCII, it will use a 7 bit encoding with the 8th bit to denote if there is another character. Otherwise it will write the number of characters, then the UTF8 characters. The code to do all this would be:

1  
kryo.writeObject(output, moo);

You would be hard pressed to represent the information more efficiently by hand and certainly not with less code. Kryo uses code generation to access fields if they are public, protected, or default access, so doing that is recommended. Serialization speed is not going to be your bottleneck, but doing it by hand is faster. Kryo can still help when doing it by hand. There are two ways to do that, like this...

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
public class Moo implements KryoSerializable {
   public int damage;
   public String name;

   public void write (Kryo kryo, Output output) {
      output.writeInt(damage, true);
      output.writeString(name);
   }

   public void read (Kryo kryo, Input input) {
      damage = input.readInt(true);
      name = input.readString();
   }
}

Or like this:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
kryo.register(Moo.class, new Serializer<Moo>() {
   public void write (Kryo kryo, Output output, Moo moo) {
      output.writeInt(moo.damage, true);
      output.writeString(moo.name);
   }

   public Moo read (Kryo kryo, Input input, Class<Moo> type) {
      Moo moo = new Moo();
      moo.damage = input.readInt(true);
      moo.name = input.readString();
      return moo;
   }
});

The parameter name for the true passed to write/readInt is named "optimizePositive", which gets you the variable length encoding. Pass nothing for a 4 byte int. If you know the string is ASCII you can call writeAscii, otherwise writeString does the magic described above to detect ASCII or write UTF8. Either way, you always use readString.

If you have ArrayLists, Maps, or other objects, Kryo can do most or all of the work for you, without a speed penalty. I can post an example if you want.

It sounds like you are only passing objects over the network, but if you are putting data into a database, you might think about how your data will evolve. If you add a field, all the data in your database won't deserialize. See that link for more. If hand rolling serialization, you can do this...

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
public class Moo implements KryoSerializable {
   public int damage;
   public String name;

   public void write (Kryo kryo, Output output) {
      output.writeInt(damage, true);
      output.writeString(name);
      output.writeBoolean(false);
   }

   public void read (Kryo kryo, Input input) {
      damage = input.readInt(true);
      name = input.readString();
      input.readBoolean();
   }
}

Now when you add a field later:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
public class Moo implements KryoSerializable {
   public int damage;
   public String name;
   public float newField;

   public void write (Kryo kryo, Output output) {
      output.writeInt(damage, true);
      output.writeString(name);
      output.writeBoolean(true);
      output.writeFloat(newField);
      output.writeBoolean(false);
   }

   public void read (Kryo kryo, Input input) {
      damage = input.readInt(true);
      name = input.readString();
      if (input.readBoolean()) {
         newField = input.readFloat();
         input.readBoolean();
      }
   }
}

This costs 1 extra byte, but means you can additively evolve your classes without invalidating previously serialized bytes. Note you don't need this for objects that are only going over the network, only for objects going to longer term storage (disk/prefs/db/etc).

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Mads

JGO Ninja


Medals: 26
Projects: 3
Exp: 6 years


One for all!


« Reply #3 - Posted 2012-06-15 17:45:41 »

Kryo does serialization very similar to what you would write by hand. Eg, take this class:

1  
2  
3  
4  
public class Moo {
   public int damage;
   public String name;
}

Kryo will write an int ID that represents the class using variable length encoding, so it will be 1 byte. Then it writes an int for "damage" using variable length encoding, so numbers closer to zero will be 1 byte, 2 bytes further away, etc. If you know the damage is always positive, you can tell Kryo and it will write the int more efficiently for positive numbers. Next it writes the name string. If the string is short and it detects ASCII, it will use a 7 bit encoding with the 8th bit to denote if there is another character. Otherwise it will write the number of characters, then the UTF8 characters. The code to do all this would be:

1  
kryo.writeObject(output, moo);

You would be hard pressed to represent the information more efficiently by hand and certainly not with less code. Kryo uses code generation to access fields if they are public, protected, or default access, so doing that is recommended. Serialization speed is not going to be your bottleneck, but doing it by hand is faster. Kryo can still help when doing it by hand. There are two ways to do that, like this...

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
public class Moo implements KryoSerializable {
   public int damage;
   public String name;

   public void write (Kryo kryo, Output output) {
      output.writeInt(damage, true);
      output.writeString(name);
   }

   public void read (Kryo kryo, Input input) {
      damage = input.readInt(true);
      name = input.readString();
   }
}

Or like this:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
kryo.register(Moo.class, new Serializer<Moo>() {
   public void write (Kryo kryo, Output output, Moo moo) {
      output.writeInt(moo.damage, true);
      output.writeString(moo.name);
   }

   public Moo read (Kryo kryo, Input input, Class<Moo> type) {
      Moo moo = new Moo();
      moo.damage = input.readInt(true);
      moo.name = input.readString();
      return moo;
   }
});

The parameter name for the true passed to write/readInt is named "optimizePositive", which gets you the variable length encoding. Pass nothing for a 4 byte int. If you know the string is ASCII you can call writeAscii, otherwise writeString does the magic described above to detect ASCII or write UTF8. Either way, you always use readString.

If you have ArrayLists, Maps, or other objects, Kryo can do most or all of the work for you, without a speed penalty. I can post an example if you want.

It sounds like you are only passing objects over the network, but if you are putting data into a database, you might think about how your data will evolve. If you add a field, all the data in your database won't deserialize. See that link for more. If hand rolling serialization, you can do this...

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
public class Moo implements KryoSerializable {
   public int damage;
   public String name;

   public void write (Kryo kryo, Output output) {
      output.writeInt(damage, true);
      output.writeString(name);
      output.writeBoolean(false);
   }

   public void read (Kryo kryo, Input input) {
      damage = input.readInt(true);
      name = input.readString();
      input.readBoolean();
   }
}

Now when you add a field later:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
public class Moo implements KryoSerializable {
   public int damage;
   public String name;
   public float newField;

   public void write (Kryo kryo, Output output) {
      output.writeInt(damage, true);
      output.writeString(name);
      output.writeBoolean(true);
      output.writeFloat(newField);
      output.writeBoolean(false);
   }

   public void read (Kryo kryo, Input input) {
      damage = input.readInt(true);
      name = input.readString();
      if (input.readBoolean()) {
         newField = input.readFloat();
         input.readBoolean();
      }
   }
}

This costs 1 extra byte, but means you can additively evolve your classes without invalidating previously serialized bytes. Note you don't need this for objects that are only going over the network, only for objects going to longer term storage (disk/prefs/db/etc).

Thank you very much for your thorough reply. I wasn't even thinking about using Kryo for my database as well, but it's certainly better than textfiles and a parser.

This answered all my questions, and I'm very happy. I did take note on one specific thing you said. In the examples, the packets are recognized by an instanceof statement. However, I really don't like the idea of that. Instead now, I have a lookup array for handlers, and every packet carries an opcode byte that determines the index of the handler in the array.

However, you just said Kryo assigns an value to recognize packets aswell. That leads me to think that I'm (again) doing something that is already done for me. Is there any way I can access that value, when a packet comes in? 

Offline Nate

JGO Kernel


Medals: 145
Projects: 4
Exp: 14 years


Esoteric Software


« Reply #4 - Posted 2012-06-15 22:30:05 »

Thank you very much for your thorough reply. I wasn't even thinking about using Kryo for my database as well, but it's certainly better than textfiles and a parser.

This answered all my questions, and I'm very happy. I did take note on one specific thing you said. In the examples, the packets are recognized by an instanceof statement. However, I really don't like the idea of that. Instead now, I have a lookup array for handlers, and every packet carries an opcode byte that determines the index of the handler in the array.

However, you just said Kryo assigns an value to recognize packets aswell. That leads me to think that I'm (again) doing something that is already done for me. Is there any way I can access that value, when a packet comes in? 
Sure
1  
2  
3  
4  
5  
server.addListener(new Listener() {
   public void received (Connection connection, Object object) {
      int id = connection.getEndPoint().getKryo().getRegistration(object.getClass()).getId();
   }
});

You can do the same on the client. Instead of "connection.getEndPoint()" you can just use the server or client reference. The IDs come from when you register the class. They can be auto generated or specified explicitly:

1  
2  
int id = server.getKryo().register(SomeClass.class).getId();
server.getKryo().register(SomeClass.class, 12);


You could also do it differently. Your classes could all implement an interface that has a getter that returns an ID. The getter would return any value you want to identify the class. It wouldn't use a field, so it wouldn't be serialized, or if you want to use a field then mark it transient.

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.

ctomni231 (34 views)
2014-07-18 06:55:21

Zero Volt (30 views)
2014-07-17 23:47:54

danieldean (25 views)
2014-07-17 23:41:23

MustardPeter (27 views)
2014-07-16 23:30:00

Cero (42 views)
2014-07-16 00:42:17

Riven (44 views)
2014-07-14 18:02:53

OpenGLShaders (32 views)
2014-07-14 16:23:47

Riven (33 views)
2014-07-14 11:51:35

quew8 (30 views)
2014-07-13 13:57:52

SHC (66 views)
2014-07-12 17:50:04
HotSpot Options
by dleskov
2014-07-08 03:59:08

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:58:24

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:47:22

How do I start Java Game Development?
by ra4king
2014-05-17 11:13:37

HotSpot Options
by Roquen
2014-05-15 09:59:54

HotSpot Options
by Roquen
2014-05-06 15:03:10

Escape Analysis
by Roquen
2014-04-29 22:16:43

Experimental Toys
by Roquen
2014-04-28 13:24:22
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!