The best way to transmit an entitys state?

Hey there, monkies.



At the moment i work myself into networking and feel quite comfortable with Spidermonkey. The mainpart of the work is already done. Server and client connect and transfer messages to my will. evil laugh



Well… chrm

Now I’m thinking about some basic things concerning entity states.

I can have several entities in my scene. These are authoratively spawned by the server and managed by my EntityManager class. It works quite well in the theory and stuff.

Since i simulate physics on the server its only natural i need to transmit states of entities quite often.



Which information should be send is clear to me… but not what is the best way to send it. I found, that i can transmit messages zipped and im going to use this. (Am i right that i need to create a instance of a ZIPCompressedMessage and simply use the setMessage() method and then send the ZIPCompressedMessage?)



Now I think about the storing of the state in the message. Here my real question starts:



Should i store the state as single attributes or should i better parse them into one string to safe place?



I’m not quite sure what is the best way to manage this kind of things. What i know till here i got from reading in the forums network and spidermonkey and some articles out there in the wide space of the internet.



Every help appreciated :slight_smile:

This is a pretty wide open topic. I’d probably avoid zipping the messages unless you start running into space problems. No reason to add latency if your messages end up being small, anyway. Hold that option in your back pocket in case you need it.



What do you mean by “one string”?



You should be sending your data in as small a form as possible… binary. SpiderMonkey will already compact fields for you so you may not need to worry too much about this. For example, a Vector3f in a message will already only take float * 3 in size (96 bits) without any extra overhead. In fact, trying to be clever in that case and sending a float[3] actually takes slightly more space in the message since it has to send the array size.



Common places to gain savings are cases where code uses an int to be convenient but a short or a byte will work over the network. For example, a game that will never have more then 65,000 entities may not need to transmit the entity ID as an int if it knows how to marshal the short instead.



And the “wide open topic” part is deciding what is sent reliably and what is sent unreliably and the strategy for balancing in between. There are a hundred tradeoffs. For example, if you send all entity update message reliably then you only have to send changes. If you are sending them unreliably then you need to make sure your entire world/zone state is updated. Or find some combination.



If your entity count is relatively small then you might just start by sending separate unreliable update messages… frequently. Get something working, see how it performs, and then circle back to bundle them into single “world state” messages or whatever as performance dictates.

Okay…

The optimazation with typing is clear. But a Vector3f seriously is smaller than an array?



What i meant by “one String” is the following:

There are several things defining an entitys state. There are networking vars (which i use sparly since they could cause some overhead but are sometimes quite usefull), positions, rotations, velocity (for prediction and smoothing and stuff… ) and eventually some more.

I dont know how Spidermonkey sends messages over the network but i was afraid that it uses something like the serializer interface (even though it dosnt use it. I looked it up… ^^).

If the message was encoded like

[java]

name {

attribute=value

attribute2=value2

attribute3=value3{

attribute3.1=value3.1

}

}

[/java]



It would be better to parse all attributes into one string so the created message would be as small as possible.

But since you said that spidermonkey does optimazation and stuff by itself i guess its better to let it handle this itself.





Another question your post threw up:

You meant if i send my updatemessages unreliable i would have to update my whole worldstate? Why is that?

I wanted to update everything which moves or changes it state in some way.Maybe with some optimization concerning the players viewfrustum so only things visible to the player are updated.

Could you please explain this point to me since i dont really understand why it should not work…?





Thanks in advance. Your last post helped me a lot. (:

Of course you can only update the changed stuff, but you have to be sure every static stuff have arrived. That is the problem with unreliable messages, you cannot be sure if every message arrive. The viewfrustum idea sounds somewhat good, that might work depending of the game you do. However I don’t know what takes longest time to do, the frustum check or sending messages. One problem is that if the players makes sounds, you can only hear what you see.

KuroSei said:
Okay...
The optimazation with typing is clear. But a Vector3f seriously is smaller than an array?


Serializer can determine that the field is a Vector3f just by using reflection but it can't know what size an array is. So when serializing a Vector3f field, it knows it can just write the x,y,z. When serializing a float[] it has to also write the length.

KuroSei said:
What i meant by "one String" is the following:
There are several things defining an entitys state. There are networking vars (which i use sparly since they could cause some overhead but are sometimes quite usefull), positions, rotations, velocity (for prediction and smoothing and stuff... ) and eventually some more.
I dont know how Spidermonkey sends messages over the network but i was afraid that it uses something like the serializer interface (even though it dosnt use it. I looked it up... ^^).
If the message was encoded like
[java]
name {
attribute=value
attribute2=value2
attribute3=value3{
attribute3.1=value3.1
}
}
[/java]

It would be better to parse all attributes into one string so the created message would be as small as possible.
But since you said that spidermonkey does optimazation and stuff by itself i guess its better to let it handle this itself.


SpiderMonkey does not send things as "strings" but as raw binary data. The reason you have to register the classes on both the client and the server is precisely because it does not send any class structure information in the message. It assumes the class structure is the same on both ends of the connection. The only case where it encodes field types is when the type is indeterminate... such as Object or a class that is not final. Then it has to send the class ID (as kept by Serializer's registry) which is still only a short.... just an extra 16 bits.

It's a ton more compact than Java's serialization but also a ton more fragile in the face of improper setup on one end. That's why I usually recommend that a developer register all of the classes they plan to register in a common utility class that both the client and the server call. That way you know for sure that the setup was the same and in the same order on both ends of the connection.

Ah… I’m too stupid. :wink:

I just came back and noticed myself why its problematic to send only single changes per UDP. Its so simple I didnt see the problem…



@cheatcat: The problem with the sounds is solved with a different solution. I buffer everything since most of the things are handled in python scripts. A flagg decides wether the information shall be send, send if visible or send in a special area defined by a boundingvolume. This way loads of possibilities are opened and the soundproblem is solved. :wink:



@pspeed:

I see. Thats good to know.

This network stuff is really nasty somewhat. :smiley:

Okay… i had some wierd problems with the serializer… For example that it would tell me “No @Serializer annotation…” but than changing of the order of the registrations solved the problem… wtf? Most likely a caching problem of netbeans… I guess…



Well… I’m trapped now.


SCHWERWIEGEND: Unhandled error, endpoint:NioEndpoint[1, java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:52015]], context:Envelope[NioEndpoint[1, java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:52015]], reliable, 18]
java.lang.RuntimeException: Error deserializing object
at com.jme3.network.base.MessageProtocol.createMessage(MessageProtocol.java:161)
at com.jme3.network.base.MessageProtocol.addBuffer(MessageProtocol.java:137)
at com.jme3.network.base.KernelAdapter.createAndDispatch(KernelAdapter.java:196)
at com.jme3.network.base.KernelAdapter.run(KernelAdapter.java:260)
[...]


