Captive Imagination
July 31, 2010, 11:47:49 PM *
Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length
News: Check out the teaser site for the first official game by Captive Imagination: http://www.galaxiesbeyond.com
 
   Home   Help Search Calendar Login Register  
Pages: [1]   Go Down
  Print  
Author Topic: Area synchronization  (Read 680 times)
0 Members and 1 Guest are viewing this topic.
Tumaini
JGN Developer
Jr. Member
*****
Offline Offline

Posts: 51


View Profile
« on: October 27, 2009, 05:18:46 PM »

Hello!
I've been using JGN in a jME-based game and it's working very well.
The project started small but is now grown slightly bigger in that all objects in the world are not in the same area.
This way I stumbled upon a problem with synchronization, which is probably due to my lack of insight into how it really works.

The way I have it setup is I'm creating three objects on the server ("the universe").
Then when a client connects only the objects that are near this client are sent (info-wise by message) to the client and created there.
Some of these objects on the server are synchronized, as they are dynamic, and if the object exists on the client I return the object in the syncObjectManager's create-method, which works well.
However, if the object doesn't exist on the client, I return null to signal that the manager shouldn't sync this object (maybe that's not how it works?). This doesn't seem to work as I continue to get "Unable to find object x" messages about those objects.

How do I go about signalling that an object should not be sync'ed on this client?
Logged
darkfrog
Administrator
Inspired Imagination
*****
Offline Offline

Posts: 2650


View Profile
« Reply #1 on: October 28, 2009, 07:43:45 AM »

That is what the proximity stuff was supposed to do, but it was disabled a while back.  There are several possible work-arounds, but if you're interested in contributing to a long-term fix I would suggest looking at the proximity functionality in synchronization and if proximity is 0 the object would not be synced and if it is 1 it would be synced at normal rate (and anywhere in-between would adjust sync-rate accordingly).
Logged
Tumaini
JGN Developer
Jr. Member
*****
Offline Offline

Posts: 51


View Profile
« Reply #2 on: October 28, 2009, 04:02:18 PM »

Thanks!
I was trying to use proximity, unaware that it was disabled and that made me a bit confused.
Now I've looked through the code for the synchronization and I couldn't find a mention of proximity in there, so I added it at a place where I thought it would fit.

It wasn't a difficult fix (although I'm not sure my fix is the most efficient) and it worked well until I moved in the game and found new dynamic objects that needed to be sync'ed (these objects are dynamically created on the client when you get near them). I then got "Unable to find object x" since the clients didn't get a SynchronizeCreateMessage, so I had to add a method in SynchronizationManager that let me find the SyncWrapper id for an object that I wanted to sync to a client, so I could send it with the object creation message.

With these two changes it seems now that my implementation is working as expected.
If you think they are good enough to include in JGN, feel free to use them (that proximity method is great, so it would be nice to be able to use it, of course). Here they are:

In SyncWrapper.java
Code:
protected void update(SynchronizationManager manager, JGNServer server, GraphicalController controller) {
long updateRate = rate;
for(JGNConnection conn : server.getConnections()) {
if(controller.proximity(getObject(), conn.getPlayerId()) > 0f) {
updateRate = (long) (rate/controller.proximity(getObject(), conn.getPlayerId()));
if (lastUpdate + updateRate < System.nanoTime()) {
if (server.getConnections().length > 0) {
SynchronizeMessage message = controller.createSynchronizationMessage(getObject());
message.setSyncManagerId(manager.getId());
message.setSyncObjectId(getId());
server.sendToPlayer(message, conn.getPlayerId());
}

lastUpdate = System.nanoTime();
}
}
}
}

protected void update(SynchronizationManager manager, JGNClient client, GraphicalController controller) {
long updateRate = rate;
for(JGNConnection conn : client.getConnections()) {
if(controller.proximity(getObject(), conn.getPlayerId()) > 0f) {
updateRate = (long) (rate/controller.proximity(getObject(), conn.getPlayerId()));
if (lastUpdate + updateRate < System.nanoTime()) {
if(client.getConnections().length > 0) {
SynchronizeMessage message = controller.createSynchronizationMessage(getObject());
message.setSyncManagerId(manager.getId());
message.setSyncObjectId(getId());
client.sendToPlayer(message, conn.getPlayerId());
}

lastUpdate = System.nanoTime();
}
}
}
}

In SynchronizationManager.java:
Code:
public short findSyncObjectId(Object obj) {
SyncWrapper sync = findWrapper(obj);
if(sync != null) {
return sync.getId();
}
return -1;
}

However, I did get this old error when I tried connecting two clients, but only after at least one of them disconnected:
Quote
2009-okt-28 21:51:19 com.captiveimagination.jgn.MessageClient setStatus
VARNING:
java.lang.Exception: MC 599948211598647296 state equal. PLEASE SEND LOG to JGN, if this appears ...(DISCONNECTED-->DISCONNECTED). Thanks
   at com.captiveimagination.jgn.MessageClient.setStatus(MessageClient.java:192)
   at com.captiveimagination.jgn.NIOMessageServer.disconnectInternal(NIOMessageServer.java:109)
   at com.captiveimagination.jgn.NIOMessageServer.updateTraffic(NIOMessageServer.java:169)
   at com.captiveimagination.jgn.clientserver.JGNServer.updateTraffic(JGNServer.java:171)
   at com.captiveimagination.jgn.clientserver.JGNServer.update(JGNServer.java:160)
   at com.captiveimagination.jgn.UpdatableRunnable.run(JGN.java:437)
   at java.lang.Thread.run(Unknown Source)
Logged
darkfrog
Administrator
Inspired Imagination
*****
Offline Offline

Posts: 2650


View Profile
« Reply #3 on: October 29, 2009, 10:53:15 AM »

