KryoNet some questions.

Sometimes I send a packet where I only set like 3 fields out of 9. (Client)->(Server) and then respond with sending a packet using all 9 fields to the client('s) (Server)->(Client)

Does it send all the fields even when they’re null/-1?
I don’t want to be sending unneccessary bytes.

If this is the case. Would I be better off overriding the send methods and make it do something like this?

void sendPacketTCP(Packet packet){
sendTCP(packet.getOpcode());
sendTCP(packet.getPayload());
}

I choose for kryonet because easy and understandable to setup.

Kryo will write a predefined NULL-byte if an object is null: https://github.com/EsotericSoftware/kryo/blob/master/src/com/esotericsoftware/kryo/Kryo.java


public class Kryo {
	static public final byte NULL = 0;
	[...]
	public void writeObjectOrNull (Output output, Object object, Class type) {
		[...]
		output.writeByte(NULL);
		[...]
	}

But only if the used Serializer allows this. You can write your serializer that does not allow null values.

Thanks a ton!
Any tutorial/examples on how write custom serializers?

This is an simple example with the LibGDX Vector3-class:


package de.vatterger.entitysystem.util.serializer;

import com.badlogic.gdx.math.Vector3;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.Serializer;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;

//This Serializer works with Vector3 only, you need to change all the types to your specific class
public class Vector3Serializer extends Serializer<Vector3>{
	//This method constructs an object of the specified type from the data you put into the output in "write(...)"
	@Override
	public Vector3 read(Kryo kryo, Input in, Class<Vector3> vectorClass) {
		//create new Object and read in the values in the same order you've written them
		return new Vector3(in.readFloat(), in.readFloat(), in.readFloat());
	}
	
	//This method serializes an object by writing the relevant data to the output.
	@Override
	public void write(Kryo kryo, Output out, Vector3 vec) {
		//write values into output
		out.writeFloat(vec.x);
		out.writeFloat(vec.y);
		out.writeFloat(vec.z);
	}
}

You can also write objects and many different primitive types, just have a look at the methods of Input/Output:

You can also use other serializers inside your serializer like this:


package de.vatterger.entitysystem.util.serializer;

import com.artemis.utils.Bag;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.Serializer;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;

import static com.esotericsoftware.kryo.serializers.DefaultArraySerializers.*;

//This will serialize the artemis-odb Bag class, a ArrayList-like data structure
public class BagSerializer extends Serializer<Bag<?>>{

	//other serializer
	ObjectArraySerializer oas = new ObjectArraySerializer();
	
	public BagSerializer() {
		//set some options
		//There can be different objects in this array!
		oas.setElementsAreSameType(false);
		//And they could be null!
		oas.setElementsCanBeNull(true);
	}
	
	@Override
	public Bag<?> read(Kryo kryo, Input in, Class<Bag<?>> bagClass) {
		Object[] content = kryo.readObject(in, Object[].class, oas);
		Bag<Object> bag = new Bag<Object>(content.length);
		for (int i = 0; i < content.length; i++) {
			bag.add(content[i]);
		}
		return bag;
	}
	
	@Override
	public void write(Kryo kryo, Output out, Bag<?> bag) {
		kryo.writeObject(out, bag.getData(), oas);
	}
}

Then just register your object with an instance of your Serializer:


kryo.register(Vector3.class, new Vector3Serializer());

I have a couple of packets where I don’t always fill in the fields as they aren’t always needed.
Would I have to write serializers for all of those?

Nope, kryo can handle null fields you can just register without a custom one.
But why would you send the packets like that in the first place?
Sending instances of different classes causes no overhead as long as you don’t have hundreds of classes registered.

What I mean is this.


      public class AddPlayerToRoom implements Packet {

	public int roomID;//We only send this field to the server, because when we want to join we have to tell which room it is.
	
        //when we add other players to the client we need to tell the other's position and index.
        public int x, y;
	public int index;

	@Override
	public void handle(Player player, UserConnection connection) {
		do..

	}

Sorry If I am not being clear.

As far as I can tell, you would need those ‘x’ and ‘y’ variables no matter the case. When you connect to a ‘room’, the server has to know where to place you (if it does not generate coordinates for your player by itself), so why would you not need those variables? I can’t think of an instance where you would connect to a ‘room’ and not have to also send the position to the server to update the other clients.

Because the server generates a spawn point, we wouldn’t want a client to be able to spawn himself into the other’s base right…

Sure, if you are looking to prevent those kinds of things then that’s fine. In that case, you should split that packet up. Let’s look through some steps:

1.) Send a packet to the server with only the roomID.
2.) Server receives packet, generates spawn coordinates.
3.) Server sends a ‘NewPlayer’ (or whatever you choose to name it) packet to ALL clients.
4.) Clients receive packet, add this player to the ‘local’ world.
5.) Your client takes control of this newly created player, so you can now interact with the game

So, you’ll need two packets for this system. No need to check if the variables are null upon receiving the packet or any of that, if you ever come across that then you know you have a design flaw. Splitting logic up (to a point) is a good idea, don’t try to squash everything together.

I get your point, I probably will make a 2 sub packages in packet.impl called in and out
I guess you’re right, it’s a design flaw