Zay-ES-Net tutorials and references?

I was wondering if there was any resources for networking with zay-es? There does not seem much on the wiki. On an old thread someone mentioned monkeyzone was an example but I could not really find anything in the source. Is it similar to networking without zay-es?

No… not of Zay-ES networking. The Zay-ES networking example is MonkeyTrap and its in the same repo as the Zay-ES source.

No, but it’s similar to Zay-es without networking.

On JME 3.1, with the latest version of Zay-ES, it’s even easier.

Off the top of my head, the basic steps are:

  1. Register EntityDataHostedService with your JME Server’s service manager (Server.getServices())
    EntityDataHostedService (zay-es-net 1.5.2 API)
  2. On the server, make sure to register all of your component classes with the Serializer.
    http://javadoc.jmonkeyengine.org/com/jme3/network/serializing/Serializer.html
  3. On the server, make sure to periodically call sendUpdates() on the EntityDataHostedService. (Like, several times a second depending on how responsive you want updates to be on the clients.)
  4. Register the EntityDataClientService with JME’ Client’s service manager (Client.getServices())
    EntityDataClientService (zay-es-net 1.5.2 API)

That class will give you the EntityData instance that you should use on the client.

Note: entities can only be modified on the server (client EntityData is read-only), so you will have to send your own messages to the server to do entity updates but everything else will work over the client EntityData.

cant believe I misread that!

Right now I am just on 3.0, but will probably switch when its a later stage of alpha. I heard the transition in general is pretty easy as of now.

This actually clears up a lot though thank you :smile:.

Note: Zay-ES should still work in 3.0, I think… but the network setup is slightly more difficult. MonkeyTrap was written on 3.0 so will work as an example of that.

How hard would you say it is to switch from 3.0 to 3.1 for zay-es-net. Maybe I should make the switch now.

The sooner the easier ^^

Agreed with @methusalah. Might as well start on the right foot as soon as possible.

Alright just installed 3.1, thanks for all the advice and suggestions!

Sorry to revive this thread, but I am just now trying to use this. I set up the client and server and added the services mentioned to the corresponding one, however there seems to be a problem with channels.

Server Abstract State I made:

@Override
public void initialize(AppStateManager stateManager, Application app){
    this.app = (SimpleApplication)app;
    this.ed = app.getStateManager().getState(EntityDataState.class).getEntityData();
    this.edhs = new EntityDataHostedService(Globals.CHANNEL,this.ed);
    
    server.getServices().addService(edhs);
    Serializer.registerClasses(Input.class,
            Update.class,
            Position.class,
            Model.class);
    server.addMessageListener(new ServerListener(), Input.class, Update.class);
    
    //server.addChannel(Globals.CHANNEL);
    server.start();
    //this.edhs.start();
    
    
    System.out.println("Server Running");
}

@Override
public void update(float tpf){
    edhs.sendUpdates();
}

ed is a ObservablEntityData.

Client Abstract State:

@Override
public void initialize(AppStateManager stateManager, Application app){
    this.edcs = new EntityDataClientService(Globals.CHANNEL);
    
    client.getServices().addService(edcs);
    Serializer.registerClasses(Input.class,
            Update.class,
            Position.class,
            Model.class);

    client.start();
}

and I try accessing the ObservableEntityData on the client here:

@Override
public void initialize(AppStateManager stateManager, Application app){
    this.app = (SimpleApplication)app;
    this.edcs = stateManager.getState(ClientState.class).getClientService(); //might have to do this per update
    this.ed = this.edcs.getEntityData();
    this.entities = ed.getEntities(Position.class, Model.class); // here is ERROR1
    
    this.app.getViewPort().setBackgroundColor(ColorRGBA.DarkGray);

    app.getCamera().lookAt(Vector3f.UNIT_Z, Vector3f.UNIT_Y);
    app.getCamera().setLocation(new Vector3f(0, 0, 60));

    DirectionalLight light = new DirectionalLight();
    light.setDirection(new Vector3f(1, 1, -1));
    this.app.getRootNode().addLight(light);

    modelFactory = new ModelFactory(this.app.getAssetManager());
}

