Is there a way to shortcut SpiderMonkey?

The use case I have in mind is a game that’s usually run on a server, but people can run it in solo mode as well.

Solo mode could easily be implemented by running a server and a client on the same machine and have the client connect to localhost. However, I’d like to avoid the serialization/deserialization overhead, and it’s also needlessly tieing up a network port.



So my idea is to run client and server in different threads on the same JVM for solo mode. (Different threads because that’s the easiest way to have the same server code for solo games and for server games.)

Messages aren’t going to be serialized and deserialized; instead, they are copied to a read-only object (as far as they aren’t read-only already), then simply handed over to the other thread’s “incoming object queue”.



Has anybody done something like that already?

What’s the best approach?

Heh. I already coded a retry loop and tested it, including the collision case.

As it happens, with 10 retries :slight_smile:



Opening and closing ports is fast enough. My run with a million ports opened and closed took less than 10 seconds. Okay, that was on a Linux machine, so YMMV for Windows.

Also, it was with ServerSocket, not with Server.



Potential trap for the unwary:

I thought that Network.createServer() would bind to the port - it’s throwing an IOException after all, right?

Well, wrong. It’s Server.start(), which will throw an unchecked KernelException if the port is already in use.

So, to detect the port-in-use condition, wrap Server.start() in try-catch.

(Hooray for open source, I wouldn’t have found that so easily if I hadn’t had the sources to step through :smiley: )

I don’t disagree with any of your findings re: server ports or whatever.



What I disagree with is that SpiderMonkey should be modified to handle the case where an app hasn’t properly abstracted their networking layer to do their own loop back. So I don’t even know where such a race condition would occur in SpiderMonkey since it would not be internally allocating random ports.



A use-case could be made to allow SpiderMonkey to create a server on an ephemeral port in the case where you were using a matching service and didn’t mind having to map a random port through your firewall or whatever. SpiderMonkey actually already can do this but you just won’t know what port because it does not expose this. In fact, all of the framework code of spider monkey doesn’t know what a port is because it does not presume a TCP/IP transport at all.



The SM Network class is really just a convenience interface for hooking up things in a natural way. You can bypass it relatively trivially to open a server on the ephemeral port and know what port that is.



[java]

InetSocketAddress address = new InetSocketAddress(0); // creates an ephemeral server

SelectorKernel reliable = new SelectorKernel(address);

Server server = new DefaultServer( gameName, version, reliable, null );

[/java]



The above creates a server without a UDP layer. That could be added easily enough though I recommend creating the UdpKernel using the same port as the InetSocketAddress.



Then in your single player version you will behave exactly as in a networked version, including two sets of processing threads, serializing messages both ways, churning through buffers (x2), and looping messages through at least half of the TCP/IP stack.



An alternative would be to implement your own loopback Client and Server. This shouldn’t be hard as many of the trickier parts like MessageListenerRegistry are already separate to allow such reuse. You could avoid all server threads except your game threads and have a single thread in your loop-back client that simply pulls messages off of a shared queue and pushes them to the client-side listeners. I don’t really have any interest in writing or maintaining such a thing as I think it is a sign of a poorly architected program that will lead to issues later. Generally there are enough differences between a single player and multiplayer game to warrant abstractions at the game level and not at the networking level. Might be useful for prototyping, I guess. In my own experience, whenever I’d tried to do it at that level I’ve always regretted it.

Actually, based on when the bind error occurs and relooking at the documentation I think the port won’t be ready in the above code until the server is started.



You can create a UdpKernel with the same address object. It actually doesn’t matter if it has the same port as the TCP layer or not… but you’d need to use the port when creating the client.



I will see about making sure SelectorKernel and UdpKernel can return the real address information post-bind.

Left 4 dead would be a good example of a game where single player and multi player are the same…as would unreal tournament. It just replaces other players with bots.

@pspeed yep, I noticed (and had already reported) that port binding (and potential conflict) will happen in start(), not in Network.createServer.

Neither Network.createServer nor Server.start() document this behaviour.



Current Javadoc:[java] /**

  • Start the server so that it will began accepting new connections
  • and processing messages.

    */

    public void start();[/java]



    Proposed Javadoc:[java] /**
  • Start the server so that it will start begin accepting new connections
  • and processing messages.<br>
  • This is the point at which a Server will start allocating OS resources
  • such as networking sockets.

    */

    public void start();[/java]



    I wasn’t aware that it’s good practice to roll your own port selection logic.

    That would be a viable course of action, though I have written my retry logic based on multiple calls to Network.createServer(), so there’s no need to go back on that anymore.



    Good to hear you’re thinking about adding a getter for the port number, thanks!

    Now if there’s also a guarantee that only ephemeral ports will be returned… :wink: (requesting “port 0” doesn’t cut it, unfortunately. not on my machine anyway.)
