[SOLVED] Now, Serializer in jME 3.1 is giving me problems!

Hi guys! This question is more related to @pspeed.

So, I updated my project to jME 3.1, and decided to work in the multiplayer now. I created a simple login server, but I’m getting errors with the Serializer. I already looked at this post, and, in short, it says I don’t need to register the classes in the client. I tried that, but I get the error when I send messages:

java.lang.IllegalArgumentException: Class has not been registered:class messages.login.NewAccountMessage
	at com.jme3.network.serializing.Serializer.getSerializerRegistration(Serializer.java:358)
	at com.jme3.network.serializing.Serializer.getSerializerRegistration(Serializer.java:327)
	at com.jme3.network.serializing.Serializer.writeClass(Serializer.java:402)
	at com.jme3.network.serializing.Serializer.writeClassAndObject(Serializer.java:422)
	at com.jme3.network.base.MessageProtocol.messageToBuffer(MessageProtocol.java:73)
	at com.jme3.network.base.DefaultClient.send(DefaultClient.java:266)
	at com.jme3.network.base.DefaultClient.send(DefaultClient.java:226)
	at main.Main.simpleInitApp(Main.java:109)
	at com.jme3.app.SimpleApplication.initialize(SimpleApplication.java:227)
	at com.jme3.system.lwjgl.LwjglAbstractDisplay.initInThread(LwjglAbstractDisplay.java:131)
	at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:212)
	at java.lang.Thread.run(Thread.java:745)

In the server, I don’t get any problems IF I register the classes before turning on the server. I do the same thing to the Client.

The Client only works if I register the classes. I send two messages, a CreateNewAccount and LoginAttempt message. These two classes are registered in the Serializer.

When I send these messages, whether the server has or not registered them, I get errors in both ends:

Client:

