SerialRegistrations issue

So i’m 90% sure this is a bug but i’m not 100% sure. I’ve recently did some pretty sweeping changes to my game and completely separated my games client and server code into different gradle projects. When I was testing I noticed that I couldn’t restart my game after previously running it unless I completely closed the application. After doing a little bit of debugging and digging I found that the SerialRegistrationsMessage being sent to the client was twice the size it was previously (350 first run -> 700 second run).

My guess is that there is some bug that causes all the registrations to get recreated. I know this at least in part true because if I lock the serializer I get an error in "ServerSerializerRegistrationsService " when it tries to re-register some classes and it complains that the serializer is locked. This doubling happens somewhere in between when the server is created and the first connection is added to the server.

I double checked in my own code that I only serialize all my classes once when the application starts so I’m not calling it again when I start the server for the second time.

The root overflow exception I am getting is:

java.nio.BufferOverflowException
	at java.nio.HeapByteBuffer.put(HeapByteBuffer.java:189)
	at java.nio.ByteBuffer.put(ByteBuffer.java:859)
	at com.jme3.network.serializing.serializers.StringSerializer.writeString(StringSerializer.java:67)
	at com.jme3.network.serializing.serializers.StringSerializer.writeObject(StringSerializer.java:103)
	at com.jme3.network.serializing.serializers.FieldSerializer.writeObject(FieldSerializer.java:202)
	at com.jme3.network.serializing.serializers.ArraySerializer.writeArray(ArraySerializer.java:124)
	at com.jme3.network.serializing.serializers.ArraySerializer.writeObject(ArraySerializer.java:109)
	at com.jme3.network.serializing.serializers.FieldSerializer.writeObject(FieldSerializer.java:202)
	at com.jme3.network.serializing.Serializer.writeClassAndObject(Serializer.java:444)
	at com.jme3.network.base.MessageProtocol.messageToBuffer(MessageProtocol.java:73)
	at com.jme3.network.base.DefaultServer$Connection.send(DefaultServer.java:582)
	at com.jme3.network.service.serializer.ServerSerializerRegistrationsService.connectionAdded(ServerSerializerRegistrationsService.java:70)
	at com.jme3.network.service.HostedServiceManager.addConnection(HostedServiceManager.java:114)
	at com.jme3.network.service.HostedServiceManager$ConnectionObserver.connectionAdded(HostedServiceManager.java:132)
	at com.jme3.network.base.DefaultServer.fireConnectionAdded(DefaultServer.java:349)
	at com.jme3.network.base.DefaultServer.registerClient(DefaultServer.java:447)
	at com.jme3.network.base.KernelAdapter.dispatch(KernelAdapter.java:173)
	at com.jme3.network.base.KernelAdapter.createAndDispatch(KernelAdapter.java:241)
	at com.jme3.network.base.KernelAdapter.run(KernelAdapter.java:284)
java.nio.BufferOverflowException
	at java.nio.HeapByteBuffer.put(HeapByteBuffer.java:189)
	at java.nio.ByteBuffer.put(ByteBuffer.java:859)
	at com.jme3.network.serializing.serializers.StringSerializer.writeString(StringSerializer.java:67)
	at com.jme3.network.serializing.serializers.StringSerializer.writeObject(StringSerializer.java:103)
	at com.jme3.network.serializing.serializers.FieldSerializer.writeObject(FieldSerializer.java:202)
	at com.jme3.network.serializing.serializers.ArraySerializer.writeArray(ArraySerializer.java:124)
	at com.jme3.network.serializing.serializers.ArraySerializer.writeObject(ArraySerializer.java:109)
	at com.jme3.network.serializing.serializers.FieldSerializer.writeObject(FieldSerializer.java:202)
	at com.jme3.network.serializing.Serializer.writeClassAndObject(Serializer.java:444)
	at com.jme3.network.base.MessageProtocol.messageToBuffer(MessageProtocol.java:73)
	at com.jme3.network.base.DefaultServer$Connection.send(DefaultServer.java:582)
	at com.jme3.network.service.serializer.ServerSerializerRegistrationsService.connectionAdded(ServerSerializerRegistrationsService.java:70)
	at com.jme3.network.service.HostedServiceManager.addConnection(HostedServiceManager.java:114)
	at com.jme3.network.service.HostedServiceManager$ConnectionObserver.connectionAdded(HostedServiceManager.java:132)
	at com.jme3.network.base.DefaultServer.fireConnectionAdded(DefaultServer.java:349)
	at com.jme3.network.base.DefaultServer.registerClient(DefaultServer.java:447)
	at com.jme3.network.base.KernelAdapter.dispatch(KernelAdapter.java:173)
	at com.jme3.network.base.KernelAdapter.createAndDispatch(KernelAdapter.java:241)
	at com.jme3.network.base.KernelAdapter.run(KernelAdapter.java:284)