ERROR1: now when Globals.CHANNEL == 0 or 1 (the defaults if I understand correctly). I get this error when i try to start the client after the server is started.

SEVERE: Uncaught exception thrown in Thread[jME3 Main,5,main]
java.lang.IllegalArgumentException: Channel is undefined:0
  at com.jme3.network.base.DefaultClient.send(DefaultClient.java:244)
  at com.simsilica.es.client.RemoteEntityData.getEntities(RemoteEntityData.java:380)
  at com.simsilica.es.client.RemoteEntityData.getEntities(RemoteEntityData.java:360)
  at appstates.VisualAppState.initialize(VisualAppState.java:50)
  at com.jme3.app.state.AppStateManager.initializePending(AppStateManager.java:251)
  at com.jme3.app.state.AppStateManager.update(AppStateManager.java:281)
  at com.jme3.app.SimpleApplication.update(SimpleApplication.java:243)
  at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:152)
  at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:192)
  at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:233)
  at java.lang.Thread.run(Thread.java:745)

If i try to set a new channel 0,1,2 etc… I get this when i try to start the server.

SEVERE: Uncaught exception thrown in Thread[jME3 Headless Main,5,main]
com.jme3.network.kernel.KernelException: Error hosting:0.0.0.0/0.0.0.0:2
  at com.jme3.network.kernel.tcp.SelectorKernel.initialize(SelectorKernel.java:97)
  at com.jme3.network.base.KernelAdapter.initialize(KernelAdapter.java:100)
  at com.jme3.network.base.DefaultServer.start(DefaultServer.java:174)
  at appstates.ServerState.initialize(ServerState.java:57)
  at com.jme3.app.state.AppStateManager.initializePending(AppStateManager.java:251)
  at com.jme3.app.state.AppStateManager.update(AppStateManager.java:281)
  at com.jme3.app.SimpleApplication.update(SimpleApplication.java:243)
  at com.jme3.system.NullContext.run(NullContext.java:132)
  at java.lang.Thread.run(Thread.java:745)
Caused by: java.net.SocketException: Permission denied

When the server is started the client can succesfully send messages right before it fails. I hope this isnt too much throw at once for a question like this. Any ideas?

This is something that kind of has to be cleaned up with a default constructor. If you want to pass a real channel like 0 then you will have to add a channel. Here, channels are indexed 0-? whatever you’ve created as for channels on your server. -1 and -2 are the default channels that are already created… but they are generally accessed with the constants MessageConnection.DEFAULT_RELIABLE and MessageConnection.DEFAULT_UNRELIABLE.

If you want to create your own channel for the entity messages then you need to add a channel on the server… but as hinted in the javadoc, the parameter to the addChannel() method is a port and not a channel index.

ahh, alright thanks! I got confused cause in the source they had 1 and 2 also under static names. I actually ran into another problem however in the same spot as before:-

this.entities = ed.getEntities(Position.class, Model.class); // here is ERROR1

except its the serializer not being able to register the class:

java.lang.IllegalArgumentException: Class has not been registered:class com.simsilica.es.net.GetEntitySetMessage
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:246)
at com.simsilica.es.client.RemoteEntityData.getEntities(RemoteEntityData.java:380)
at com.simsilica.es.client.RemoteEntityData.getEntities(RemoteEntityData.java:360)
at appstates.VisualAppState.initialize(VisualAppState.java:50)

this one seemed a little weird to me so I checked what I could think of making sure GetEntitySetMessage was serializable etc… and even tried serializing along my components as I posted above and got this:

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:266)
at com.jme3.network.base.DefaultClient.send(DefaultClient.java:246)
at com.simsilica.es.client.RemoteEntityData.getEntities(RemoteEntityData.java:380)
at com.simsilica.es.client.RemoteEntityData.getEntities(RemoteEntityData.java:360)
at appstates.VisualAppState.initialize(VisualAppState.java:50)
at com.jme3.app.state.AppStateManager.initializePending(AppStateManager.java:251)
at com.jme3.app.state.AppStateManager.update(AppStateManager.java:281)

