Error Listener been added to wiki

Hello all

Just so people know it’s there I have added the Error Listener to the wiki.
You can find it in the advanced multiplayer section (https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:advanced:networking)

I basically said what it does and said how to implement it and how to start you off.

I found it when working on some networking stuff for my game. To my surprise it’s a very useful listener, but no one has ever talked about it before. I will keep adding to the network section of the wiki as I’m spending quite a bit of time right now in the javadoc looking at networking.

Thanks Sam

4 Likes

Cool, thanks!

Good job :slight_smile:

Thanks.

The documentation is a bit hit or miss there. For example, I just scanned through it and this bit:

A server has a game version and game name property. Each client expects to communicate with a server with a certain game name and version. Test first whether the game name matches, and then whether game version matches, before sending any messages! If they do not match, you should refuse to connect, because unmatched clients and servers will likely miscommunicate.

Is incorrect. SpiderMonkey will reject it for you… you have no choice. :slight_smile:

A few notes on the parts you added…
" register it to myClient in MyGameClients’s simpleInitApp() "
…but then our example shows myServer.

In fact, ErrorListener is setup to work on both client and server, I just never implemented it on server. So when you implement the class you have to make sure to set its type parameter to Client.
implements ErrorListener<Client>

Also, the javadoc for Client.addErrorListener() mentions:

If a client has no error listeners then the default behavior is to close the connection and provide an appropriate DisconnectInfo to any ClientStateListeners. If the application adds its own error listeners then it must take care of closing the connection itself.

This might be worth adding some kind of note box for because if you add an error listener you are basically taking over some of the default error handling. If you don’t close the connection yourself then it could get stuck open or something.

Edit: fixed the type parameter on my ErrorListener example.

1 Like

Note also… with the default error handling the ClientStateListener will also get notified with the exception. So if you want to keep the default handling of closing the connection but just wanted the exception info, then a ClientStateListener might be the better way to go.

Always some good reasons to use either depending on need.

1 Like

Thanks guys

I will go change those things today and I will add some notes in the error listener about that’s overriding the default close connection processes. That also the clientstatelistener handles default errors. Also I will change the wording so that it mentions that it can be used on a client and a server.

Thanks Sam

@_Master_Mas_ said: Thanks guys

I will go change those things today and I will add some notes in the error listener about that’s overriding the default close connection processes. That also the clientstatelistener handles default errors. Also I will change the wording so that it mentions that it can be used on a client and a server.

Thanks Sam

Well, it can’t be used on the server yet because I never added that code. :slight_smile: But the interface was designed that way so you have to implement with the right type parameter. “implements ErrorListener<Client>” for example.

Thanks pspeed, I will take note of that

Sam

Hi pspeed and others. I have added the notes to the wiki page about how the ErrorListener overrides your default close action for the client. Also I mention that it cause be used on the server but it has be implemented yet. I fixed some of the small mistakes I had in there. I also mention in the ClientStateListener that it uses a default close action and if they want more control on what happens when a client is closed, that they should look at the ErrorListener.

Sam

1 Like

Thanks.

Also, there is this part that disturb me :

The com.jme3.network.ClientStateListener notifies the Client when the Client has fully connected to the server (including any internal handshaking), and when the Client is kicked (disconnected) from the server.

Especially the part “including any internal handshaking”.
For me, the name of the game and its version should be a part of the handshaking, and we should have a way to add some more “handshaking” things, like a verification of a name+password or stuff like that. As this kind of stuff is not “standard”, we should have a way to do it ourself, before the “client connection” is yelled. So, what we can do is :

  1. modify the wiki. Hey, it’s easy, but not really helpfull here.
  2. add a way to set a “client connection validator” which, by default, do only the standard job but can be changed to a more complete one.
  3. add a state to the connection like “doingAdditionnalHandshaking” so the listener will have 2 call, the first one with this new state and the second one throw by us with the “connected” state.
    4.add a “session” concept above that (see below)

