Network issue using RemoteEntityData

Hi everybody,

I’m trying to figure out how to set up a RemoteEntityData to use my server side entities on the client. I had a look at the MonkeyTrap project and tried to create my code from scratch afterwards from what I have learned there. But as soon as my client tries to retrieve an EntitySet from the server, I get the following exception:

java.lang.IllegalArgumentException: Channel is undefined:0
at com.jme3.network.base.DefaultClient.send(DefaultClient.java:216)
at com.simsilica.es.client.RemoteEntityData.getEntities(RemoteEntityData.java:346)
at com.simsilica.es.client.RemoteEntityData.getEntities(RemoteEntityData.java:326)
at mygame.GameClient.simpleInitApp(GameClient.java:48)
at com.jme3.app.SimpleApplication.initialize(SimpleApplication.java:226)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.initInThread(LwjglAbstractDisplay.java:130)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:207)
at java.lang.Thread.run(Thread.java:744)

My test project consists of a client, a server and a single component. It doesn’t really do anything besides setting of the network connection and creating the entityData.

Here’s the server:

public class GameServer extends SimpleApplication {

    public static final int DEFAULT_PORT = 4284;
    private Server server;
    private EntityData entityData = new DefaultEntityData();
    private EntityDataHostService edHost;

    public static void main(String[] args) {
        GameServer app = new GameServer();
        app.start(JmeContext.Type.Headless);
    }

    public GameServer() {
        setShowSettings(false);
    }

    @Override
    public void simpleInitApp() {
        EntitySerializers.initialize();
        Serializer.registerClass(TestComponent.class);
        settings.setFrameRate(20);

        // Create Entity
        EntityId entityId = entityData.createEntity();
        entityData.setComponent(entityId, new TestComponent(0));

        try {
            // Start Server
            server = Network.createServer("Server", 1, DEFAULT_PORT, DEFAULT_PORT);
            server.addChannel(DEFAULT_PORT + 1);
            edHost = new EntityDataHostService(server, 0, entityData);
            server.start();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}

And here is the client:

public class GameClient extends SimpleApplication {

    private Client client;
    private RemoteEntityData entityData;
    private EntitySet entitySet;

    public static void main(String[] args) {
        GameClient app = new GameClient();
        app.start();
    }

    @Override
    public void simpleInitApp() {
        EntitySerializers.initialize();
        Serializer.registerClass(TestComponent.class);

        try {
            // Start client
            client = Network.connectToServer("Server", 1, "127.0.0.1", GameServer.DEFAULT_PORT, GameServer.DEFAULT_PORT);
            client.start();

            // Get entities from server
            entityData = new RemoteEntityData(client, 0);
            entitySet = entityData.getEntities(TestComponent.class);  // <-- This is the line that causes the exception
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}

My TestComponent should not be relevant, but just for the sake of completeness, here it is:

@Serializable
public class TestComponent implements EntityComponent {

    private float value = 0;

    public TestComponent() {
    }

    public TestComponent(float value) {
        this.value = value;
    }

    public float getValue() {
        return value;
    }
}

I’m pretty sure that I made a very simple mistake somewhere.
Hopefully one of you can help me out here.

Not familiar with ZayES’s code but I will try to help you out. Judging from the stacktrace, you didn’t define the channel on the client side. Try registering that channel on the clientside also.

Nah, he shouldn’t have to register it on the client as the server will send the info.

To OP:
I believe the issue might be that you are creating the RemoteEntityData before you are actually connected. You’ll notice that MonkeyTrap doesn’t set up any of that until the connection has been established by getting its ClientStateListener.clientConnected() method called.

You will need to do something similar.

Note: in 3.1 there is new service model that makes it easier to add services early and they will be initialized as part of the client connection setup. Simplifies this code a little but either way you will likely want to have a ClientStateListener for other reasons… since it also tells you when the connection drops.

Yes, that’s it!

And now that you mention it, it totally makes sense to wait for the client to finish connecting. I don’t know, why I didn’t think of that…

Thanks very much for the help. Now I can finally continue.