Broadcast filters

Hello, I am trying to learn Jmonkey and started looking at SpiderMonkey (cool!).
I tend to learn better when I try to achieve something , so I started coding a generic Chat service class that could be embedded in games.
The idea is to allow any game based in JME to be able to add chat functionality simply by including this class (you never know, it might even turn out useful in the end :slight_smile: )

Basic concepts of the server are :

  1. It can instantiate a new Server.object (on its own port etc) or use an existing open Server
  2. It can use default message handlers or you can install your own message handlers
  3. It manages channels and user subscription to them

I am not using the Server channel functionality because a) I did not really understand how it works and b) I fear it would not fit my needs.
What I am trying to achieve is to have 2 different kind of channels :

  1. (With positive ids) managed by subscription, meaning you have a list of users who will receive the message
  2. (With negative ids) managed programmatically, meaning you pass a Filter handler and your game logic will decide who should receive the msg. This could be i.e. a Team channel, Vehicle channel, Proximity etc

So, now I am dealing with broadcast / Fitlers and I am a bit lost.
In the example of a subscription channel, I have a HashMap like this :

private HashMap<Integer,Collection<Integer>> subscriptions;

The key is the channel Id, and the value is a Collection with all the ids of the HostedConnections subscribed (I might need to turn it into a HostedConnection Collection in the end, maybe).

The point where I am stuck : how do I use that collection to filter the broadcast?