Right now, i don’t use the clientstatelistener for its connection part at all, i “initialize” the connection after i received and verified the “game version+name message”. So the connection is etablished in a messagelistener which is a bit strange. But, except to detect ddos the “clientstatelistener” is only usefull for the “disconnected” part. I mean : what i am suppose to initialize when i am even not sure that the client is running the right game ?
And right now i am even not talking about login.
And, also, make a link between a disconnection of the client’s socket and the disconnection of the client is not a good thing. I may want to have a protocole with many short connection (where the client diconnect when he has nothing to say) and have a “session” concept over it. So, even the “disconnect” part of the client statelistener should be reworked.

If i can put my hands on connection and disconnection signals, i can “smooth” that to create a session concept (if the client reconnect soon enough and identify himself i can pretend that he didn’t leave in the first time).

I CAN do it right now, but i shouldn’t need to implement myself such low level things. And if i use sessions, i would like to keep the same “hostedconnection” during it (at least keep datas i put in it).

I certainly like the idea bubuche. Just to admit straight out that I also use the messagelistener as a way to confirm the clients. I send data like username, current game version and server version that it needs. Also as my is a racer I also send a lot of data back to the client when the official status is connected.

I agree with the network solution should have more advanced tools such as the one you’ve mention above.

I would like to see a way that I can send custom data through a clientstatelistener. Also to have a way so if a user gets rejected at first but in a small period after that if they join and are successful that get the same hosted connection id.

So the question we should ask is should someone code this type of system as an extension for now or should we reply on devs to integrate it in the near future. Norman and pspeed or any other of the devs feel free to comment on this proposal

Sam

@bubuche said: Also, there is this part that disturb me :

Especially the part “including any internal handshaking”.
For me, the name of the game and its version should be a part of the handshaking, and we should have a way to add some more “handshaking” things, like a verification of a name+password or stuff like that. As this kind of stuff is not “standard”, we should have a way to do it ourself, before the “client connection” is yelled. So, what we can do is :

You do have a way to do it yourself. Register a ClientStateListener and to the validation on connected. At that point, SpiderMonkey has already determined that you are connected to the proper game name and version because without at least that you can do no communication at all. You won’t even know what messages to use.

@bubuche said:
  1. modify the wiki. Hey, it’s easy, but not really helpfull here.
  2. add a way to set a “client connection validator” which, by default, do only the standard job but can be changed to a more complete one.
  3. add a state to the connection like “doingAdditionnalHandshaking” so the listener will have 2 call, the first one with this new state and the second one throw by us with the “connected” state.
    4.add a “session” concept above that (see below)

Right now, i don’t use the clientstatelistener for its connection part at all, i “initialize” the connection after i received and verified the “game version+name message”. So the connection is etablished in a messagelistener which is a bit

Why do you do this game version + name thing when Spider Monkey is already doing it in a way that won’t break if the message serialization is different?

@bubuche said: strange. But, except to detect ddos the "clientstatelistener" is only usefull for the "disconnected" part. I mean : what i am suppose to initialize when i am even not sure that the client is running the right game ?

When client state listener is called you already know it’s the right game. Game name and version has already been checked. When client state listener is called, game name and version has already been checked.

The game name and version is checked before client state listener is called. You will get a disconnect error (pretty sure that’s how I notify you) when their is a version mismatch.

@bubuche said:

And right now i am even not talking about login.
And, also, make a link between a disconnection of the client’s socket and the disconnection of the client is not a good thing. I may want to have a protocole with many short connection (where the client diconnect when he has nothing to say) and have a “session” concept over it. So, even the “disconnect” part of the client statelistener should be reworked.

Then you should use a different networking API as this one is intended for persistent connections. But I’m still not sure I see what the problem is. You just have to manage your session separately from the connection objects.

