I’ve been tinkering with a new demo of jME-Networking / JGN using the FlagRush tutorial and though I have it working I think I’m going to have to create a new revision of Lesson9 that uses a standard heightmap rather than generating it on the fly:
I underestimated how much of an impact that would make. :o
Anyway, my hope was to make this work with Lesson9 of the flag rush tutorial without changing a single line of code…I now see that is going to be impossible unless Mojo wants to revise his tutorial to use a stored heightmap? ;) I should have this all finished and checked in by end of week for those of you that would like to see a complete and functional multi-client/server demonstration of using JGN in jME.
That would be really nice DF. I gave up JGN (and multiplayer for instance) for my first game but will come back to it asap. This demo would help me see how to do some stuff I think. Looking forward to seeing it.
Thanks
Why dont you host it and do a webstart ??
I could, but it's not like it's really a playable game…the flags aren't actually synchronized (so you'll get flags appearing in different spots on different clients) and no score is really kept. However, it would be pretty easy for someone to turn it into a functional and playable game if they wanted. This was intended to keep things simple and be used as an example.
darkfrog said:
Okay, this is finished and checked in. All you need is the latest version of JGN from the trunk and the jme-networking extension that contains the Flag Rush networking demonstration.
Sorry for my ignorance but where can I get the jme-networking extension sources from?
Thanks.
Filip
The post that has URLs for everything is here:
http://forum.captiveimagination.com/index.php/topic,97.0.html
However, the JARs have not yet been updated, so the only way to get the latest functionality is by checking it out from the SVN repositories referred to on that post. You’ll need JGN and jme-networking and the source code for FlagRushNetworking is in the jme-networking project.
I just updated my local copies of JGN and jme-networking from the SVN server, but I'm getting a class not found in the jme-networking classes for the ClientSynchronizer and SynchronizationListener, also the package com.captiveimagination.jgn.sync.* doesn't seem to exist:
SimpleTestClient.java
ClientSynchronizer cSynchronizer = new ClientSynchronizer(controller, client);
cSynchronizer.register((short)1, test.getBox(), 50, 0); // Register the client panel to send send updates to the server every 50ms
JGN.createThread(cSynchronizer).start();
SynchronizationListener clientListener = new SynchronizationListener(cSynchronizer, false);
client.addMessageListener(clientListener);
}
Maybe my copy is messed up, but I just wanted to make sure all the new files were committed to the JGN SVN.
The newest version of JGN got rid of all that and created a new package com.captiveimagination.jgn.synchronization instead. I forgot to delete the old example that was still relying on it…I had removed it from the build path in Eclipse so I could still reference it, but forgot to remove it when I was done updating. :o
I've deleted it from SVN now. If you update everything should be fine.
Cool I updated and everything is working good, I tried out the Flagrush JGN networking, looks pretty sweet!
Nice!
Looking forward to check it out!
Haven't checked up with JGN for a few days, does the latest JGN contain your planned updates (System handling Authoritive/Passive registration of spatials etc.?)
Yes, the updates refer to JGN and jme-networking.
I've got some other work I need to do on JGN tonight anyway, so it shouldn't be too bad to figure out what's going on with your issue at the same time.
Which steps should exactly be taken to unregister a synchronized spatial?
I made a very basic example where I made a box that could float arround but whenever I use the "unregister" method I seem to get troubles. Im guessing I should do more than a "clientSyncManager.unregister(box)" call.
The problems I get vary from nullpointers and the SynchronizerManager not having cleared the "spatial list" after the unregister call. The example is very small (and even uses gamestates! woohoo!).
If unregistering synced spatials -should- work with only the unregister method, I could ofcourse post it.
Edit: To be on the safe side, i've added my package
simpleClientTest.java
public class simpleClientTest extends simpleSyncObjectManager
{
private SynchronizationManager clientSyncManager;
private simpleGameTest gameInst;
/** Creates a new instance of simpleClientTest */
public simpleClientTest() throws UnknownHostException, IOException, InterruptedException
{
// Define the server address
InetSocketAddress serverReliable = new InetSocketAddress(InetAddress.getLocalHost(), 9100);
InetSocketAddress serverFast = new InetSocketAddress(InetAddress.getLocalHost(), 9200);
// Initialize networking
InetSocketAddress clientReliable = new InetSocketAddress(InetAddress.getLocalHost(), 0);
InetSocketAddress clientFast = new InetSocketAddress(InetAddress.getLocalHost(), 0);
JGNClient client = new JGNClient(clientReliable, clientFast);
JGN.createThread(client).start();
// Instantiate an instance of a JMEGraphicalController
JMEGraphicalController controller = new JMEGraphicalController();
// Create SynchronizationManager instance for this server
clientSyncManager = new SynchronizationManager(client, controller);
clientSyncManager.addSyncObjectManager(this);
JGN.createThread(clientSyncManager).start();
gameInst = new simpleGameTest(true);
setScene(gameInst.getRootNode());
// Connect to the server before we register anything
System.out.println("Connecting!");
client.connectAndWait(serverReliable, serverFast, 5000);
System.out.println("Connected!");
client.getServerConnection().getReliableClient().addMessageListener(new MessageListener()
{
public void messageCertified(Message message)
{
System.out.println("Message Certified: " + message);
}
public void messageFailed(Message message)
{
System.out.println("Message Failed: " + message);
}
public void messageReceived(Message message)
{
System.out.println("Message Received: " + message);
}
public void messageSent(Message message)
{
System.out.println("Message Sent: " + message);
}
});
clientSyncManager.register(controller, gameInst.getPlayerSpatial(), new SynchronizeCreateMessage(), 50);
System.out.println("Next: " + clientSyncManager.nextId());
//A test thread to test Unregistering
removeThread rmThread = new removeThread(clientSyncManager, gameInst.getPlayerSpatial());
rmThread.start();
}
public static void main (String[] args) throws UnknownHostException, IOException, InterruptedException
{
simpleClientTest app = new simpleClientTest();
}
}
class removeThread extends Thread
{
private SynchronizationManager clientSyncManager;
private Spatial player;
public removeThread(SynchronizationManager clientSyncManager, Spatial player)
{
this.player=player;
this.clientSyncManager=clientSyncManager;
}
public void run()
{
while(true)
{
if(MouseInput.get().isButtonDown(1))
{
clientSyncManager.unregister(player);
}
}
}
}
simpleServerTest.java
public class simpleServerTest extends simpleSyncObjectManager
{
public simpleServerTest() throws UnknownHostException, IOException
{
// Initialize networking
InetSocketAddress serverReliable = new InetSocketAddress(InetAddress.getLocalHost(), 9100);
InetSocketAddress serverFast = new InetSocketAddress(InetAddress.getLocalHost(), 9200);
JGNServer server = new JGNServer(serverReliable, serverFast);
// Instantiate an instance of a JMEGraphicalController
JMEGraphicalController controller = new JMEGraphicalController();
// Create SynchronizationManager instance for this server
SynchronizationManager serverSyncManager = new SynchronizationManager(server, controller);
serverSyncManager.addSyncObjectManager(this);
JGN.createThread(server).start();
JGN.createThread(serverSyncManager).start();
simpleGameTest gameInst = new simpleGameTest(false);
setScene(gameInst.getRootNode());
}
public static void main (String[] args) throws UnknownHostException, IOException
{
simpleServerTest app = new simpleServerTest();
}
}
simpleGameTest.java
public class simpleGameTest
{
private StandardGame game;
private DebugGameState state;
private Box playerBox;
private String clientTitle;
/** Creates a new instance of simpleGameTest */
public simpleGameTest(boolean clientConn)
{
if (clientConn)
{
clientTitle = "Game client";
}
else
clientTitle = "Server client";
game = new StandardGame(clientTitle);
game.start();
state = new DebugGameState();
GameStateManager.getInstance().attachChild(state);
state.setActive(true);
state.getLightState().setEnabled(false);
if(clientConn)
BuildPlayer();
simpleControlGameState controlState = new simpleControlGameState(playerBox);
GameStateManager.getInstance().attachChild(controlState);
controlState.setActive(true);
state.getRootNode().updateRenderState();
}
public void BuildPlayer()
{
playerBox = new Box("PlayerBox",new Vector3f(0,0,0), new Vector3f(2,4,2));
playerBox.setModelBound(new BoundingBox());
playerBox.updateModelBound();
state.getRootNode().attachChild(playerBox);
}
public Spatial getPlayerSpatial()
{
return playerBox;
}
public Node getRootNode()
{
return state.getRootNode();
}
}
simpleControlGameState.java
public class simpleControlGameState extends GameState
{
private Box playerBox;
/** Creates a new instance of EmptyGameState */
public simpleControlGameState(Box playerBox)
{
this.playerBox = playerBox;
}
public void update(float tpf)
{
if(MouseInput.get().isButtonDown(0))
{
playerBox.setLocalTranslation(playerBox.getLocalTranslation().getX(),
playerBox.getLocalTranslation().getY()+0.1f,
playerBox.getLocalTranslation().getZ());
}
}
public void render(float tpf)
{
}
public void cleanup()
{
}
}
simpleSyncObjectManager.java
public abstract class simpleSyncObjectManager implements SyncObjectManager
{
private Box playerBox;
private Node scene;
public void setScene(Node scene)
{
this.scene = scene;
}
public Object create(SynchronizeCreateMessage scm)
{
System.out.println("CREATING PLAYER!");
return BuildPlayer();
}
public boolean remove(SynchronizeRemoveMessage srm, Object obj)
{
System.out.println("DESTROYING PLAYER!");
return removeRemotePlayer((Box)obj);
}
public Box BuildPlayer()
{
playerBox = new Box("PlayerBox",new Vector3f(0,0,0), new Vector3f(2,4,2));
playerBox.setModelBound(new BoundingBox());
playerBox.updateModelBound();
//Shouldn't be here
updateScene();
return playerBox;
}
private boolean removeRemotePlayer(Box player)
{
return player.removeFromParent();
}
public void updateScene()
{
scene.attachChild(playerBox);
scene.updateRenderState();
}
}
Is "DESTROYING PLAYER!" appearing in your console output?
Yea, multiple times actually… which makes it very strange.
This is my output when I try to unregister
Server output
INFO: Child (Text) attached to this node (TextNode)
Sep 26, 2007 1:59:52 AM com.jme.scene.Node <init>
INFO: Node created.
**************** CONNECTED!
Unable to find object: 1 on Server
CREATING PLAYER!
Sep 26, 2007 2:00:02 AM com.jme.scene.Node attachChild
INFO: Child (PlayerBox) attached to this node (RootNode)
Sep 26, 2007 2:00:07 AM com.captiveimagination.jgn.ro.RemoteInvocationListener messageReceived
SEVERE:
java.lang.NullPointerException
at com.captiveimagination.jgn.ro.RemoteInvocationListener.messageReceived(RemoteInvocationListener.java:101)
at com.captiveimagination.jgn.MessageServer.sendToListener(MessageServer.java:463)
at com.captiveimagination.jgn.MessageServer.notifyIncoming(MessageServer.java:490)
at com.captiveimagination.jgn.MessageServer.updateEvents(MessageServer.java:381)
at com.captiveimagination.jgn.clientserver.JGNServer.updateEvents(JGNServer.java:177)
at com.captiveimagination.jgn.clientserver.JGNServer.update(JGNServer.java:162)
DESTROYING PLAYER!
at com.captiveimagination.jgn.UpdatableRunnable.run(JGN.java:402)
at java.lang.Thread.run(Thread.java:619)
Sep 26, 2007 2:00:07 AM com.jme.scene.Node detachChildAt
INFO: Child removed.
Sep 26, 2007 2:00:07 AM com.captiveimagination.jgn.ro.RemoteInvocationListener messageReceived
SEVERE:
java.lang.NullPointerException
at com.captiveimagination.jgn.ro.RemoteInvocationListener.messageReceived(RemoteInvocationListener.java:101)
at com.captiveimagination.jgn.MessageServer.sendToListener(MessageServer.java:463)
at com.captiveimagination.jgn.MessageServer.notifyIncoming(MessageServer.java:490)
at com.captiveimagination.jgn.MessageServer.updateEvents(MessageServer.java:381)
at com.captiveimagination.jgn.clientserver.JGNServer.updateEvents(JGNServer.java:177)
at com.captiveimagination.jgn.clientserver.JGNServer.update(JGNServer.java:162)
at com.captiveimagination.jgn.UpdatableRunnable.run(JGN.java:402)
at java.lang.Thread.run(Thread.java:619)
DESTROYING PLAYER!
Client 1 output
INFO: Child (PlayerBox) attached to this node (RootNode)
Connecting!
Sep 26, 2007 2:00:02 AM com.captiveimagination.jgn.clientserver.JGNClient$1 messageReceived
INFO: JGNClient: playerId was assigned to 0
Connected!
Message Sent: Receipt certifying:0
Sep 26, 2007 2:00:02 AM com.captiveimagination.jgn.clientserver.JGNClient$1 messageReceived
INFO: JGNClient: playerId was assigned to 0
Sep 26, 2007 2:00:02 AM com.captiveimagination.jgn.clientserver.JGNClient connectAndWait
INFO: JGN Connected
SENDING MESSAGE: com.captiveimagination.jgn.ro.RemoteObjectRequestMessage@d19bc8
Message Sent: com.captiveimagination.jgn.ro.RemoteObjectRequestMessage@14a8cd1
Message Received: com.captiveimagination.jgn.ro.RemoteObjectResponseMessage@1630ab9
Message Received: Receipt certifying:1
Message Certified: com.captiveimagination.jgn.ro.RemoteObjectRequestMessage@14a8cd1
SENDING MESSAGE: com.captiveimagination.jgn.ro.RemoteObjectRequestMessage@1551f60
Message Sent: com.captiveimagination.jgn.ro.RemoteObjectRequestMessage@17ee8b8
Message Sent: Receipt certifying:1
Message Sent: com.captiveimagination.jgn.synchronization.message.SynchronizeCreateMessage@e0b6f5
Message Received: com.captiveimagination.jgn.ro.RemoteObjectResponseMessage@10bc49d
Message Received: Receipt certifying:3
Message Received: Receipt certifying:2
Message Certified: com.captiveimagination.jgn.ro.RemoteObjectRequestMessage@17ee8b8
Message Certified: com.captiveimagination.jgn.synchronization.message.SynchronizeCreateMessage@e0b6f5
Next: 2
Message Sent: Receipt certifying:2
SENDING MESSAGE: com.captiveimagination.jgn.ro.RemoteObjectRequestMessage@115273a
Message Sent: com.captiveimagination.jgn.ro.RemoteObjectRequestMessage@1c282a1
Message Sent: com.captiveimagination.jgn.synchronization.message.SynchronizeRemoveMessage@10e3293
Message Received: com.captiveimagination.jgn.ro.RemoteObjectResponseMessage@1f78ef1
Message Received: Receipt certifying:95
Message Received: Receipt certifying:94
Message Received: com.captiveimagination.jgn.synchronization.message.SynchronizeRemoveMessage@1c9a690
Message Certified: com.captiveimagination.jgn.ro.RemoteObjectRequestMessage@1c282a1
Message Certified: com.captiveimagination.jgn.synchronization.message.SynchronizeRemoveMessage@10e3293
DESTROYING PLAYER!
Exception in thread "Thread-7" java.lang.NullPointerException
at com.captiveimagination.jgn.ro.RemoteInvocationListener.messageReceived(RemoteInvocationListener.java:101)
at com.captiveimagination.jgn.MessageServer.sendToListener(MessageServer.java:463)
at com.captiveimagination.jgn.MessageServer.notifyIncoming(MessageServer.java:490)
at com.captiveimagination.jgn.MessageServer.updateEvents(MessageServer.java:381)
at com.captiveimagination.jgn.clientserver.JGNServer.updateEvents(JGNServer.java:177)
at com.captiveimagination.jgn.clientserver.JGNServer.update(JGNServer.java:162)
at com.captiveimagination.jgn.UpdatableRunnable.run(JGN.java:402)
at java.lang.Thread.run(Thread.java:619)
Sep 26, 2007 2:00:07 AM com.jme.scene.Node detachChildAt
INFO: Child removed.
Sep 26, 2007 2:00:07 AM com.captiveimagination.jgn.UpdatableRunnable run
SEVERE: Update thread 10 will die, because..
Sep 26, 2007 2:00:07 AM com.captiveimagination.jgn.UpdatableRunnable run
SEVERE: -->
java.lang.NullPointerException
at com.captiveimagination.jgn.ro.RemoteInvocationListener.messageReceived(RemoteInvocationListener.java:101)
at com.captiveimagination.jgn.MessageServer.sendToListener(MessageServer.java:463)
at com.captiveimagination.jgn.MessageServer.notifyIncoming(MessageServer.java:490)
at com.captiveimagination.jgn.MessageServer.updateEvents(MessageServer.java:381)
at com.captiveimagination.jgn.clientserver.JGNServer.updateEvents(JGNServer.java:177)
at com.captiveimagination.jgn.clientserver.JGNServer.update(JGNServer.java:162)
at com.captiveimagination.jgn.UpdatableRunnable.run(JGN.java:402)
at java.lang.Thread.run(Thread.java:619)
Exception in thread "JGN_Upd10" java.lang.RuntimeException: java.lang.NullPointerException
at com.captiveimagination.jgn.UpdatableRunnable.run(JGN.java:407)
at java.lang.Thread.run(Thread.java:619)
Caused by: java.lang.NullPointerException
at com.captiveimagination.jgn.ro.RemoteInvocationListener.messageReceived(RemoteInvocationListener.java:101)
at com.captiveimagination.jgn.MessageServer.sendToListener(MessageServer.java:463)
at com.captiveimagination.jgn.MessageServer.notifyIncoming(MessageServer.java:490)
at com.captiveimagination.jgn.MessageServer.updateEvents(MessageServer.java:381)
at com.captiveimagination.jgn.clientserver.JGNServer.updateEvents(JGNServer.java:177)
at com.captiveimagination.jgn.clientserver.JGNServer.update(JGNServer.java:162)
at com.captiveimagination.jgn.UpdatableRunnable.run(JGN.java:402)
... 1 more
Message Sent: Receipt certifying:3
Message Sent: Receipt certifying:4
SENDING MESSAGE: com.captiveimagination.jgn.ro.RemoteObjectRequestMessage@1d6776d
Message Sent: com.captiveimagination.jgn.ro.RemoteObjectRequestMessage@13ad085
Message Sent: com.captiveimagination.jgn.synchronization.message.SynchronizeRemoveMessage@4fce71
Message Received: com.captiveimagination.jgn.ro.RemoteObjectResponseMessage@17a8a02
Message Received: Receipt certifying:98
Message Received: Receipt certifying:97
Message Certified: com.captiveimagination.jgn.ro.RemoteObjectRequestMessage@13ad085
Message Certified: com.captiveimagination.jgn.synchronization.message.SynchronizeRemoveMessage@4fce71
Message Sent: Receipt certifying:5
It's cool that the server doesn't crash though (something that did happen in the previous version) when these exceptions are generated!!!
Compliments on that :)
I'll try to get a look at this tomorrow night, out of free time for today though.
I almost feel sorry for bugging you with my problems
Alrighty, i'll patiently await your findings. The whole synchronization system you made is definatly an improvement though. I noticed there is also room for an SyncObjectType parameter within the SynchronizeCreateMessage. I pressume that can be used to identify what kind of synced object needs to be created? Or is it internally in use?
Yes, that's correct. You can define the type of object to create. For most games that's probably enough, but if you wanted to get more elaborate you'd simply extend the SynchronizeCreateMessage with your own custom information that you need to pass to further clarify what and how the object should be created.
It would seem as though your remote invocation was returning a null value (probably something wrong on your end) but it should be handled more gracefully now if you update from SVN.