Networking serializing error


#1

Hi monkeys!! I’m relatively new to this community and I already need some help.
I’m trying to implement my first server - client game. Im following one tutorial on youtube, but when I’m trying to serialize my PlayerData.class, i’ve got the following error on console. I hope you might help me!

Grave: Uncaught exception thrown in Thread[LWJGL Renderer Thread,5,main]
java.lang.RuntimeException: Error serializing message
at com.jme3.network.base.MessageProtocol.messageToBuffer(MessageProtocol.java:81)
at com.jme3.network.base.DefaultClient.send(DefaultClient.java:237)
at com.jme3.network.base.DefaultClient.send(DefaultClient.java:207)
at mygame.ClientMain.simpleUpdate(ClientMain.java:65)
at com.jme3.app.SimpleApplication.update(SimpleApplication.java:242)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:151)
at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:185)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:228)
at java.lang.Thread.run(Thread.java:744)
Caused by: com.jme3.network.serializing.SerializerException: Error writing object for field:private mygame.PlayerData mygame.messages.PlayerDataMessage.data
at com.jme3.network.serializing.serializers.FieldSerializer.writeObject(FieldSerializer.java:180)
at com.jme3.network.serializing.Serializer.writeClassAndObject(Serializer.java:389)
at com.jme3.network.base.MessageProtocol.messageToBuffer(MessageProtocol.java:73)
… 8 more
Caused by: com.jme3.network.serializing.SerializerException: Error writing object for field:private com.jme3.math.Vector3f mygame.PlayerData.location
at com.jme3.network.serializing.serializers.FieldSerializer.writeObject(FieldSerializer.java:180)
at com.jme3.network.serializing.Serializer.writeClassAndObject(Serializer.java:389)
at com.jme3.network.serializing.serializers.FieldSerializer.writeObject(FieldSerializer.java:175)
… 10 more
Caused by: java.lang.NullPointerException
at com.jme3.network.serializing.serializers.Vector3Serializer.writeObject(Vector3Serializer.java:55)
at com.jme3.network.serializing.serializers.FieldSerializer.writeObject(FieldSerializer.java:173)
… 12 more


#2

The Vector3f serializer doesn’t support null values.


#3

So i should use new Vector3f(Vector3f.ZERO)? the first time I serialize the message?


#4

Yeah, just set it to something.

…this isn’t a great way to pass around player location anyway but you’ll probably figure that out further down the networked-game road.


#5

//@Serializable
public class PlayerData {
private Vector3f location;
private Quaternion rotation;
private Vector3f walkDirection;

//CLIENT

private int id;
private String name ="";

public PlayerData(){
    location = new Vector3f(Vector3f.ZERO);
    rotation = new Quaternion(Quaternion.ZERO);
    walkDirection = new Vector3f(Vector3f.ZERO);}

public PlayerData(int id){
    this.id = id;
    location = new Vector3f(Vector3f.ZERO);
    rotation = new Quaternion(Quaternion.ZERO);
    walkDirection = new Vector3f(Vector3f.ZERO);
}

public PlayerData(int id, String name){
    this.name = name;
    this.id = id;
    location = new Vector3f(Vector3f.ZERO);
    rotation = new Quaternion(Quaternion.ZERO);
    walkDirection = new Vector3f(Vector3f.ZERO);
}
/**
 * @return the location
 */
public Vector3f getLocation() {
    return location;
}

/**
 * @param location the location to set
 */
public void setLocation(Vector3f location) {
    this.location = location;
}

/**
 * @return the rotation
 */
public Quaternion getRotation() {
    return rotation;
}

/**
 * @param rotation the rotation to set
 */
public void setRotation(Quaternion rotation) {
    this.rotation = rotation;
}

/**
 * @return the walkDirection
 */
public Vector3f getWalkDirection() {
    return walkDirection;
}

/**
 * @param walkDirection the walkDirection to set
 */
public void setWalkDirection(Vector3f walkDirection) {
    this.walkDirection = walkDirection;
}

/**
 * @return the id
 */
public int getId() {
    return id;
}

/**
 * @return the name
 */
public String getName() {
    return name;
}

/**
 * @param name the name to set
 */
public void setName(String name) {
    this.name = name;
}

void setId(int id) {
    this.id=id;}

}

just done this, stille getting the same error


#6

Then you aren’t really running that code on the side that did the sending.

Fact 1: The error indicated that a Vector3f field was null. No ifs/ands/or buts.
Fact 2: The code above does not have null fields.

Therefore: you aren’t running that code or something else is nulling out the fields. It cannot be otherwise.

Add a toString() method to your message class and System.out.println() it right before you send it.


#7

Got a NullPointer on location;