I did get some Info output to the console that seems to indicate GetEntitySetMessage is not the first to get serialized, but I cant imagine why this class in particular is having a problem.

You have to register all of your serializable component classes with the Serializer on the server. It’s not automatic (can’t be actually.)

I guess I am confused because I thought I was doing that here.

@Override
public void initialize(AppStateManager stateManager, Application app){
    this.app = (SimpleApplication)app;
    this.ed = app.getStateManager().getState(EntityDataState.class).getEntityData();
    this.edhs = new EntityDataHostedService(Globals.CHANNEL,this.ed);

    server.getServices().addService(edhs);
    Serializer.registerClasses(Input.class, //HERE
        Update.class,
        Position.class,
        Model.class);
    server.addMessageListener(new ServerListener(), Input.class, Update.class);

...

My only components so far are Position.class, and Model.class. (Inptut.class and Update.class are just AbstractMessages). Both are marked with @Serializable as well as extend EntityComponent. I also have the AbstractAppState that holds ObservableEntityData as serializable.and tried serializing that aswell (shot in the dark).

Ah… you must be calling getEntities() before you are actually connected.

Register a connection listener with the client and then don’t do anything with the connection until you are connected.

Added the connectionListener:

@Override
public void initialize(AppStateManager stateManager, Application app){
    this.edcs = new EntityDataClientService(Globals.CHANNEL);
    
    client.addClientStateListener(this);
    
    Serializer.registerClasses(Input.class,
            Update.class,
            Position.class,
            Model.class);
    client.getServices().addService(edcs);

    client.start();
    
    //HERE ON IS ADDED CODE
    synchronized(lock){
        if(!client.isConnected()){
            try {
                System.out.println();
                System.out.println("wating for connection to complete");
                System.out.println();
                lock.wait();
            } catch (InterruptedException ex) {
                Logger.getLogger(ClientState.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
}
...
@Override
public void clientConnected(Client c){
    synchronized(lock){
        System.out.println("ClientConnected Called notifying object");
        lock.notify();
    }
}

@Override
public void clientDisconnected(Client c, DisconnectInfo info){
    
}

and just to be sure I print the state of client.isConnected(); right before the error happens and it evaluates to true.

I still get the same error :frowning: :

wating for connection to complete

ClientConnected Called notifying object

true

Feb 27, 2016 9:41:23 AM com.jme3.app.Application handleError
SEVERE: Uncaught exception thrown in Thread[jME3 Main,5,main]
java.lang.IllegalArgumentException: Class has not been registered:class com.simsilica.es.net.GetEntitySetMessage
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:246)
at com.simsilica.es.client.RemoteEntityData.getEntities(RemoteEntityData.java:380)
at com.simsilica.es.client.RemoteEntityData.getEntities(RemoteEntityData.java:360)
at appstates.VisualAppState.initialize(VisualAppState.java:53)
at com.jme3.app.state.AppStateManager.initializePending(AppStateManager.java:251)

maybe im doing something really simple wrong? calling functions in a weird order? I will keep investigating.

I see you are also registering classes on the client? What version of JME are you using again?

Yea I am, but it dosnt seem to make a difference if i remove the components (keeping the non-components serialized). I am running 3.1

There were some networking bug fixes after alpha 1… I don’t know if they affect this or not.

Yea maybe I will revisit this later. I was going to start thinking about networking right away as many people seem to recommend it, however adding zay-es-net to a zay-es non-networking game actually does not seem that complicated after learning more and just trying it (I could be horribly wrong :stuck_out_tongue:). Hopefully alpha 2 is not too too far away and I will try again then.

Hi there !

If you are creating a game with Zay-ES from zero, maybe you could consider participating to the Alchemist alpha test? The networking version of Zay-ES is not currently implementd but we can work on that.

Have fun !