I called the following code on both server and client:
[java]
public static void register() {
Serializer.registerClass(mError.class);
Serializer.registerClass(mEntityUpdate.class);
Serializer.registerClass(mSelectLevel.class);
}
[/java]

But when i try to send a message from the client to the server the posted Exception occures.
I'm clueless... oO


I'm using the newest version of the jMP, Windows7 aso. The connection is obviously created. Just calling
C.send(new mSelectLevel("dm-testmap"));
will cause the error on the server...


Me needs halp.:(

Was there more to that stack trace? Usually the original exception (ie: the important one) is included.



Other than that, I’d have to know what was in the message that was failing to send.



Also, is this with a recent nightly build or a stock alpha 4?

I updated everything to the newest stand via jMP and activated Nightly.



The message was this (It’s very simple yet, but there are some other things in my mind which will find their places in it soon…):

[java]@Serializable

public class mSelectLevel implements Message {



/**

  • UDP or TCP?

    */

    private boolean isR;



    /**
  • The name of the map which is supposed to be loaded.

    */

    private String MapName;



    /**
  • Additional parameters for the load…

    */

    private String param;







    /**
  • This will send the message with an emtpy parameter list
  • @param M The name of the Map

    */

    public mSelectLevel(String M) {

    isR=true;

    param=new String();

    MapName=M;

    }



    /**
  • This will send the message with a predefined parameter list
  • @param M The Name of the Map
  • @param P The optional parameterlist.

    */

    public mSelectLevel(String M, String P) {

    isR=true;

    param=P;

    MapName=M;

    }







    public String getParam() {

    return param;

    }



    public String getMapName() {

    return MapName;

    }



    public Message setReliable(boolean f) {

    isR=f;

    return this;

    }



    public boolean isReliable() {

    return isR;

    }



    }

    [/java]





    The complete stacktrace was this:
SCHWERWIEGEND: Unhandled error, endpoint:NioEndpoint[1, java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:52015]], context:Envelope[NioEndpoint[1, java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:52015]], reliable, 18]
java.lang.RuntimeException: Error deserializing object
at com.jme3.network.base.MessageProtocol.createMessage(MessageProtocol.java:161)
at com.jme3.network.base.MessageProtocol.addBuffer(MessageProtocol.java:137)
at com.jme3.network.base.KernelAdapter.createAndDispatch(KernelAdapter.java:196)
at com.jme3.network.base.KernelAdapter.run(KernelAdapter.java:260)
Caused by: com.jme3.network.serializing.SerializerException: Error creating object of type:class testban.util.network.mSelectLevel
at com.jme3.network.serializing.serializers.FieldSerializer.readObject(FieldSerializer.java:116)
at com.jme3.network.serializing.Serializer.readClassAndObject(Serializer.java:329)
at com.jme3.network.base.MessageProtocol.createMessage(MessageProtocol.java:157)
... 3 more
Caused by: java.lang.InstantiationException: testban.util.network.mSelectLevel
at java.lang.Class.newInstance0(Class.java:340)
at java.lang.Class.newInstance(Class.java:308)
at com.jme3.network.serializing.serializers.FieldSerializer.readObject(FieldSerializer.java:114)
... 5 more



The "5 more..." may be where i could find my answer but i dont know how i can tell netbeans to tell me the whole thing... :(

Messages have to have no-arg constructors or SpiderMonkey can’t create them on the receiving end.

Also… if you extend AbstractMessage then you won’t have to worry about your own setReliable()/isReliable() methods.

I see… That explains much.

Okay, i will try it out. Just one moment.





You are a treasure, you know?

It worked perfectly fine. Thank you so much for your fast help!



I love the monkeys rly. :slight_smile: