I've been working on fixing regional sync'ing, using the proximity method in GraphicalController.
The updating bit has already been commited, but the create/remove bit is bigger and therefore I provide a patch here, so that anyone can test it and see if it's working as intended in their program, before I commit it to the repository.
With this patch objects that return 0 from proximity for a given player id are not updated to that player and if it a create message had previously been sent (when proximity returned a value > 0) a remove message is sent.
Also, if proximity returns 0 already when a client connects, no create message is sent.
If proximity later returns a value > 0, a create message is sent to the player in question.
The proximity method should return a value between 0 and 1, inclusive.
0 means the object is not visible to (or should not be updated for) the player with that player id.
1 means it is visible and should be updated at regular speed.
A value between 0 and 1, exclusive, means it is visible and should be updated at a reduced rate (closer to 0 means more time between updates).
Please let me know in this thread if you have any problems or if your tests were successful as well.
Index: src/com/captiveimagination/jgn/synchronization/SynchronizationManager.java
===================================================================
--- src/com/captiveimagination/jgn/synchronization/SynchronizationManager.java (revision 1280)
+++ src/com/captiveimagination/jgn/synchronization/SynchronizationManager.java (working copy)
@@ -164,9 +164,15 @@
// Send create message onward
if (client != null) {
- client.broadcast(wrapper.getCreateMessage());
+ client.sendToServer(wrapper.getCreateMessage());
} else {
- server.sendToAll(wrapper.getCreateMessage());
+ for(JGNConnection conn : server.getConnections()) {
+ if(controller.proximity(wrapper.getObject(), conn.getPlayerId()) > 0f) {
+ System.out.println("Sending create for "+wrapper.getId());
+ wrapper.setLastAction(conn.getPlayerId(), (short)1);
+ server.sendToPlayer(wrapper.getCreateMessage(), conn.getPlayerId());
+ }
+ }
}
// Add to manager to be updated
@@ -202,7 +208,16 @@
client.sendToServer(release);
}
} else {
- server.sendToAll(remove);
+ //Check which players are near this object
+ for(JGNConnection conn : server.getConnections()) {
+ if(controller.proximity(wrapper.getObject(), conn.getPlayerId()) > 0f) {
+ if(wrapper.hasLastAction(conn.getPlayerId())) {
+ if(wrapper.getLastAction(conn.getPlayerId()) != 2) {
+ server.sendToPlayer(remove, conn.getPlayerId());
+ }
+ }
+ }
+ }
// Release id
serverReleaseId(wrapper.getId());
@@ -335,6 +350,16 @@
SyncWrapper wrapper = new SyncWrapper(obj, 0, message, message.getPlayerId());
wrapper.setId(message.getSyncObjectId());
passive.add(wrapper);
+ if(server != null) {
+ // Forward create message to clients
+ for(JGNConnection conn : server.getConnections()) {
+ if(controller.proximity(wrapper.getObject(), conn.getPlayerId()) > 0f && conn.getPlayerId() != wrapper.getOwnerPlayerId()) {
+ System.out.println("Sending create for "+wrapper.getId());
+ wrapper.setLastAction(conn.getPlayerId(), (short)1);
+ server.sendToPlayer(wrapper.getCreateMessage(), conn.getPlayerId());
+ }
+ }
+ }
return obj;
}
}
@@ -459,25 +484,76 @@
public void connected(JGNConnection connection) {
// Client connected to server - send creation messages to new connection
for (SyncWrapper wrapper : queue) {
- connection.sendMessage(wrapper.getCreateMessage());
+ if(controller.proximity(wrapper.getObject(), connection.getPlayerId()) > 0f) {
+ if(wrapper.hasLastAction(connection.getPlayerId())) {
+ if(wrapper.getLastAction(connection.getPlayerId()) != 1) {
+ connection.sendMessage(wrapper.getCreateMessage());
+ wrapper.setLastAction(connection.getPlayerId(), (short)1);
+ System.out.println("Sending create for "+wrapper.getId());
+ }
+ }
+ else {
+ connection.sendMessage(wrapper.getCreateMessage());
+ wrapper.setLastAction(connection.getPlayerId(), (short)1);
+ System.out.println("Sending create for "+wrapper.getId());
+ }
+ }
}
for (SyncWrapper wrapper : disabled) {
- connection.sendMessage(wrapper.getCreateMessage());
+ if(controller.proximity(wrapper.getObject(), connection.getPlayerId()) > 0f) {
+ if(wrapper.hasLastAction(connection.getPlayerId())) {
+ if(wrapper.getLastAction(connection.getPlayerId()) != 1) {
+ connection.sendMessage(wrapper.getCreateMessage());
+ wrapper.setLastAction(connection.getPlayerId(), (short)1);
+ System.out.println("Sending create for "+wrapper.getId());
+ }
+ }
+ else {
+ connection.sendMessage(wrapper.getCreateMessage());
+ wrapper.setLastAction(connection.getPlayerId(), (short)1);
+ System.out.println("Sending create for "+wrapper.getId());
+ }
+ }
}
for (SyncWrapper wrapper : passive) {
- connection.sendMessage(wrapper.getCreateMessage());
+ if(controller.proximity(wrapper.getObject(), connection.getPlayerId()) > 0f) {
+ if(wrapper.hasLastAction(connection.getPlayerId())) {
+ if(wrapper.getLastAction(connection.getPlayerId()) != 1) {
+ connection.sendMessage(wrapper.getCreateMessage());
+ wrapper.setLastAction(connection.getPlayerId(), (short)1);
+ System.out.println("Sending create for "+wrapper.getId()+" to "+connection.getPlayerId());
+ }
+ }
+ else {
+ connection.sendMessage(wrapper.getCreateMessage());
+ wrapper.setLastAction(connection.getPlayerId(), (short)1);
+ System.out.println("Sending create for "+wrapper.getId()+" to "+connection.getPlayerId());
+ }
+ }
}
}
public void disconnected(JGNConnection connection) {
// Remove all connections associated with this player
- short playerId = connection.getPlayerId();
for (SyncWrapper wrapper : passive) {
- if (wrapper.getOwnerPlayerId() == playerId) {
+ if (wrapper.getOwnerPlayerId() == connection.getPlayerId()) {
SynchronizeRemoveMessage removeMessage = new SynchronizeRemoveMessage();
removeMessage.setSyncObjectId(wrapper.getId());
removeQueue.add(removeMessage);
}
}
+ //Remove values for lastServerUpdate and lastAction for this connection
+ for (SyncWrapper wrapper : passive) {
+ wrapper.removeLastServerUpdateKey(connection.getPlayerId());
+ wrapper.removeLastActionKey(connection.getPlayerId());
+ }
+ for (SyncWrapper wrapper : queue) {
+ wrapper.removeLastServerUpdateKey(connection.getPlayerId());
+ wrapper.removeLastActionKey(connection.getPlayerId());
+ }
+ for (SyncWrapper wrapper : disabled) {
+ wrapper.removeLastServerUpdateKey(connection.getPlayerId());
+ wrapper.removeLastActionKey(connection.getPlayerId());
+ }
}
}
\ No newline at end of file
Index: src/com/captiveimagination/jgn/synchronization/SyncWrapper.java
===================================================================
--- src/com/captiveimagination/jgn/synchronization/SyncWrapper.java (revision 1279)
+++ src/com/captiveimagination/jgn/synchronization/SyncWrapper.java (working copy)
@@ -40,6 +40,7 @@
import com.captiveimagination.jgn.clientserver.JGNServer;
import com.captiveimagination.jgn.synchronization.message.SynchronizeCreateMessage;
import com.captiveimagination.jgn.synchronization.message.SynchronizeMessage;
+import com.captiveimagination.jgn.synchronization.message.SynchronizeRemoveMessage;
class SyncWrapper {
private short id;
@@ -50,8 +51,19 @@
private SynchronizeCreateMessage createMessage;
private short ownerPlayerId;
+ /**
+ * Long for storing when the last update to the server was, if this is on the client
+ */
private long lastUpdate;
+ /**
+ * Map for storing when the last update to a connected client was, if this is on the server
+ */
private HashMap<Short, Long> lastServerUpdate = new HashMap<Short, Long>();
+ /**
+ * Map for storing the last action of create/remove for a connected client
+ * 0 = nothing; 1 = sent create message; 2 = sent remove message
+ */
+ private HashMap<Short, Short> lastAction = new HashMap<Short, Short>();
public SyncWrapper(Object object, long rate, SynchronizeCreateMessage createMessage, short ownerPlayerId) {
if (object == null) throw new RuntimeException("Object is null: " + id);
@@ -95,6 +107,36 @@
return ownerPlayerId;
}
+ /**
+ * Removes this key from the lastServerUpdate map
+ * @param playerId the key to remove
+ * @return the object referenced by this key or null if the key didn't exist
+ */
+ public Object removeLastServerUpdateKey(short playerId) {
+ return lastServerUpdate.remove(playerId);
+ }
+
+ /**
+ * Removes this key from the lastAction map
+ * @param playerId the key to remove
+ * @return the object referenced by this key or null if the key didn't exist
+ */
+ public Object removeLastActionKey(short playerId) {
+ return lastAction.remove(playerId);
+ }
+
+ public boolean hasLastAction(short playerId) {
+ return lastAction.containsKey(playerId);
+ }
+
+ public short getLastAction(short playerId) {
+ return lastAction.get(playerId);
+ }
+
+ public void setLastAction(short playerId, short value) {
+ lastAction.put(playerId, value);
+ }
+
protected void update(SynchronizationManager manager, JGNServer server, GraphicalController controller) {
long updateRate = rate;
SynchronizeMessage message = controller.createSynchronizationMessage(getObject());
@@ -102,6 +144,23 @@
message.setSyncObjectId(getId());
for(JGNConnection conn : server.getConnections()) {
if(controller.proximity(getObject(), conn.getPlayerId()) > 0f && conn.getPlayerId() != getOwnerPlayerId()) {
+ //Check if sent create message
+ if(lastAction.containsKey(conn.getPlayerId())) {
+ if(lastAction.get(conn.getPlayerId()) != 1) { //No create message sent
+ System.out.println("Sending create for "+getId());
+ lastAction.put(conn.getPlayerId(), (short)1);
+ getCreateMessage().setSyncObjectId(getId());
+ getCreateMessage().setSyncManagerId(manager.getId());
+ server.sendToPlayer(getCreateMessage(), conn.getPlayerId());
+ }
+ }
+ else {
+ System.out.println("Sending create for "+getId());
+ lastAction.put(conn.getPlayerId(), (short)1);
+ getCreateMessage().setSyncObjectId(getId());
+ getCreateMessage().setSyncManagerId(manager.getId());
+ server.sendToPlayer(getCreateMessage(), conn.getPlayerId());
+ }
updateRate = (long) (rate/controller.proximity(getObject(), conn.getPlayerId()));
if(!lastServerUpdate.containsKey(conn.getPlayerId())) {
lastServerUpdate.put(conn.getPlayerId(), (long)0);
@@ -114,6 +173,22 @@
lastServerUpdate.put(conn.getPlayerId(), System.nanoTime());
}
}
+ else if(controller.proximity(getObject(), conn.getPlayerId()) == 0f){
+ //Check if sent remove message
+ if(lastAction.containsKey(conn.getPlayerId())) {
+ if(lastAction.get(conn.getPlayerId()) != 2) { //No remove message sent
+ System.out.println("Sending remove for "+getId());
+ lastAction.put(conn.getPlayerId(), (short)2);
+ SynchronizeRemoveMessage remove = new SynchronizeRemoveMessage();
+ remove.setSyncObjectId(getId());
+ remove.setSyncManagerId(manager.getId());
+ server.sendToPlayer(remove, conn.getPlayerId());
+ }
+ }
+ else {
+ lastAction.put(conn.getPlayerId(), (short)2);
+ }
+ }
}
}
EDIT: Put in code tags.
EDIT2: Fixed bug with unregister that was sending not using proximity.