Feb 23, 2016 12:44:43 PM com.jme3.network.message.SerializerRegistrationsMessage registerAll
INFO: Registering:Registration[-19 = com.jme3.math.Vector3f, serializer=com.jme3.network.serializing.serializers.Vector3Serializer]
Feb 23, 2016 12:44:43 PM com.jme3.network.message.SerializerRegistrationsMessage registerAll
INFO: Registering:Registration[-41 = com.jme3.network.serializing.serializers.FieldSerializer, serializer=null]
Feb 23, 2016 12:44:43 PM com.jme3.network.message.SerializerRegistrationsMessage registerAll
INFO: Registering:Registration[-43 = [I, serializer=com.jme3.network.serializing.serializers.ArraySerializer]
Feb 23, 2016 12:44:43 PM com.jme3.network.message.SerializerRegistrationsMessage registerAll
INFO: Registering:Registration[-46 = [Lcom.jme3.network.message.SerializerRegistrationsMessage$Registration;, serializer=com.jme3.network.serializing.serializers.ArraySerializer]
Feb 23, 2016 12:44:43 PM com.jme3.network.base.DefaultClient handleError
SEVERE: Termining connection due to unhandled error
com.jme3.network.kernel.ConnectorException: Connector closed.
	at com.jme3.network.base.ConnectorAdapter.run(ConnectorAdapter.java:161)

Server:

Feb 23, 2016 12:44:43 PM com.jme3.network.base.KernelAdapter reportError
SEVERE: Unhandled error, endpoint:NioEndpoint[1, java.nio.channels.SocketChannel[connected local=/127.0.0.1:1000 remote=/127.0.0.1:49653]], context:Envelope[NioEndpoint[1, java.nio.channels.SocketChannel[connected local=/127.0.0.1:1000 remote=/127.0.0.1:49653]], reliable, 106]
java.lang.RuntimeException: Error deserializing object, clas ID:-48
	at com.jme3.network.base.MessageProtocol.createMessage(MessageProtocol.java:184)
	at com.jme3.network.base.MessageProtocol.addBuffer(MessageProtocol.java:160)
	at com.jme3.network.base.KernelAdapter.createAndDispatch(KernelAdapter.java:216)
	at com.jme3.network.base.KernelAdapter.run(KernelAdapter.java:280)
Caused by: com.jme3.network.serializing.SerializerException: Class not found for buffer data.
	at com.jme3.network.serializing.Serializer.readClassAndObject(Serializer.java:390)
	at com.jme3.network.base.MessageProtocol.createMessage(MessageProtocol.java:180)
	... 3 more

So, I need youtr help guys. I’m running only one instance of Server and Client, if that helps.

Thanks,
Ev1lbl0w

Most likely cause is that you are trying to send a message from the client before the connection has been fully established. In general, you should register a connection listener and not send anything until connected. At that point, all of the client services have been properly initialized.

Also note, there were a few bugs fixed in networking since the alpha1 release. They shouldn’t affect this (and may not affect you at all).

So serialization does not send classes or class definitions. So both sides need exactly the same class to serialize properly. Also you need to register all such classes in most frameworks that i have ever used. So that the framework knows what the structure of the data will look like. Basically defining the structs that end up getting sent.

It looks like in the last example you just don’t have the class locally to serialize to. So it throws an exception which eventually leads to the client shutting down and closing the connection.

Since I almost always do my own network code, i can’t comment on the jme network stuff specifically. But i tend to not use serilization and use messages instead. Often human readable ones and things like tcpdump can be use to diagnose problems.

Except it does.

Yes, because he tried to send a message before he got the classes from the server.

I wrote the JME networking code… so at least in this I know what I’m talking about.

JME’s serialization is not Java’s serialization but a very light-weight, low-overhead format for sending messages, basically. For example, class with two 32-bit ints in it will serialize as only 2 + 2 + 4 + 4 bytes… 12 bytes us not bad for automatic serialization. Most messages will flatten this way with some care and there is then only a 4 byte overhead in the protocol.

Thanks for the replys.

So, I commented the code that sends the messages. The client simply registers all messages, uses the Network method to connect to the server and starts. Exact same thing for the server.

I get this error in the Client:

SEVERE: Termining connection due to unhandled error
java.lang.RuntimeException: Error deserializing object, clas ID:-48
	at com.jme3.network.base.MessageProtocol.createMessage(MessageProtocol.java:184)
	at com.jme3.network.base.MessageProtocol.addBuffer(MessageProtocol.java:160)
	at com.jme3.network.base.ConnectorAdapter.run(ConnectorAdapter.java:169)
Caused by: com.jme3.network.serializing.SerializerException: Class not found for buffer data.
	at com.jme3.network.serializing.Serializer.readClassAndObject(Serializer.java:390)
	at com.jme3.network.base.MessageProtocol.createMessage(MessageProtocol.java:180)
	... 2 more

Exception in thread "com.jme3.network.kernel.tcp.SocketConnector@4df37741" java.lang.IllegalStateException: ClientServiceManager not started.
	at com.jme3.network.service.ServiceManager.stop(ServiceManager.java:96)
	at com.jme3.network.base.DefaultClient.closeConnections(DefaultClient.java:292)
	at com.jme3.network.base.DefaultClient.handleError(DefaultClient.java:389)
	at com.jme3.network.base.DefaultClient$Redispatch.handleError(DefaultClient.java:474)
	at com.jme3.network.base.ConnectorAdapter.handleError(ConnectorAdapter.java:149)
	at com.jme3.network.base.ConnectorAdapter.run(ConnectorAdapter.java:178)

I don’t get anything in the server side. I’m sure the client/server is not sending any messages. I also checked my Firewall, and it’s not bolcking anything related to Java.

In case you want, here’s the code for the connection:

Client:

try {
            Serializer.registerClasses(NewAccountMessage.class, LoginAttemptMessage.class);
            client = Network.connectToServer("localhost", 1000);
            client.start();
            /*NewAccountMessage newAccount = new NewAccountMessage("xxxxx", "xxxxx");
            LoginAttemptMessage login = new LoginAttemptMessage("xxxxx", Encryptor.encrypt("xxxxx"));
            client.send(newAccount);
            client.send(login);*/
        } catch(IOException ioe) {
            ioe.printStackTrace();
        }

Server:

try {
            Serializer.registerClasses(NewAccountMessage.class, LoginAttemptMessage.class, LoginResultMessage.class);
            loginServer = Network.createServer(1000);
            loginServer.start();
            loginServer.addMessageListener(this);
            System.out.println("Login server operational!");
        } catch(IOException e) {
            System.err.println("Login server could not initialize!");
            e.printStackTrace();
        }

Do not register classes on the client. These classes are sent over automatically and if you register them first you will mess up the registry. I should probably detect it but I don’t yet.

Thanks @pspeed. I commented that, but I still get the exact same error.

Show me the latest code.

Also, are you running alpha1 or the latest code from head? A bunch of bugs were fixed after alpha 1.

I am using alpha 1 SDK.

As requested, here is the latest code for both client and server.

Client:

try {
            //Serializer.registerClasses(NewAccountMessage.class, LoginAttemptMessage.class);
            client = Network.connectToServer("localhost", 1000);
            client.start();
            /*NewAccountMessage newAccount = new NewAccountMessage("xxxxxxxx", "xxxxxxxx");
            LoginAttemptMessage login = new LoginAttemptMessage("xxxxxxxx", Encryptor.encrypt("xxxxxxxx"));
            client.send(newAccount);
            client.send(login);*/
        } catch(IOException ioe) {
            ioe.printStackTrace();
        }

Server:

try {
            Serializer.registerClasses(NewAccountMessage.class, LoginAttemptMessage.class, LoginResultMessage.class);
            loginServer = Network.createServer(Fields.LOGIN_SERVER_PORT);
            loginServer.start();
            loginServer.addMessageListener(this);
            System.out.println("Login server operational!");
        } catch(IOException e) {
            System.err.println("Login server could not initialize!");
            e.printStackTrace();
        }

I’m 99.999% sure I had this problem intermittently and fixed it post alpha 1. Alpha 2 will be available from maven repos ‘any time now’ I think.

Edit: also if you get really curious what is going on then you can turn on FINER/FINEST logging on the server and you will get all kinds of output about what’s being serialized. (Though I may have added that also post-alpha1… not sure.)

So serialization does not send classes or class definitions.
Except it does.

So i send a via jME class, that is then loaded by a custom class loader on the receiving side? I think not. Also that gets very very messy with namespace/classname issues.

BTW my old biology equivalent of SETI online used network class loaders. It would be a very bad fit for a game even if JME does send the class. Which i am very doubtful about.

No, both sides have to have the class (that’s not at all the issue here) but you only have to register it on the server now.

So, after a long break, I updated the jme to alpha 2. Using the exact same code. Still getting the same issue.

Do the TestChatClient and TestChatServer work for you?

Some part of the code we can’t see is broken in your example. We’ll have to see a complete failing example to help more but I have several applications (simple to complex) that work just fine with the latest SpiderMonkey code.

Thanks @pspeed. The TestChatClient and TestChatServer are working fine. Then I decied to llok into the code, and I found out this:

public static void main(String... args) throws Exception {
        TestChatServer.initializeClasses();

        // Grab a host string from the user
        String s = getString(null, "Host Info", "Enter chat host:", "localhost");
        if (s == null) {
            System.out.println("User cancelled.");
            return;
        }

        TestChatClient test = new TestChatClient(s);
        test.setVisible(true);
    }

Look at this line of code right in the beginning:

TestChatServer.initializeClasses();

If I comment that line, I get the exact same error I have. So, should I ask for the server to register the classes everytime a new connection is added?

Also, I don’t want to “give access” to the server that easily, because of hacking. My game’s Client and Server are completely isolated from each other, and they can only communicate through messages. Am I doing it right?

That’s fine… but you will need a library of classes that they both share. The classes that they send back and forth must be the exact same .class files or you will pull all of your hair out.

That line is no longer needed on clients. Your tests must be using old spider monkey code.

If you want to look at another example you can try this:

It does some extra ES related stuff but that shouldn’t matter to prove that things work without message registration on the client.

If the client is requiring that then you are running old spidermonkey code… like pre 3.1 alpha 1.

Copied the example to a new project running on alpha 2.

Runs normally. When I comment that line:

TestChatServer.initializeClasses();

I have this exception:

SEVERE: Termining connection due to unhandled error
java.lang.RuntimeException: Error deserializing object, class ID:-46
	at com.jme3.network.base.MessageProtocol.createMessage(MessageProtocol.java:184)
	at com.jme3.network.base.MessageProtocol.addBuffer(MessageProtocol.java:160)
	at com.jme3.network.base.ConnectorAdapter.run(ConnectorAdapter.java:169)
Caused by: java.io.IOException: Could not read String: Invalid length identifier.
	at com.jme3.network.serializing.serializers.StringSerializer.readString(StringSerializer.java:89)
	at com.jme3.network.serializing.serializers.StringSerializer.readObject(StringSerializer.java:97)
	at com.jme3.network.serializing.serializers.StringSerializer.readObject(StringSerializer.java:44)
	at com.jme3.network.serializing.serializers.FieldSerializer.readObject(FieldSerializer.java:161)
	at com.jme3.network.serializing.serializers.ArraySerializer.readArray(ArraySerializer.java:147)
	at com.jme3.network.serializing.serializers.ArraySerializer.readObject(ArraySerializer.java:88)
	at com.jme3.network.serializing.Serializer.readClassAndObject(Serializer.java:392)
	at com.jme3.network.base.MessageProtocol.createMessage(MessageProtocol.java:180)
	... 2 more

And if I try to send any message, I get the same error of mine.

Going to check your Zay-ES net example now.

Also try enabling FINEST logging however that’s done with JUL. Just for any com.jme3.network stuff would be fine for this problem.

Comparing your TestChatClient code to what’s actually in github, it’s different.

    public static void main(String... args) throws Exception {
    
        // Note: in JME 3.1 this is generally unnecessary as the server will
        // send a message with all server-registered classes.
        // TestChatServer.initializeClasses();
        // Leaving the call commented out to be illustrative regarding the
        // common old pattern.

        // Grab a host string from the user
        String s = getString(null, "Host Info", "Enter chat host:", "localhost");
        if (s == null) {
            System.out.println("User cancelled.");
            return;
        }

        // Register a shutdown hook to get a message on the console when the
        // app actually finishes
        Runtime.getRuntime().addShutdownHook(new Thread() {
                @Override
                public void run() {
                    System.out.println("Chat client is terminating.");
                }
            });


        TestChatClient test = new TestChatClient(s);
        test.setVisible(true);
    }

…but it does fail for me also.

On your server, swap those two lines.

I’ve updated the TestChatServer in github.

Why?
Server creation registers its own message for passing class registrations. If you register your classes first then it screws up the ordering and the client doesn’t know how to read the message that’s about to tell it what the other messages will be.