diff --git a/src/main/java/com/threerings/presents/peer/data/NodeObject.java b/src/main/java/com/threerings/presents/peer/data/NodeObject.java index c85514240..47ab29afc 100644 --- a/src/main/java/com/threerings/presents/peer/data/NodeObject.java +++ b/src/main/java/com/threerings/presents/peer/data/NodeObject.java @@ -53,6 +53,10 @@ public class NodeObject extends DObject @Generated(value={"com.threerings.presents.tools.GenDObjectTask"}) public static final String CLIENTS = "clients"; + /** The field name of the nodeProxies field. */ + @Generated(value={"com.threerings.presents.tools.GenDObjectTask"}) + public static final String NODE_PROXIES = "nodeProxies"; + /** The field name of the locks field. */ @Generated(value={"com.threerings.presents.tools.GenDObjectTask"}) public static final String LOCKS = "locks"; @@ -152,6 +156,9 @@ public class NodeObject extends DObject /** Contains information on all clients connected to this node. */ public DSet clients = new DSet(); + /** Contains information on all node objects proxied by this node. */ + public DSet nodeProxies = new DSet(); + /** The set of locks held by this node. */ public DSet locks = new DSet(); @@ -267,6 +274,57 @@ public class NodeObject extends DObject this.clients = clone; } + /** + * Requests that the specified entry be added to the + * nodeProxies set. The set will not change until the event is + * actually propagated through the system. + */ + @Generated(value={"com.threerings.presents.tools.GenDObjectTask"}) + public void addToNodeProxies (NodeProxyInfo elem) + { + requestEntryAdd(NODE_PROXIES, nodeProxies, elem); + } + + /** + * Requests that the entry matching the supplied key be removed from + * the nodeProxies set. The set will not change until the + * event is actually propagated through the system. + */ + @Generated(value={"com.threerings.presents.tools.GenDObjectTask"}) + public void removeFromNodeProxies (Comparable key) + { + requestEntryRemove(NODE_PROXIES, nodeProxies, key); + } + + /** + * Requests that the specified entry be updated in the + * nodeProxies set. The set will not change until the event is + * actually propagated through the system. + */ + @Generated(value={"com.threerings.presents.tools.GenDObjectTask"}) + public void updateNodeProxies (NodeProxyInfo elem) + { + requestEntryUpdate(NODE_PROXIES, nodeProxies, elem); + } + + /** + * Requests that the nodeProxies field be set to the + * specified value. Generally one only adds, updates and removes + * entries of a distributed set, but certain situations call for a + * complete replacement of the set value. The local value will be + * updated immediately and an event will be propagated through the + * system to notify all listeners that the attribute did + * change. Proxied copies of this object (on clients) will apply the + * value change when they received the attribute changed notification. + */ + @Generated(value={"com.threerings.presents.tools.GenDObjectTask"}) + public void setNodeProxies (DSet value) + { + requestAttributeChange(NODE_PROXIES, value, this.nodeProxies); + DSet clone = (value == null) ? null : value.clone(); + this.nodeProxies = clone; + } + /** * Requests that the specified entry be added to the * locks set. The set will not change until the event is diff --git a/src/main/java/com/threerings/presents/peer/data/NodeProxyInfo.java b/src/main/java/com/threerings/presents/peer/data/NodeProxyInfo.java new file mode 100644 index 000000000..2b2a5f11d --- /dev/null +++ b/src/main/java/com/threerings/presents/peer/data/NodeProxyInfo.java @@ -0,0 +1,45 @@ +// +// $Id$ +// +// Narya library - tools for developing networked games +// Copyright (C) 2002-2011 Three Rings Design, Inc., All Rights Reserved +// http://code.google.com/p/narya/ +// +// This library is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation; either version 2.1 of the License, or +// (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package com.threerings.presents.peer.data; + +import com.threerings.io.SimpleStreamableObject; + +import com.threerings.presents.dobj.DSet; + +/** + * Contains information about a proxied {@link NodeObject}. + */ +public class NodeProxyInfo extends SimpleStreamableObject + implements DSet.Entry +{ + /** The name of the node. */ + public String nodeName; + + /** The oid of the proxied object. */ + public int nodeOid; + + // documentation inherited from interface DSet.Entry + public Comparable getKey () + { + return nodeName; + } +} diff --git a/src/main/java/com/threerings/presents/peer/server/PeerManager.java b/src/main/java/com/threerings/presents/peer/server/PeerManager.java index 770d2d96c..d18bae748 100644 --- a/src/main/java/com/threerings/presents/peer/server/PeerManager.java +++ b/src/main/java/com/threerings/presents/peer/server/PeerManager.java @@ -338,6 +338,14 @@ public abstract class PeerManager didInit(); } + /** + * Sets the strategy used to determine relationships between peers. + */ + public void setRelationshipStrategy (RelationshipStrategy strategy) + { + _relationshipStrategy = strategy; + } + /** * Returns true if the supplied peer credentials match our shared secret. */ @@ -568,7 +576,7 @@ public abstract class PeerManager public void proxyRemoteObject ( final DObjectAddress remote, final ResultListener listener) { - if (remote.nodeName.equals(_nodeName)) { + if (remote.nodeName.equals(_nodeName)) { _omgr.postRunnable(new Runnable() { public void run () { listener.requestCompleted(remote.oid); @@ -1066,6 +1074,7 @@ public abstract class PeerManager } @Override public void handleSuccess () { + _relationshipStrategy.determineRelationships(_nodes, _nodeName); for (NodeRecord record : _nodes) { if (record.nodeName.equals(_nodeName)) { continue; @@ -1594,6 +1603,9 @@ public abstract class PeerManager } }; + /** The strategy used to determine this peer's relationship to others. */ + protected RelationshipStrategy _relationshipStrategy = RelationshipStrategy.FULLY_CONNECTED; + protected String _nodeName, _sharedSecret; protected NodeRecord _self; protected NodeObject _nodeobj; diff --git a/src/main/java/com/threerings/presents/peer/server/PeerNode.java b/src/main/java/com/threerings/presents/peer/server/PeerNode.java index 3f9e86bb6..6f9f335ea 100644 --- a/src/main/java/com/threerings/presents/peer/server/PeerNode.java +++ b/src/main/java/com/threerings/presents/peer/server/PeerNode.java @@ -47,6 +47,7 @@ import com.threerings.presents.peer.data.ClientInfo; import com.threerings.presents.peer.data.NodeObject; import com.threerings.presents.peer.net.PeerBootstrapData; import com.threerings.presents.peer.server.persist.NodeRecord; +import com.threerings.presents.peer.server.persist.NodeRecord.Relationship; import com.threerings.presents.server.PresentsDObjectMgr; import com.threerings.presents.server.net.PresentsConnectionManager; import com.threerings.presents.server.net.ServerCommunicator; @@ -128,16 +129,17 @@ public class PeerNode public void refresh (NodeRecord record) { - // if the hostname of this node changed, kill our existing client and connect anew - if (!record.hostName.equals(_record.hostName) && _client.isActive()) { + // if the hostname of this node changed or we have an indirect relationship, log off + if ((!record.hostName.equals(_record.hostName) || + record.relationship == Relationship.INDIRECT) && _client.isActive()) { _client.logoff(false); } // use our new record _record = record; - // if our client is active, we're groovy - if (_client.isActive()) { + // if our client is active or needn't be, we're groovy + if (_client.isActive() || record.relationship == Relationship.INDIRECT) { return; } diff --git a/src/main/java/com/threerings/presents/peer/server/RelationshipStrategy.java b/src/main/java/com/threerings/presents/peer/server/RelationshipStrategy.java new file mode 100644 index 000000000..d7d34af97 --- /dev/null +++ b/src/main/java/com/threerings/presents/peer/server/RelationshipStrategy.java @@ -0,0 +1,65 @@ +// +// $Id$ +// +// Narya library - tools for developing networked games +// Copyright (C) 2002-2011 Three Rings Design, Inc., All Rights Reserved +// http://code.google.com/p/narya/ +// +// This library is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation; either version 2.1 of the License, or +// (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package com.threerings.presents.peer.server; + +import java.util.List; + +import com.threerings.presents.peer.server.persist.NodeRecord; +import com.threerings.presents.peer.server.persist.NodeRecord.Relationship; + +/** + * Used to determine the relationships between the local node and all others. + */ +public interface RelationshipStrategy +{ + /** A strategy in which all nodes are directly connected to one another. */ + public static final RelationshipStrategy FULLY_CONNECTED = new RelationshipStrategy() { + public void determineRelationships (List nodes, String localNode) { + for (NodeRecord node : nodes) { + node.relationship = Relationship.DIRECT_EQUAL; + } + } + }; + + /** A strategy in which all child nodes connect to a single parent. */ + public static final RelationshipStrategy SINGLE_PARENT = new RelationshipStrategy() { + public void determineRelationships (List nodes, String localNode) { + String parentNode = localNode; + for (NodeRecord node : nodes) { + if (node.nodeName.compareTo(parentNode) < 0) { + parentNode = node.nodeName; + } + } + boolean amParent = localNode.equals(parentNode); + for (NodeRecord node : nodes) { + node.relationship = amParent ? Relationship.CHILD : + (node.nodeName.equals(parentNode) ? + Relationship.PARENT : Relationship.INDIRECT); + } + } + }; + + /** + * Determines the relationships between this node and all others. + */ + public void determineRelationships (List nodes, String localNode); +} diff --git a/src/main/java/com/threerings/presents/peer/server/persist/NodeRecord.java b/src/main/java/com/threerings/presents/peer/server/persist/NodeRecord.java index 572fd9164..ba20f8cd9 100644 --- a/src/main/java/com/threerings/presents/peer/server/persist/NodeRecord.java +++ b/src/main/java/com/threerings/presents/peer/server/persist/NodeRecord.java @@ -38,6 +38,9 @@ import com.samskivert.depot.expression.ColumnExp; @Entity(name="NODES") public class NodeRecord extends PersistentRecord { + /** The types of relationships between nodes. */ + public enum Relationship { PARENT, CHILD, DIRECT_EQUAL, INDIRECT }; + // AUTO-GENERATED: FIELDS START public static final Class _R = NodeRecord.class; public static final ColumnExp NODE_NAME = colexp(_R, "nodeName"); @@ -72,6 +75,9 @@ public class NodeRecord extends PersistentRecord @Column(name="LAST_UPDATED") public Timestamp lastUpdated; + /** The relationship of the local node to the node described. */ + public transient Relationship relationship; + /** Used to create a blank instance when loading from the database. */ public NodeRecord () {