Networking example?

Hi everyone! I have been looking around for ages now for an example of networking that actually involves syncing Nodes, Spatials etc between clients. I have taken a look at the example networked game but I haven’t quite figured it out yet.

My question is, does anyone know how to create synchronisation between client and client? I can set up the server and connect clients to it but I fail to understand how I can pass information between the server and the client? Anyone who can help me I will be very grateful.

Thanks.

As always, the documentation wiki helps best:

https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:advanced:networking



Try entering search terms like these (“networking”, “collision” etc.) in the search field in the upper right of the SDK too, it contains all info from the wiki.

Thanks normen, I have read this entire page and correct me if I’m wrong but it only seems to explain how to send messages between server and client? I am talking about sending info like Vector3f’s, Node’s and Spatial’s.

Thanks again.

Yeah, there isn’t more to it. Just send the x,y,z components via a message, probably also speed & direction somehow. You don’t wanna send a whole Spatial, just the relevant data to recreate it on each client.

[java]

client.send(Message m)

server.send(Message m)

[/java]



That’s the short version. both the client instance and the server instance will have a send method you can use to send messages. On the server you would use the client.send to send to the client instance, on the client you would use server.send.



Aside from that, you need to have a way to receive the information on the client and the server. This is done by registereing a MessageListener. MessageListener is an interface with one method: receiveMessage. an instance of the implementing class is registered to the client and the server. Through this, you can receive and process the messages.



There is also the ErrorListener. This interface can be used to catch and process any excption which occur on the network connection. It works similar to the MessageListener and also needs to be registered with the server and the client.



I hope this helps out a bit. It should at least point you in the right direction. If you need more details, I can provide those as well, but by looking up those details yourself you’ll probably get more out of it.

Oh, so it is more like interpreting String data that is sent rather than the actual objects?

@ollie said:
Oh, so it is more like interpreting String data that is sent rather than the actual objects?

However you can best encode your data, yeah. A simple int/long that specifies a certain model template that client and server know would do just as well. Don't link the objects on screen and the "game objects" or "objects in your world" too much in your mind, the ones on screen are only there for the visual representation, ideally your game works without rendering anything just as well.

AH I think I get it now. I guess the name ‘Message’ is slightly misleading… Thanks for your help and I will report back if I manage to do something :slight_smile:

By the way i downloaded MonkeyZone a few days ago (because I think this is the network example) and I get this error:

java.lang.IndexOutOfBoundsException
at java.nio.Buffer.checkIndex(Buffer.java:514)
at java.nio.DirectFloatBufferU.put(DirectFloatBufferU.java:259)
at jme3tools.optimize.GeometryBatchFactory.doTransformTangents(GeometryBatchFactory.java:80)
at jme3tools.optimize.GeometryBatchFactory.mergeGeometries(GeometryBatchFactory.java:214)
at com.jme3.monkeyzone.WorldManager.createNavMesh(WorldManager.java:248)
at com.jme3.monkeyzone.ClientMain$6.run(ClientMain.java:354)
at java.lang.Thread.run(Thread.java:662)

Need I some old Jmonkey libs?

Just use the stable version.

Ok, I nearly have it working (I think) if someone could take a look at the source code for me.

The problem seems to be that one client won’t take the vector from the other.

Thanks again to anyone who can help.



Source Code

Ok, I nearly have it working (I think) if someone could take a look at the source code for me.

The problem seems to be that one client won’t take the vector from the other.

Thanks again to anyone who can help.



Source Code

Hm? I tried to use the librarys of the JMonkey Beta but i still get an error.

@ollie said:
Ok, I nearly have it working (I think) if someone could take a look at the source code for me.
The problem seems to be that one client won't take the vector from the other.
Thanks again to anyone who can help.

Source Code


Any chance you can post the relevant parts here in java blocks or post it in a way that doesn't require downloading and unpacking something?

I was hoping to look and see what your comment about "the term 'message' being slightly misleading" meant but I don't have time to download a rar, unpack it, etc.

@pspeed Alright, just a minute…

@ollie said:
@pspeed Alright, just a minute...

One day later.... Haha, sorry. I got sidetracked... Anyway...
These are what I believe to be the relevant classes.
[java]package com.neonmonkey;