Any thoughts or ideas? I don’t know enough about the internals of spidermonkey to know what exactly is going on or why.

It’s a known bug… at least known by me… that right now games will have trouble starting twice in the same app instance. This is partially the crappy way the serializer works.

I think the simplest fix is probably to provide some way to refresh it to its original initialized state. I have this problem in every one of my games that let the player start their server right in the client. Even the sim-ethereal demos have this problem.

Edit: the crappy work-around is to keep a static boolean around and make sure to only register your classes once. Of course this will break when I (or someone) get(s) around to fixing the real issue… but at least that would require a code change on your part, too. (to reinit the serializer registry)

I am only initializing / registering my classes with the serializer once already. It is still doubling the classes that get sent in the SerialRegistrationsMessage.

Ok, the that’s a different bug.

…then your mention of locking the serializer seems to have nothing to do with any of this. If you got errors after locking the registry then you were registering twice.

I was confused because the usual pattern is to register serializers during server startup.

Well the error I got after locking the serializer was in ServerSerializerRegistrationsService and not in my code. Specifically I am getting the following error:

Caused by: java.lang.RuntimeException: Serializer registry locked trying to register class:class com.jme3.network.message.SerializerRegistrationsMessage
	at com.jme3.network.serializing.Serializer.registerClassForId(Serializer.java:196)
	at com.jme3.network.serializing.Serializer.registerClass(Serializer.java:238)
	at com.jme3.network.serializing.Serializer.registerClass(Serializer.java:152)
	at com.jme3.network.service.serializer.ServerSerializerRegistrationsService.onInitialize(ServerSerializerRegistrationsService.java:53)
	at com.jme3.network.service.serializer.ServerSerializerRegistrationsService.onInitialize(ServerSerializerRegistrationsService.java:48)
	at com.jme3.network.service.AbstractService.initialize(AbstractService.java:74)
	at com.jme3.network.service.AbstractService.initialize(AbstractService.java:44)
	at com.jme3.network.service.ServiceManager.addService(ServiceManager.java:126)
	at com.jme3.network.service.HostedServiceManager.addService(HostedServiceManager.java:82)
	at com.jme3.network.base.DefaultServer.addStandardServices(DefaultServer.java:112)
	at com.jme3.network.base.DefaultServer.<init>(DefaultServer.java:100)
	at com.jme3.network.Network.createServer(Network.java:95)
	at com.voxelcraft.states.VoxelServer.start(VoxelServer.java:158)
	at com.voxelcraft.states.VoxelServer.stateAttached(VoxelServer.java:118)
	at com.jme3.app.state.AppStateManager.attach(AppStateManager.java:133)
	at com.voxelcraft.client.states.GuiState.startJoinHost(GuiState.java:714)
	at com.voxelcraft.client.gui.WorldDisplayConverter.selectWorldClicked(WorldDisplayConverter.java:49)
	... 25 more

I’ve set breakpoints in my code where I register with the serializer and I never enter that section after application initialization. When I check the “SerializerRegistrationsMessage.INSTANCE” variable before I create my server it shows 350 classes. Sometime between the server being created (Network.createServer) and when the client connects and it attempts to send SerializerRegistrationsMessage the number of classes has exactly doubled in size.

Ok, that class is following the usual pattern and so will have a problem with the locked registry.

I’ve looked at where the SerializerRegistrationMessage is building its list of compiled classes and I don’t see anything obvious that would continue appending to the list. compile() completely rebuilds the list each time to my eyes.

Wait… are the client and server running on the same JVM? If so, by unlocking the registry then the client doesn’t know that it’s running on the same JVM and will register the classes again. So maybe that’s it. Though really the service was supposed to lock the registry again by then I think.

Yes, I am currently running both the client and the server in the same JVM. I’ll try adding the source to my build and step through some of these classes and see what is going on.

I’ve just committed a change to jme 3.1 and master that lets an app completely reinitialize the Serializer’s internal registries.

This should be the typical approach to serialization registration:
-when creating your new server instance, call Serializer.initialize()
-then register your own classes
-run the server as normal

This will prevent a lot of badness and let you more easily use services that must register their own classes and cannot do it any other time than server initialization.

3 Likes

Thanks, I’ll give it a shot this afternoon and check it out!

Just did a quick code change for the new initialize and it worked like a charm! Thanks for the help pspeed!

1 Like