Search code examples
network-programmingkryonet

Slick2D KryoNet Applet


I'm using Kryonet with Slick2d to make a java game.

It works fine when running as a java application, however when running as an applet I get the following error:

00:00  INFO: [kryonet] Server opened.
00:04 DEBUG: [kryonet] Port 9991/TCP connected to: /(ip):55801
00:04 DEBUG: [kryo] Write: RegisterTCP
00:04  INFO: [kryonet] Connection 1 connected: /(ip)
00:04  INFO: [SERVER] Someone has connected.
00:04 ERROR: [kryonet] Error reading TCP from connection: Connection 1
com.esotericsoftware.kryonet.KryoNetException: Error during deserialization.
    at com.esotericsoftware.kryonet.TcpConnection.readObject(TcpConnection.java:141)

    at com.esotericsoftware.kryonet.Server.update(Server.java:192)
    at com.esotericsoftware.kryonet.Server.run(Server.java:350)
    at java.lang.Thread.run(Unknown Source)
Caused by: com.esotericsoftware.kryo.KryoException: Buffer underflow.
    at com.esotericsoftware.kryo.io.Input.require(Input.java:162)
    at com.esotericsoftware.kryo.io.Input.readLong(Input.java:621)
    at com.esotericsoftware.kryo.io.Input.readDouble(Input.java:745)
    at com.esotericsoftware.kryo.serializers.DefaultSerializers$DoubleSerializer.read(DefaultSerializers.java:141)
    at com.esotericsoftware.kryo.serializers.DefaultSerializers$DoubleSerializer.read(DefaultSerializers.java:131)
    at com.esotericsoftware.kryo.Kryo.readClassAndObject(Kryo.java:735)
    at com.esotericsoftware.kryonet.KryoSerialization.read(KryoSerialization.java:57)
    at com.esotericsoftware.kryonet.TcpConnection.readObject(TcpConnection.java:139)
    ... 3 more
00:04  INFO: [SERVER] Someone has disconnected.
00:04  INFO: [kryonet] Connection 1 disconnected.

The server is running locally as a runnable jar and the client applet in a HTML file locally aswell, which is running xampp to act as a web server.

I've tried different serializers, buffer sizes and sending just single String/Booleans etc, it just doesn't seem to like anything. The client connects to the server perfectly fine, however when it comes to sending any packets, I get the above error, no matter what packet is sent.

Any help/advice would be really appreciated - I've been stumped on this for a while! Thanks


Solution

  • I believe I have the same problem or at least similar one. I am using Kryonet for server and client. The client is an applet and when I run it trough Eclipse's Applet Viewer it works fine. When I run it trough a web server I get similar errors. Client and server connect, server receives client's packets, but the client gives an error wherever it tries any deserialization. I found that the applet permissions are to blame. If you change the permissions of the Applet Viewer (if you are using Eclipse) to be the same as of a web page, you will get the same errors. The advantage is that you can then debug the problem.

    To change the permissions for Eclipse: Go to your project folder \bin\ and open "java.policy.applet". Inside you should have:

    grant {
      permission java.security.AllPermission;
    };
    

    Change that to:

    grant {
        permission java.io.FilePermission "<<ALL FILES>>", "read, write, execute, delete";  
        permission java.net.SocketPermission "*", "accept, connect, listen, resolve";  
        permission java.util.PropertyPermission "*", "read, write";  
        permission java.lang.RuntimePermission "*";  
        permission java.awt.AWTPermission "showWindowWithoutWarningBanner";  
    };
    

    With this change I had the same behavior for Applet Viewer as with an embedded applet. This is not a full solution, but can help in finding the cause of the problem.

    Update: I have found what is the problem in my case. The problem is in the FieldSerializer and the other serializers using it. When a class is registered, the FieldSerializer goes over it's fields and set's them all to be accessible. This operation is not allowed for an applet. The result is wrong registration and serialization/deserialization. I have found 2 workarounds:

    1) Use of another serializer. The default one is a FieldSerializer and can be changed using

    public void setDefaultSerializer (Class<? extends Serializer> serializer)
    

    another option is to set the serializer when registering each class. Do not use serializers based on the FieldSerializer.

    2) Try to fix the FieldSerializer. What I am doing is not fully correct, but it works in my case. We will make the FieldSerializer continue the registration if setting of the accessibility is causing an Exception. Another thing we need to do is set all fields of the classes we register to public. TO change the FieldSerializer you need the Kryo sources. Go to FieldSerializer.java, mething rebuildCachedFields(). You will find the following code there:

        if (!field.isAccessible()) {
            if (!setFieldsAsAccessible) continue;
            try {
                field.setAccessible(true);
            } catch (AccessControlException ex) {
                continue;
            }
        }
    

    You need to change that to:

        if (!field.isAccessible()) {
            if (setFieldsAsAccessible)
            try {
                field.setAccessible(true);
            } catch (AccessControlException ex) {
            }
        }
    

    The other thing that needs to be changed is all of the registered classes to have only public fields.