can you provide that as a patch?
Logged
Tumaini
JGN Developer
Jr. Member
*****
Offline Offline

Posts: 51


View Profile
« Reply #4 on: October 30, 2009, 09:56:24 AM »

Ok, here's the patch!

Code:
Index: src/com/captiveimagination/jgn/synchronization/SynchronizationManager.java
===================================================================
--- src/com/captiveimagination/jgn/synchronization/SynchronizationManager.java (revision 1276)
+++ src/com/captiveimagination/jgn/synchronization/SynchronizationManager.java (working copy)
@@ -306,6 +306,14 @@
  return wrapper;
  }
 
+ public short findSyncObjectId(Object obj) {
+ SyncWrapper sync = findWrapper(obj);
+ if(sync != null) {
+ return sync.getId();
+ }
+ return -1;
+ }
+
  public void addSyncObjectManager(SyncObjectManager som) {
  objectManagers.add(som);
  }
@@ -406,6 +414,10 @@
  if (controller.validateMessage(m, obj)) {
  // Successfully validated synchronization message
  controller.applySynchronizationMessage(m, obj);
+ // Check to see if we should send the message on
+ if(server != null) {
+ wrapper.update(this, server, controller);
+ }
  } else {
  // Failed validation, so we ignore the message and send back our own
  m = controller.createSynchronizationMessage(obj);
Index: src/com/captiveimagination/jgn/synchronization/SyncWrapper.java
===================================================================
--- src/com/captiveimagination/jgn/synchronization/SyncWrapper.java (revision 1276)
+++ src/com/captiveimagination/jgn/synchronization/SyncWrapper.java (working copy)
@@ -33,7 +33,10 @@
  */
 package com.captiveimagination.jgn.synchronization;
 
+import java.util.HashMap;
+
 import com.captiveimagination.jgn.clientserver.JGNClient;
+import com.captiveimagination.jgn.clientserver.JGNConnection;
 import com.captiveimagination.jgn.clientserver.JGNServer;
 import com.captiveimagination.jgn.synchronization.message.SynchronizeCreateMessage;
 import com.captiveimagination.jgn.synchronization.message.SynchronizeMessage;
@@ -48,6 +51,7 @@
  private short ownerPlayerId;
 
  private long lastUpdate;
+ private HashMap<Short, Long> lastServerUpdate = new HashMap<Short, Long>();
 
  public SyncWrapper(Object object, long rate, SynchronizeCreateMessage createMessage, short ownerPlayerId) {
  if (object == null) throw new RuntimeException("Object is null: " + id);
@@ -92,25 +96,34 @@
  }
 
  protected void update(SynchronizationManager manager, JGNServer server, GraphicalController controller) {
- if (lastUpdate + rate < System.nanoTime()) {
- if (server.getConnections().length > 0) {
- SynchronizeMessage message = controller.createSynchronizationMessage(getObject());
- message.setSyncManagerId(manager.getId());
- message.setSyncObjectId(getId());
- server.sendToAll(message);
+ long updateRate = rate;
+ SynchronizeMessage message = controller.createSynchronizationMessage(getObject());
+ message.setSyncManagerId(manager.getId());
+ message.setSyncObjectId(getId());
+ for(JGNConnection conn : server.getConnections()) {
+ if(controller.proximity(getObject(), conn.getPlayerId()) > 0f) {
+ updateRate = (long) (rate/controller.proximity(getObject(), conn.getPlayerId()));
+ if(!lastServerUpdate.containsKey(conn.getPlayerId())) {
+ lastServerUpdate.put(conn.getPlayerId(), (long)0);
+ }
+ if (lastServerUpdate.get(conn.getPlayerId()) + updateRate < System.nanoTime()) {
+ if (server.getConnections().length > 0) {
+ server.sendToPlayer(message, conn.getPlayerId());
+ }
+
+ lastServerUpdate.put(conn.getPlayerId(), System.nanoTime());
+ }
  }
-
- lastUpdate = System.nanoTime();
  }
  }
 
  protected void update(SynchronizationManager manager, JGNClient client, GraphicalController controller) {
- if (lastUpdate + rate < System.nanoTime()) {
- SynchronizeMessage message = controller.createSynchronizationMessage(getObject());
- message.setSyncManagerId(manager.getId());
- message.setSyncObjectId(getId());
- client.broadcast(message);
-
+ SynchronizeMessage message = controller.createSynchronizationMessage(getObject());
+ message.setSyncManagerId(manager.getId());
+ message.setSyncObjectId(getId());
+
+ if(lastUpdate + rate < System.nanoTime()) {
+ client.sendToServer(message);
  lastUpdate = System.nanoTime();
  }
  }


I couldn't attach it to this post, as I got this error:
Code:
Cannot access attachments upload path!

EDIT: I entered my latest suggested patch above.
This sends sync messages from a client only to the server and then the server runs its update method on that wrapper to relay the sync message to the clients, depending on the outcome of proximity.
I also had to add a new HashMap to store the last update time for each object on the server, or some objects would never get updated (or some would get updated too frequently). There needs to be added some function for regularly checking this HashMap to remove entries for clients that have disconnected though, but besides that it's working well in the tests I've done.
« Last Edit: November 03, 2009, 06:19:38 AM by Tumaini » Logged
darkfrog
Administrator
Inspired Imagination
*****
Offline Offline

Posts: 2650


View Profile
« Reply #5 on: November 05, 2009, 07:53:31 AM »

I've applied the patch to the repository.

Thanks!
Logged
Pages: [1]   Go Up
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.11 | SMF © 2006-2009, Simple Machines LLC Valid XHTML 1.0! Valid CSS!
Page created in 0.188 seconds with 22 queries.