server.broadcast(Filters.in(<mycollectionsomehow>,:my message");

?

Also, I am using HashMaps (non thread safe) instead of Hastables (thread safe), I may revert back to Hashtables, but I am not sure how thread-messy it can get INSIDE the chat server (objects are not accessed from outside)

Thanks
Francesco

The broadcast method takes a filter for HostedConnections. So if you wanted to use the default implementations provided by Filters then you’d need a collection of HostedConnections.

In general, my advice:

Don’t worry about creating a server. It’s reasonable to make them create one first.

Let the user tell you which channel to use. Channels help keep traffic separate so that it’s not waiting for different events. For example, if a game only uses one channel and has some big message like a terrain tile… that will block that channel until the message is sent. Messages sent on separate channels will go through just fine. Basically, a channel is a separate socket connection.

Personally, if I were to do this I would implement it as a single listener class and some custom message classes. There is no reason to let them reuse their own listeners as message listeners are supposed to be message specific. So you might have join, leave, “message” messages and then your listener would worry about what to do with those… as well as listening for connections to be added and removed. To use it, the caller would create your “chat handler” and pass it the server and the channel. You would register your listeners (which would basically be the chat handler class) to listen for connections and the messages you are interested in.

Also, there is really no reason to keep your own map. Should you need to use client IDs for anything then the server class can already provide the hosted connections for those.

Also, remember that if you need any client specific stuff stored for a connection then you can just store it in the session data for that connection. http://hub.jmonkeyengine.org/javadoc/com/jme3/network/HostedConnection.html#setAttribute(java.lang.String,%20java.lang.Object)

As an aside: never use Hashtable for multithreaded hashmaps. It makes you look like a total newb or a throwback from the 90s. :slight_smile: ConcurrentHashMap is superior in every way that matters and is the proper way to implement a concurrent hash map (thus the name).

Lol, thanks for the hints, as explained my effort was basically didactic and …well, you ALMOST got me with that " look like a total newb or a throwback from the 90s". The "almost"part comes from the fact that I am coming from the 80s (not with java tho, mostly used Pascal and Assembly back then), not the 90s :slight_smile:

Still, my idea of channels was more to segregate chat messages and managing my lists was mainly to provide the game programmer with an extremely easy way of having people to subscribe to one or more channels, which is not exactly what you describe.

The setAttribute sounds interesting, need to see if I can use it in a convenient way.

About having an existing server object or creating one… I just went for the flexible way : if you have one cool, just pass it, if not I will create one for you (we guys from the 80s try to be kind with programmers :slight_smile: ).

About the message handlers I implemented them pretty much the way you described (If I understood correctly), plus I left the option for the programmer to override my default handlers.

the problem I have with the filter is that if I try something like this :

	   server.broadcast(Filters.in(server.getConnections()),"whatever");

It tells me I should pass a Filter<? super HostedConnection>
I am checking the Filter static methods to figure it out, I will eventually manage.

Thanks again for the hints!

Sometimes Java generics are pretty stupid. You may want to split it into multiple lines… at the very least it will help nail down what it’s actually afraid of…
Filter filter = Filters.in(server.getConnections())

…or whatever.

Yeah, it worked now, with a normal collection, don’t know why it was giving me that error before.

At the moment I implemented the following messages :
login, (no security/authentication involved, will probably provide an handle for an external method)
channleList,
channelSubscription/unsubscription
ping (both from server to client and from client to server)
and obviously Chat :slight_smile:

I tested a "tell"message with a server and two clients (the server routes the message, don’t think there is an easy way to connect between two clients unless I create Server objects also in the client… but I would rather skip that part.

It is pretty basic but it works :slight_smile:
Once it is a bit more complete and polished I will share the code, it might run useful to others.

I am planning to implement a process on the server that scans the clients sending a ping and those that do not respond in a given timeframe they get kicked.
I guess there is no sure way to detect if a client disconnected with an unclean procedure (i.e. crashed), so I guess pinging them is the only way to check who is still alive and who is not.
Now that I think of it, I should probably Implement a whole broadcast ping from the server and not cycle the clients even if I assume that network-wise it would not change anything.

thanks again for the support anyways.

Francesco

Generally, if the client dies then the socket dies… and the connection will get a remove event on the server. It may lag by some number of seconds but it’s probably going to happen faster than your ping.

Tried that, as I was not sure.

Indeed registering a ConnectionListener does the trick if the client closes gracefully :

@Override
public void connectionRemoved(Server server, HostedConnection conn) {		
	System.out.println("connection removed");
	removeUser(conn);
}

However, for what I know, TCP/IP does not have a permanent connection (unlike a serial port) therefore it does not know when a connection crashes, unless it pings the other end every now and then.
I closed the client application (which I guess will also close the client socket) and nothing was detected by the connectionListener, I don’t think that SpiderMonkey implements the pinging policy to verify if clients dropped dead :slight_smile:

Adding a client.close() in the overrided destroy might help, but not in the case of a network failure or computer crash.

@Saburo said: Tried that, as I was not sure.

Indeed registering a ConnectionListener does the trick if the client closes gracefully :

@Override
public void connectionRemoved(Server server, HostedConnection conn) {		
	System.out.println("connection removed");
	removeUser(conn);
}

However, for what I know, TCP/IP does not have a permanent connection (unlike a serial port) therefore it does not know when a connection crashes, unless it pings the other end every now and then.
I closed the client application (which I guess will also close the client socket) and nothing was detected by the connectionListener, I don’t think that SpiderMonkey implements the pinging policy to verify if clients dropped dead :slight_smile:

Adding a client.close() in the overrided destroy might help, but not in the case of a network failure or computer crash.

If you closed a client and the server didn’t know about it then something is wrong. Eventually it will know because the OS on the client will close the socket. Only if you manage to severe the socket without having one end close it (really hard to simulate) will you have an issue… but even then, eventually the connection may die. Actually, it would be enough for the server to send periodic messages to the client. The client could simply ignore them, though.

…but any time you tried to send the dead connections a chat message then it would be detected (almost) right away. (Where “almost” is the amount of time it takes the socket to recognize the packet was not received because the connection is dead.)

Hello, you can try my code if you like :

There are two files :1 the first one contains the chat classes (package com.jme3.network.chat) and the second one a test class for the server and one for the client.
The server does not require any argument, for the client you need to supply 2 arguments : name of the client and name of a second client it will try to send messages to.

i.e : run TestServer
run TestClient CL1 CL2
run TestClient CL2 CL1

Sorry the code is still a bit messy, but not too complex I believe.
If, from the client, you try to gracefully close the connection, you will notice that the server issues a “connection removed” line in the console.
If you simply close the TestClient app with the “x” button on the window (and you do not override the destroy method) , the server will not identify that the connection is closed.

That’s why I was planning to ping the clients every 10-20 seconds maybe and check who is still alive and who’s not.

@Saburo said: Hello, you can try my code if you like :

https://www.dropbox.com/sh/vrwf0fede7do4us/Sb8wqw5C72/jme

There are two files :1 the first one contains the chat classes (package com.jme3.network.chat) and the second one a test class for the server and one for the client.
The server does not require any argument, for the client you need to supply 2 arguments : name of the client and name of a second client it will try to send messages to.

i.e : run TestServer
run TestClient CL1 CL2
run TestClient CL2 CL1

Sorry the code is still a bit messy, but not too complex I believe.
If, from the client, you try to gracefully close the connection, you will notice that the server issues a “connection removed” line in the console.
If you simply close the TestClient app with the “x” button on the window (and you do not override the destroy method) , the server will not identify that the connection is closed.

That’s why I was planning to ping the clients every 10-20 seconds maybe and check who is still alive and who’s not.

How long did you wait? What OS do you use?

I tested with win7 64 (I also have an ubuntui 13.04 64 but did not try it yet there) , waited for about 1 min

(Note : I am testing from within Eclipse, but should not make any difference I think).

btw : some generic TPC/IP connection details here :
http://blog.stephencleary.com/2009/05/detection-of-half-open-dropped.html

The article basically says (and this is what I also remembered from previous experiences) that the listenere never knows if a connection has been dropped unless it tries to send data (and fails doing so)

@Saburo said: I tested with win7 64 (I also have an ubuntui 13.04 64 but did not try it yet there) , waited for about 1 min

(Note : I am testing from within Eclipse, but should not make any difference I think).

btw : some generic TPC/IP connection details here :

The article basically says (and this is what I also remembered from previous experiences) that the listenere never knows if a connection has been dropped unless it tries to send data (and fails doing so)

Well, just like the OS closes all of your open file handles when a process dies it should be closing the sockets also… in which case the other end should get notified.

Anyway, sockets are bidirectional, so the act of sending a message is enough to cause it to detect the drop on the other end. The other end doesn’t even need to respond. So you’d only have to send pings if the chats were idle for more than your polling period… in which case it probably also doesn’t matter that someone dropped.

The other thing is that a user is probably using this in a game where they are sending there own messages quite frequently (many times a second in fact) and so the drop will also be detected there. The thing is… for some reason I see the drops in Mythruna right away when a client crashes even if I’m not sending anything to them. Perhaps it’s because I do the clean up as part of an AppState’s cleanup() and perhaps this is running even when the app crashes (I doubt it). It will run in all other cases, though.

Just discovered something : closing the jmonkey windows with the “X” did actually NOT shutdown the application (client) as I expected, I added some console output when the client receives a ping and I saw that output AFTER I closed the window :slight_smile:

After all it was a good exercise, anyways I have my pinging / kicking functionality up now :slight_smile:
Overall I am quite impressed with SpiderMonkey, it is quite easy and versatile at the same time, gives you quite some good control.

Thanks again for your support, cleaning up the chat thing and will move on transmitting object translation/rotation/velocity/states vectors next :slight_smile:

Francesco

@Saburo said: Just discovered something : closing the jmonkey windows with the "X" did actually NOT shutdown the application (client) as I expected, I added some console output when the client receives a ping and I saw that output AFTER I closed the window :)

Ahah! :slight_smile:

@Saburo said: After all it was a good exercise, anyways I have my pinging / kicking functionality up now :) Overall I am quite impressed with SpiderMonkey, it is quite easy and versatile at the same time, gives you quite some good control.

Thanks. I tried really hard to make it intuitive and flexible when I rewrote it.