import com.jme3.app.SimpleApplication;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.network.Client;
import com.jme3.network.Network;
import com.jme3.network.serializing.Serializer;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Node;
import com.jme3.system.JmeContext.Type;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
*
* @author Oliver
*/
public class GameClient extends SimpleApplication {

private Client client;

private Node player1, player2;

private ClientListener clientListener = new ClientListener();

public void simpleInitApp(){
registerClasses();
try {
client = Network.connectToServer("192.168.1.66", 6143);
client.start();
} catch (IOException ex) {
Logger.getLogger(GameClient.class.getName()).log(Level.SEVERE, null, ex);
}

client.addMessageListener(clientListener, Vector3fMessage.class);
player1 = (Node) assetManager.loadModel("Models/test.j3o");
player2 = player1;
rootNode.attachChild(player1);
rootNode.attachChild(player2);
DirectionalLight d = new DirectionalLight();
d.setColor(ColorRGBA.White.mult(2.5f));
d.setDirection(new Vector3f(3.0f, 5.0f, 12.0f));
AmbientLight a = new AmbientLight();
a.setColor(ColorRGBA.White.mult(2.5f));
rootNode.addLight(d);
rootNode.addLight(a);
player1.getWorldTranslation().set(cam.getLocation());
if(client.isConnected()) System.out.println("Connected to server!");
}

@Override
public void simpleUpdate(float tpf){
try{
client.send(new Vector3fMessage(cam.getLocation()));
System.out.println("1: " + clientListener.getVectorAt(0) + ", 2: " + clientListener.getVectorAt(1));
player1.getWorldTranslation().set(clientListener.getVectorAt(0));
player2.getWorldTranslation().set(clientListener.getVectorAt(1));
}catch(Exception e){}
}

@Override
public void simpleRender(RenderManager rm) {

}

private void registerClasses(){
Serializer.registerClass(Vector3fMessage.class);
}

public static void main(String[] args) {
SimpleApplication app = new GameClient();
app.start(Type.Display);
}
}
[/java]
[java]package com.neonmonkey;

import com.jme3.math.Vector3f;
import com.jme3.network.AbstractMessage;
import com.jme3.network.serializing.Serializable;

/**
*
* @author Oliver
*/
@Serializable
public class Vector3fMessage extends AbstractMessage {
private Vector3f vector;
public Vector3fMessage(){}
public Vector3fMessage(Vector3f v){
this.vector = v;
}
public Vector3f getVector3f(){
return vector;
}
}
[/java]
[java]package com.neonmonkey;

import com.jme3.math.Vector3f;
import com.jme3.network.Client;
import com.jme3.network.Message;
import com.jme3.network.MessageListener;
import java.util.ArrayList;

/**
*
* @author Oliver
*/
public class ClientListener implements MessageListener<Client> {

private ArrayList<Vector3f> vectors = new ArrayList<Vector3f>();

public ClientListener(){
vectors.add(Vector3f.ZERO);
vectors.add(Vector3f.ZERO);
}

public void messageReceived(Client source, Message m) {
//Vector3f
if(m instanceof Vector3fMessage){
Vector3fMessage v = (Vector3fMessage) m;
switch(source.getId()){
case 0:
vectors.set(0, v.getVector3f());
break;
case 1:
vectors.set(1, v.getVector3f());
break;
}
}
}

public Vector3f getVectorAt(int i){
return vectors.get(i);
}

}
[/java]

I print out the locations of the vectors where the players should be but the problem is, client number 1 only has vector number 1 moving correctly (number 2 stays at 0) and client number 2 has vector number 2 working but not number one. Thanks for any help :)

This isn’t your problem but it is a big problem:

player1.getWorldTranslation().set(clientListener.getVectorAt(0));

player2.getWorldTranslation().set(clientListener.getVectorAt(1));



Don’t ever do that. Call setLocalTranslation() with the appropriate value. Never ever ever operate directly on the vector returned by getWorldTranslation(). For one thing, it’s dangerous. For another, it doesn’t let the spatial know it needs to recalculate its bounds, etc…



Really, to be sure the messages are right then I’d need to see the server code too.



I see that you have misunderstood how messages are sent a little bit and that’s probably causing you issue.



A client sends a message to the server and the server can send messages back to that client or all clients, etc… If you need to know which client the message came from then you need to make that part of your message. The source.getId() on the client side is that client’s ID.



Typically, the server is keeping track of where everyone is and broadcasting state. Any peer-to-peer style messaging needs to be implemented by the game code (ie: include the originating client ID in the message).



You are better off in the long run if you let the server be authoritative, though.

1 Like

Thanks for the help. I will try something a little simpler I think before I get the hang of it. And thanks for the tip on setting the translation. So what kind of things should the client listener do? For example, a program in which I wanted two clients to each control their own cube that can be moved around in space and each of the clients can see each others cube update as they move about. How would I go about doing that?

How I would do it is to have each client send their position to the server. The server will then send out the “world state” back to the clients every so often.



You could do it the way you are but you will have to include the “object ID” your vector3f message… and ultimately you will run into problems of synchronization, etc. as the clients will always have slightly different views. A lot depends on what you ultimately want to do.



If you want to see an example where clients send messages that other clients see (along with who sent them) then you can look at the TestChatClient and TestChatServer. It embeds the source of the message as a name String I think but it could just as easily be a player ID or whatever… and the chat message a position+rotation.



What kind of game are you ultimately trying to make?

1 Like
@pspeed said:
What kind of game are you ultimately trying to make?

Well at the moment, I am simply experimenting with networking, I know my questions have made me sound like a major newbie but I am quite experienced with Java and 2D game development and I wanted to move on to 3D development. I have a lot of ideas brewing in my head but I feel that I need to learn something like networking as multiplayer is key in most modern games. I'm sure I will slowly learn it until I can do it off by heart.
I have one final question as well, by 'world state', do you mean the Node/Spatial? Thanks again pspeed :)