@bubuche said: If i can put my hands on connection and disconnection signals, i can "smooth" that to create a session concept (if the client reconnect soon enough and identify himself i can pretend that he didn't leave in the first time).

I CAN do it right now, but i shouldn’t need to implement myself such low level things. And if i use sessions, i would like to keep the same “hostedconnection” during it (at least keep datas i put in it).

HostedConnection has one main job: letting you send stuff to a specific client. It also provides some nice book-keeping with the session attributes. Without the connection back to the client (and the connection setup is not really cheap), there isn’t really a great reason to have a HostedConnection around. It’s super-trivial to keep your own map of state. SpiderMonkey would have no reliable way of verifying that it was the same connection coming in again anyway. You’d have to go through the whole game-specific connection process again every time. Everyone will want to do this a different way because each of them will have some way to attack it.

A game with many short lived connections may be better served by some kind of REST API on the server using standard HTTP(S) connections.

For the record, “internal handshaking” in this case is the setup of the various channels (two by default) and the client and server agreeing on what those callback ports are and such. Even a UDP round trip is performed so that the server knows where in your NAT firewall to send back UDP packets. During this process, as part of the initial message to the server, the game name and version are checked. The client is kicked if they don’t match.

When ClientStateListener is called, you will know that the channels have been fully setup and the game name and version verified. At that point, (assuming you are setting up your messages properly) you can be reasonably sure that you will not fail with strange serialization errors.

SpiderMonkey’s serializer leans towards tiny messages rather than robust messages. It’s not like Java’s serialization that sends along a ton of other data so that errors can be properly checked on the other end. SpiderMonkey has no such assurances. A message with a single string field ends up as four shorts and some character data (off the top of my head). The message size, an ID for the message class, an ID for the String class, a size, and the characters.

@_Master_Mas_ said: I certainly like the idea bubuche. Just to admit straight out that I also use the messagelistener as a way to confirm the clients. I send data like username, current game version and server version that it needs. Also as my is a racer I also send a lot of data back to the client when the official status is connected.

You do not need to do some of this. In fact, you cannot reliably do it at all. The game name and version checking that SpiderMonkey provides will work even if the protocol changes. If the serialization initialization order changes… or if you’ve connected to a completely different server that happens to have messages registered in a way that doesn’t break right away.

@_Master_Mas_ said: I agree with the network solution should have more advanced tools such as the one you've mention above.

I would like to see a way that I can send custom data through a clientstatelistener. Also to have a way so if a user gets rejected at first but in a small period after that if they join and are successful that get the same hosted connection id.

So the question we should ask is should someone code this type of system as an extension for now or should we reply on devs to integrate it in the near future. Norman and pspeed or any other of the devs feel free to comment on this proposal

Sam

There is no reason to send custom data through the client state listener. You already have ways to send messages. I don’t see why there needs to be another.

ClientStateListener
-connected
So send your log in info to the server
SomeMessageListener
-messageReceived( ServerSaysYouAreYou )
So now start your game

I don’t see what the issue is, really. ClientStateListener can’t really send custom data because it isn’t sure it knows how to send it… so internally it would just be sending a separate message anyway. So any interface we provided would be 1000 times more limiting than just sending your own messages.

Maybe I should come at the other way: why is this hard?

Ok, i said something stupid, i have to admit it. Ok, there is already a way to define properly game name and version.

BUT, it’s a bit hidden. I followed the tutorial instructions (and it works ^^) but i didn’t find a way to define game name and version. No “setGameName” or “setVersion” or “setGameInformations” or whatever.
It’s why i ended with this solution.
Actually, you can create define these informations in the construction of the client/server.
[java]Network.connectToServer(name, version, host, hostPort)
[/java]

and you have the same thing for the server. However, the version is an int here, so i’ll keep my version of that, with a float (which is also the version of the game … maybe it’s ulgy ^^).

There is other things that i don’t like very much with this network approach (like the number of classes and the boilerplate code …) but this is a whole different topic.

About the session stuff … well, it would be nice to have it, it’s in the OSI model after all (the 5th level). But nobody do that level most of time ^^

EDIT :
ok, i added the game name+version library check (with the constructor). It’s temporary but this is how i do that:

[java]
int int_version = Float.floatToIntBits(GameData.version);
client = Network.connectToServer(GameData.name, int_version, “127.0.0.1”, GameData.port);[/java]

I’ll try to improve that later but then i’ll have to change the part of code in the message listener that did the check previously ^^. It’s temporary ^^.

For me, I only update the network version when the protocol changes. That way 3.4.1 can still connect to a 3.4.8 server if the protocol hasn’t changed.

My only complaint about the current network code is Serializer. I wish almost every day that I’d rewritten that when I rewrote the rest. I’m not sure how the rest could have any fewer classes and still do what it does. The API-level classes (the part the user sees) are about as minimal as it could possibly get.

I’m also not sure what you mean by boiler plate code. You can setup a full server in about 3 lines and a full client in about three lines (not counting listener implementations in both cases). The rest is your game.

…except Serializer, of course. That part is a pain. Soon I think I will make the serialization implementation pluggable so we can experiment with different approaches. The code is already setup that way internally anyway.

actually for each message:

1 class for the message.
1 class to catch the message client side.
1 class to catch the message server side
1 class to deal with the scene (you can’t do it from the network thread)

and everytime you have to register the listener client side and/or server side, check the type of the message and convert it (why it’s not in the template parameter ?), write the accessor, write the constructor to have a way to access the application, add an empty constructor, add it to the serializer (in the static method of the class that both client and server call, thanks for your advice :slight_smile: ).

Don’t be afraid, to “check the type of the message” i just put it in “assert”. I know that it will crash anyway if there is a bug, and there is no clean way to solve this. If there is a bug, just silently ignore the message is not a solution.

You know what i would love to have ? A way to declare a method as “remote” and a way to call server-side a method client-side, with parameter etc. Maybe declare an interface as “remote” and put in it all the “communication” methods. But in this way it would be much faster to code. And as long as the method doesn’t return something, the call doesn’t need to be syncronized.
We can of course, also have the opposite : declare a method as remote and the client can call it server-side.

It would also help to developp games in a “normal” way and switch to multiplayer without too much change in the code, as long as the code is well enough organized.

I know that some (all ?) database works this way.

EDIT : and, yes, i am talking about cache-things etc. Very high level network, but also very handy. It seems that ut (at least the 2004) has things like that in it, “remotly called” methods. I am not sure, but i know they have a keyword to make the difference between method only called client side and methods called in both side.

If you look at the way the Zay-Es networking does this:

Repository:
http://code.google.com/p/jmonkeyplatform-contributions/source/browse/trunk/zay-es#zay-es%2Fextensions%2FZay-ES-Net%2Fsrc%2Fcom%2Fsimsilica%2Fes%2Fnet

Specific class:
http://code.google.com/p/jmonkeyplatform-contributions/source/browse/trunk/zay-es/extensions/Zay-ES-Net/src/com/simsilica/es/net/AbstractMessageDelegator.java

Concrete classes:
http://code.google.com/p/jmonkeyplatform-contributions/source/browse/trunk/zay-es/extensions/Zay-ES-Net/src/com/simsilica/es/net/ObjectMessageDelegator.java
And on the server a session specific one:
http://code.google.com/p/jmonkeyplatform-contributions/source/browse/trunk/zay-es/extensions/Zay-ES-Net/src/com/simsilica/es/server/SessionDataDelegator.java

It removes a lot of the boiler plate code. You can poke around a little to see how Zay-ES-net actually uses those.

I rarely have more than maybe 5 listeners for any given game as I base the listeners on functionality and not message types.

I have designed an efficient RPC mechanism (SpiderMonkey already has an RMI-like implementation but it’s not very efficient). Doing this efficiently is tricky to get right. The message delegator is a nice sort of in-between that removes some of the boiler plate.

P.S.: I’ve moved these delegators from project to project a few times now… and now they show up in Zay-ES-Net. If I come up with names I’m happy with then I will probably move them to SpiderMonkey utils or something.