@Override
public void simpleUpdate(float tpf){
    if (client.isConnected()){
        System.out.println("id: "+ playerData.getId());
        System.out.println("loc: "+playerData.getLocation().toString());
        System.out.println("rot: "+playerData.getRotation().toString());
        System.out.println("walkdir: "+playerData.getWalkDirection().toString());
        System.out.println("name: "+playerData.getName());
        client.send(new PlayerDataMessage(playerData));
    }

I’ll show you my clientMain.java code:
Btw: Thank you for your help.

package mygame;

import mygame.messages.TextMessage;
import com.jme3.app.SimpleApplication;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.util.CollisionShapeFactory;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.network.Client;
import com.jme3.network.ClientStateListener;
import com.jme3.network.Network;
import com.jme3.scene.Geometry;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Box;
import com.jme3.system.JmeContext;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import mygame.messages.PlayerDataMessage;

public class ClientMain extends SimpleApplication implements ClientStateListener{
private Client client;
private PlayerAppState playerAppState;
private BulletAppState bulletAppState;
private PlayerData playerData;
private SceneManager sceneManager = new SceneManager();

public static void main(String[] args){
    Utils.initSerializers();
    
    ClientMain app = new  ClientMain();
    app.setPauseOnLostFocus(false);
    app.start(JmeContext.Type.Display);
}
@Override
public void simpleInitApp() {
    bulletAppState = new BulletAppState();
    stateManager.attach(bulletAppState);
    
    bulletAppState.setDebugEnabled(true);
    
    flyCam.setMoveSpeed(30);
    flyCam.setDragToRotate(true);
    cam.setLocation(new Vector3f(-13.749275f, 15.582828f, 109.14174f));
    cam.setRotation(new Quaternion(0.0020679887f, 0.9958238f, -0.08823604f, 0.023347257f));
    
    initPlayerData();
    initConnection();
    initAppStates();
    initScene();
}

@Override
public void simpleUpdate(float tpf){
    if (client.isConnected()){
        System.out.println("id: "+ playerData.getId());
        System.out.println("loc: "+playerData.getLocation().toString());
        System.out.println("rot: "+playerData.getRotation().toString());
        System.out.println("walkdir: "+playerData.getWalkDirection().toString());
        System.out.println("name: "+playerData.getName());
        client.send(new PlayerDataMessage(playerData));
    }
}

private void initConnection() {
    try {
        client = Network.connectToServer(Utils.IP_ADDRESS, Utils.PORT);
        client.addMessageListener(new ClientListener(client,sceneManager));
        client.addClientStateListener(this);
        client.start();
        
        System.out.println("Cliente conectado.");
    } catch (IOException ex) {
        Logger.getLogger(ClientMain.class.getName()).log(Level.SEVERE, null, ex);
    }
}    

public void clientConnected(Client c) {
    playerData.setId(c.getId());
    client.send(new TextMessage("Hello server! Soy el ID: "+c.getId()));
}

public void clientDisconnected(Client c, DisconnectInfo info) {
    client.send(new TextMessage("Desconectado id: "+c.getId()));
}

   
private void initAppStates(){
    playerAppState = new PlayerAppState(bulletAppState,rootNode,playerData);
    stateManager.attach(playerAppState);
}

private void initScene() {
    Spatial scene = new Geometry("Scene", new Box(100,1,100));
    scene.setLocalTranslation(0, 0, 0);
    Material mathb = new Material(assetManager,
            "Common/MatDefs/Misc/Unshaded.j3md");
    mathb.setColor("Color", ColorRGBA.Pink);
    scene.setMaterial(mathb);
    com.jme3.bullet.collision.shapes.CollisionShape sceneShape =
            CollisionShapeFactory.createMeshShape(scene);
    RigidBodyControl landscape = new RigidBodyControl(sceneShape, 0);
    scene.addControl(landscape);
    bulletAppState.getPhysicsSpace().addAll(scene);
    rootNode.attachChild(scene);
    
    AmbientLight ambient = new AmbientLight();
    ambient.setColor(ColorRGBA.White);
    rootNode.addLight(ambient);
    
    DirectionalLight sun = new DirectionalLight();
    sun.setDirection((new Vector3f(-0.5f,-0.5f,-0.5f)).normalizeLocal());
    sun.setColor(ColorRGBA.White);
    rootNode.addLight(sun);
}

private void initPlayerData() {
    playerData = new PlayerData();
}

}


#8

Note: toString() is completely redundant there unless you actually like NPEs when the location is null.

And you should get in the habit of creating proper toString() methods for your objects in cases like this. Then you could just print playerData itself.

Maybe PlayerAppState is clearning it?

I mean… going back and forth on the forum like this is going to take forever where as finding where you are setting the field to null will take you like 2 minutes on your own. Just debug the issue… find out where you are setting it to null. Use the debugger and set a breakpoint if you want.


#9

thank you! I’ll check again and post here the results :stuck_out_tongue:


#10

Or if you like defensive coding, you can change the above to:

public void setLocation(Vector3f location) {
    if( location == null ) {
        throw new IllegalArgumentException("location cannot be null");
    }
    this.location = location;
}

…then you’ll find out exactly where it’s happening.


#11

worked using this:

public void clientConnected(Client c) {
playerData.setId(c.getId());
playerData.setLocation(new Vector3f(Vector3f.ZERO));
playerData.setRotation(new Quaternion(Quaternion.ZERO));
playerData.setWalkDirection(new Vector3f(Vector3f.ZERO));
client.send(new TextMessage("Hello server! Soy el ID: "+c.getId()));
}

You were right, all Vectors and Quats were null. It was supposed to start on Zero, but they didn’t.


#12

…because you set them to null somewhere or you weren’t really using the PlayerData you posted that initializes them.


#13

Thank you so much anyway !


#14

Better yet:

public void setLocation(Vector3f location) {
    this.location = Objects.requireNonNull( location, "location cannot be null" );
}

EDIT: Actually the above it what I meant to type.


#15

Why not? That seems like something people might want to send.

(I know it’s a few months late but I just found out about this issue and I’m curious why that decision was made)


#16

Oversight? Before my time.

The problem is that fixing it silently breaks every existing game that upgrade by causing corruption of their streams. It also increases the size of data when sending Vector3f which could conceivably cause problems for some games.

So, fixing it breaks almost everyone and helps almost no one.