@zarch said:
Left 4 dead would be a good example of a game where single player and multi player are the same...as would unreal tournament. It just replaces other players with bots.


For single player, do they seriously run the whole game server+client on the local machine including networking and everything?!?! If so, my view of them just went down about 1000%.

My guess is that they've actually architected their game to allow connecting the client code directly to their own server code. Mythruna does this too. It's not hard with a proper design.
@toolforger said:
Good to hear you're thinking about adding a getter for the port number, thanks!
Now if there's also a guarantee that only ephemeral ports will be returned... ;) (requesting "port 0" doesn't cut it, unfortunately. not on my machine anyway.)


Requesting port 0 will return an ephemeral port almost by definition. This is why I think the IANA list is bogus since OSes are happily using ports that they think they are allocating. After all, any outbound socket you create could be gobbling up their ports.

Ah, I think I start to see the source of the confusion.



Yep, a randomly assigned port is “ephemeral” in the sense that the port number is used only temporarily.



IANA’s didn’t do anything specific for ephemeral ports before Aug 2011, so everybody was simply grabbing a port and hoped for the best.

Starting Aug 2011, IANA as promised to never allocate ports 49152…65535, so using that range for ephemeral ports means that you’ll never block the port of another service.

(I think I was too hasty assuming that “49152…65535” is already the same as “ephemeral”. Which is true only after agreeing that they should be the same… ah well, my bad.)

Yeah, the bottom line is that regardless of what IANA says, your OS will allocate ports based on what your OS uses to allocate ports. That’s just as safe as any other connection your machine will make and you can hope that if they overlap then it’s something your OS will sort out someday.

Why would you use a network lib at all if what you want to do isn’t networking? :?

“The use case I have in mind is a game that’s usually run on a server, but people can run it in solo mode as well.”



To clarify: I’m not necessarily after integrating SpiderMonkey into the solo case, it might be enough to structure the application so that it can serve both scenarios.

I’m doing it with my BlockManager (a class full of information that is read only after initialization) like this:

I have a Singleton Class “Bridge”. If the BlockManager is constructed in all ways I put it’s reference into that class und the local client then may take it. This has the benefit of not wasting memory.

With data that might be changed differently on client and server one could think of a similar system in the “producer consumer” design that is a popular way to let Threads talk to each other. (Google will find much of information).

1 Like

Yes, I was thinking about doing producer-consumer via queues of objects - inbound and outbound queue (for sending and receiving data).

The solo mode would simply make the inbound queue of one thread the outbound of the other. The client-server mode would send the outbound queue to a thread that does the SpiderMonkeying.



But that’s just one way to do it, and maybe not the best one.

So I’m still interested in other people’s ideas and observations.

I would not risk adding bugs for the minimal reduce in overhead due to no serialization… If you really want to, just replace the part where your entities or whatever send their data over the network and just replace that by some other way to do messaging… But again, I don’t think it’ll be a great advantage if I understand you right…

I agree that the (de)serialization overhead is probably negligible, especially since the client-server mode needs to be ultra-frugal about the amount of data it sends.



The real issue is the server port. If I just hardcode a port number, this will work 99% of the time, but the remaining 1% will fill the forums with calls for help. And with scary stories about other software not starting or not working properly - not good.

If I let people configure a port number, that’s another entry barrier for the casual gamer. Not good either.



(BTW I don’t consider configuring a port number an undue hardship for a server administrator - these guys should already know what a port number is and what effects a conflict might have.)

It is almost always better to architect your game in such a way so that networking can be removed without changing the structure of the game at all. For example, in Mythruna, I have specific client interfaces that have different implementations for networked versus non-networked. Everything else is the same. The single-player versions just do a little more since they have their own direct game systems instead of sending messages to the server.



There are other reasons to do this also since many times you have features in single player that you don’t in multiplayer and vice versa. It’s nice to have the option.

Is the server also playing the game or is it sometimes headless?



If it can play then the simplest way will be to just run a server with 0 other players and don’t handle it as a special case at all. If you want to avoid network overhead the code that receives messages off the network can just post them to the same queue your internal code does. That can remain the same whether running single player or not so again you have the same code in both cases…

@zarch said:
Is the server also playing the game or is it sometimes headless?

If it can play then the simplest way will be to just run a server with 0 other players and don't handle it as a special case at all. If you want to avoid network overhead the code that receives messages off the network can just post them to the same queue your internal code does. That can remain the same whether running single player or not so again you have the same code in both cases...


He's worried about port collisions in single player.

True.



Single player would most likely not both grabbing a port if the direct message passing was available. Otherwise hunting for an available port isn’t a big deal and it is easy enough to present that as part of the connection string (or registering with the central server as available host, or whatever) used by other players.