From adeb3054d4f8b6bc73b806cbaff32d5249d7f6df Mon Sep 17 00:00:00 2001 From: Mike Thomas Date: Thu, 27 May 2010 20:30:53 +0000 Subject: [PATCH] Make the zone handling appropriately peer-aware. It's worth noting that due to adding moveRequiresServerSwitch() to ZoneService, this will break some things which use this. git-svn-id: svn+ssh://src.earth.threerings.net/vilya/trunk@913 c613c5cb-e716-0410-b11b-feb51c14d237 --- .../whirled/zone/client/ZoneDirector.as | 44 +++- .../client/ZoneService_ZoneMoveListener.as | 3 + .../data/ZoneMarshaller_ZoneMoveMarshaller.as | 14 +- .../whirled/zone/client/ZoneDirector.java | 47 ++++- .../whirled/zone/client/ZoneService.java | 6 + .../whirled/zone/data/ZoneMarshaller.java | 24 ++- .../whirled/zone/peer/data/HostedZone.java | 38 ++++ .../zone/peer/server/PeeredZoneManager.java | 11 + .../zone/peer/server/PeeredZoneRegistry.java | 193 ++++++++++++++++++ .../zone/peer/server/ZoneNodeObject.java | 73 +++++++ .../zone/peer/server/ZonePeerManager.java | 80 ++++++++ 11 files changed, 519 insertions(+), 14 deletions(-) create mode 100644 src/java/com/threerings/whirled/zone/peer/data/HostedZone.java create mode 100644 src/java/com/threerings/whirled/zone/peer/server/PeeredZoneManager.java create mode 100644 src/java/com/threerings/whirled/zone/peer/server/PeeredZoneRegistry.java create mode 100644 src/java/com/threerings/whirled/zone/peer/server/ZoneNodeObject.java create mode 100644 src/java/com/threerings/whirled/zone/peer/server/ZonePeerManager.java diff --git a/src/as/com/threerings/whirled/zone/client/ZoneDirector.as b/src/as/com/threerings/whirled/zone/client/ZoneDirector.as index 4ff3a951..0aeb72c8 100644 --- a/src/as/com/threerings/whirled/zone/client/ZoneDirector.as +++ b/src/as/com/threerings/whirled/zone/client/ZoneDirector.as @@ -7,6 +7,7 @@ import com.threerings.util.ResultListener; import com.threerings.presents.client.BasicDirector; import com.threerings.presents.client.Client; import com.threerings.presents.client.ClientEvent; +import com.threerings.presents.client.ConfirmAdapter; import com.threerings.crowd.data.PlaceConfig; @@ -97,22 +98,31 @@ public class ZoneDirector extends BasicDirector return false; } + _pendingZoneId = zoneId; + + sendMoveRequest(); + + return true; + } + + protected function sendMoveRequest () :void + { // let our zone observers know that we're attempting to switch zones - notifyObservers(zoneId); + notifyObservers(_pendingZoneId); // check the version of our cached copy of the scene to which we're requesting to move; if // we were unable to load it, assume a cached version of zero var sceneVers :int = 0; + var sceneId :int = -1; var pendingModel :SceneModel = _scdir.getPendingModel(); if (pendingModel != null) { sceneVers = pendingModel.version; } // issue a moveTo request - log.info("Issuing zoned moveTo(" + ZoneUtil.toString(zoneId) + + log.info("Issuing zoned moveTo(" + ZoneUtil.toString(_pendingZoneId) + ", " + sceneId + ", " + sceneVers + ")."); - _zservice.moveTo(zoneId, sceneId, sceneVers, this); - return true; + _zservice.moveTo(_pendingZoneId, sceneId, sceneVers, this); } override protected function fetchServices (client :Client) :void @@ -141,6 +151,9 @@ public class ZoneDirector extends BasicDirector // keep track of the summary _summary = summary; + // We're not heading there any more. + _pendingZoneId = -1; + // pass the rest off to the standard scene transition code _scdir.moveSucceeded(placeId, config); @@ -156,6 +169,9 @@ public class ZoneDirector extends BasicDirector // keep track of the summary _summary = summary; + // We're not heading there any more. + _pendingZoneId = -1; + // pass the rest off to the standard scene transition code _scdir.moveSucceededWithUpdates(placeId, config, updates); @@ -170,6 +186,9 @@ public class ZoneDirector extends BasicDirector // keep track of the summary _summary = summary; + // We're not heading there any more. + _pendingZoneId = -1; + // pass the rest off to the standard scene transition code _scdir.moveSucceededWithScene(placeId, config, model); @@ -177,6 +196,17 @@ public class ZoneDirector extends BasicDirector notifyObservers(summary); } + // from interface ZoneService_ZoneMoveListener + public function moveRequiresServerSwitch (hostname :String, ports :TypedArray) :void + { + log.info("Zone switch requires server switch", "host", hostname, "ports", ports); + // ship on over to the other server + _wCtx.getClient().moveToServer(hostname, ports, new ConfirmAdapter( + function () :void { // succeeded + sendMoveRequest(); + }, requestFailed)); + } + // from interface ZoneService.ZoneMoveListener public function requestFailed (reason :String) :void { @@ -215,6 +245,9 @@ public class ZoneDirector extends BasicDirector return; // if we're currently somewhere, just stay there } + // Not gonna get there, so clear it. + _pendingZoneId = -1; + // otherwise if we were previously in a zone/scene, try going back there if (_previousZoneId != -1) { moveTo(_previousZoneId, previousSceneId); @@ -263,5 +296,8 @@ public class ZoneDirector extends BasicDirector /** Our previous zone id. */ protected var _previousZoneId :int = -1; + + /** Where we're headed. */ + protected var _pendingZoneId :int = -1; } } diff --git a/src/as/com/threerings/whirled/zone/client/ZoneService_ZoneMoveListener.as b/src/as/com/threerings/whirled/zone/client/ZoneService_ZoneMoveListener.as index c69f692f..695ac6b9 100644 --- a/src/as/com/threerings/whirled/zone/client/ZoneService_ZoneMoveListener.as +++ b/src/as/com/threerings/whirled/zone/client/ZoneService_ZoneMoveListener.as @@ -33,6 +33,9 @@ import com.threerings.whirled.zone.data.ZoneSummary; public interface ZoneService_ZoneMoveListener extends InvocationService_InvocationListener { + // from Java ZoneService_ZoneMoveListener + function moveRequiresServerSwitch (arg1 :String, arg2 :TypedArray /* of int */) :void + // from Java ZoneService_ZoneMoveListener function moveSucceeded (arg1 :int, arg2 :PlaceConfig, arg3 :ZoneSummary) :void diff --git a/src/as/com/threerings/whirled/zone/data/ZoneMarshaller_ZoneMoveMarshaller.as b/src/as/com/threerings/whirled/zone/data/ZoneMarshaller_ZoneMoveMarshaller.as index bdff60dc..e3bb8111 100644 --- a/src/as/com/threerings/whirled/zone/data/ZoneMarshaller_ZoneMoveMarshaller.as +++ b/src/as/com/threerings/whirled/zone/data/ZoneMarshaller_ZoneMoveMarshaller.as @@ -33,19 +33,27 @@ import com.threerings.whirled.zone.client.ZoneService_ZoneMoveListener; public class ZoneMarshaller_ZoneMoveMarshaller extends InvocationMarshaller_ListenerMarshaller { + /** The method id used to dispatch moveRequiresServerSwitch responses. */ + public static const MOVE_REQUIRES_SERVER_SWITCH :int = 1; + /** The method id used to dispatch moveSucceeded responses. */ - public static const MOVE_SUCCEEDED :int = 1; + public static const MOVE_SUCCEEDED :int = 2; /** The method id used to dispatch moveSucceededWithScene responses. */ - public static const MOVE_SUCCEEDED_WITH_SCENE :int = 2; + public static const MOVE_SUCCEEDED_WITH_SCENE :int = 3; /** The method id used to dispatch moveSucceededWithUpdates responses. */ - public static const MOVE_SUCCEEDED_WITH_UPDATES :int = 3; + public static const MOVE_SUCCEEDED_WITH_UPDATES :int = 4; // from InvocationMarshaller_ListenerMarshaller override public function dispatchResponse (methodId :int, args :Array) :void { switch (methodId) { + case MOVE_REQUIRES_SERVER_SWITCH: + (listener as ZoneService_ZoneMoveListener).moveRequiresServerSwitch( + (args[0] as String), (args[1] as TypedArray /* of int */)); + return; + case MOVE_SUCCEEDED: (listener as ZoneService_ZoneMoveListener).moveSucceeded( (args[0] as int), (args[1] as PlaceConfig), (args[2] as ZoneSummary)); diff --git a/src/java/com/threerings/whirled/zone/client/ZoneDirector.java b/src/java/com/threerings/whirled/zone/client/ZoneDirector.java index cf36e2b1..15d8ef97 100644 --- a/src/java/com/threerings/whirled/zone/client/ZoneDirector.java +++ b/src/java/com/threerings/whirled/zone/client/ZoneDirector.java @@ -129,22 +129,31 @@ public class ZoneDirector extends BasicDirector return false; } + _pendingZoneId = zoneId; + + sendMoveRequest(); + return true; + } + + protected void sendMoveRequest () + { // let our zone observers know that we're attempting to switch zones - notifyObservers(Integer.valueOf(zoneId)); + notifyObservers(Integer.valueOf(_pendingZoneId)); // check the version of our cached copy of the scene to which we're requesting to move; if // we were unable to load it, assume a cached version of zero int sceneVers = 0; + int sceneId = -1; SceneModel pendingModel = _scdir.getPendingModel(); if (pendingModel != null) { sceneVers = pendingModel.version; + sceneId = pendingModel.sceneId; } // issue a moveTo request - log.info("Issuing zoned moveTo(" + ZoneUtil.toString(zoneId) + + log.info("Issuing zoned moveTo(" + ZoneUtil.toString(_pendingZoneId) + ", " + sceneId + ", " + sceneVers + ")."); - _zservice.moveTo(_ctx.getClient(), zoneId, sceneId, sceneVers, this); - return true; + _zservice.moveTo(_ctx.getClient(), _pendingZoneId, + sceneId, sceneVers, this); } @Override @@ -164,6 +173,21 @@ public class ZoneDirector extends BasicDirector _previousZoneId = -1; } + // from interface SceneService.SceneMoveListener + public void moveRequiresServerSwitch (String hostname, int[] ports) + { + // ship on over to the other server + _ctx.getClient().moveToServer(hostname, ports, new ZoneService.ConfirmListener() { + public void requestProcessed () { + // resend our move request now that we're connected to the new server + sendMoveRequest(); + } + public void requestFailed (String reason) { + ZoneDirector.this.requestFailed(reason); + } + }); + } + // from interface ZoneService.ZoneMoveListener public void moveSucceeded (int placeId, PlaceConfig config, ZoneSummary summary) { @@ -175,6 +199,9 @@ public class ZoneDirector extends BasicDirector // keep track of the summary _summary = summary; + // We're not heading there any more. + _pendingZoneId = -1; + // pass the rest off to the standard scene transition code _scdir.moveSucceeded(placeId, config); @@ -189,6 +216,9 @@ public class ZoneDirector extends BasicDirector // keep track of the summary _summary = summary; + // We're not heading there any more. + _pendingZoneId = -1; + // pass the rest off to the standard scene transition code _scdir.moveSucceededWithUpdates(placeId, config, updates); @@ -203,6 +233,9 @@ public class ZoneDirector extends BasicDirector // keep track of the summary _summary = summary; + // We're not heading there any more. + _pendingZoneId = -1; + // pass the rest off to the standard scene transition code _scdir.moveSucceededWithScene(placeId, config, model); @@ -247,6 +280,9 @@ public class ZoneDirector extends BasicDirector return; // if we're currently somewhere, just stay there } + // Not gonna get there, so clear it. + _pendingZoneId = -1; + // otherwise if we were previously in a zone/scene, try going back there if (_previousZoneId != -1) { moveTo(_previousZoneId, previousSceneId); @@ -295,4 +331,7 @@ public class ZoneDirector extends BasicDirector /** Our previous zone id. */ protected int _previousZoneId = -1; + + /** Where we're headed. */ + protected int _pendingZoneId = -1; } diff --git a/src/java/com/threerings/whirled/zone/client/ZoneService.java b/src/java/com/threerings/whirled/zone/client/ZoneService.java index 9a7e85d5..186435b7 100644 --- a/src/java/com/threerings/whirled/zone/client/ZoneService.java +++ b/src/java/com/threerings/whirled/zone/client/ZoneService.java @@ -56,6 +56,12 @@ public interface ZoneService extends InvocationService */ public void moveSucceededWithScene ( int placeId, PlaceConfig config, ZoneSummary summary, SceneModel model); + + /** + * Indicates that the client must switch to the specified server and reissue its move + * request in order to relocate to its desired zone. + */ + public void moveRequiresServerSwitch (String hostname, int[] ports); } /** diff --git a/src/java/com/threerings/whirled/zone/data/ZoneMarshaller.java b/src/java/com/threerings/whirled/zone/data/ZoneMarshaller.java index 830c9995..738743ac 100644 --- a/src/java/com/threerings/whirled/zone/data/ZoneMarshaller.java +++ b/src/java/com/threerings/whirled/zone/data/ZoneMarshaller.java @@ -49,9 +49,22 @@ public class ZoneMarshaller extends InvocationMarshaller public static class ZoneMoveMarshaller extends ListenerMarshaller implements ZoneMoveListener { + /** The method id used to dispatch {@link #moveRequiresServerSwitch} + * responses. */ + public static final int MOVE_REQUIRES_SERVER_SWITCH = 1; + + // from interface ZoneMoveMarshaller + public void moveRequiresServerSwitch (String arg1, int[] arg2) + { + _invId = null; + omgr.postEvent(new InvocationResponseEvent( + callerOid, requestId, MOVE_REQUIRES_SERVER_SWITCH, + new Object[] { arg1, arg2 }, transport)); + } + /** The method id used to dispatch {@link #moveSucceeded} * responses. */ - public static final int MOVE_SUCCEEDED = 1; + public static final int MOVE_SUCCEEDED = 2; // from interface ZoneMoveMarshaller public void moveSucceeded (int arg1, PlaceConfig arg2, ZoneSummary arg3) @@ -64,7 +77,7 @@ public class ZoneMarshaller extends InvocationMarshaller /** The method id used to dispatch {@link #moveSucceededWithScene} * responses. */ - public static final int MOVE_SUCCEEDED_WITH_SCENE = 2; + public static final int MOVE_SUCCEEDED_WITH_SCENE = 3; // from interface ZoneMoveMarshaller public void moveSucceededWithScene (int arg1, PlaceConfig arg2, ZoneSummary arg3, SceneModel arg4) @@ -77,7 +90,7 @@ public class ZoneMarshaller extends InvocationMarshaller /** The method id used to dispatch {@link #moveSucceededWithUpdates} * responses. */ - public static final int MOVE_SUCCEEDED_WITH_UPDATES = 3; + public static final int MOVE_SUCCEEDED_WITH_UPDATES = 4; // from interface ZoneMoveMarshaller public void moveSucceededWithUpdates (int arg1, PlaceConfig arg2, ZoneSummary arg3, SceneUpdate[] arg4) @@ -92,6 +105,11 @@ public class ZoneMarshaller extends InvocationMarshaller public void dispatchResponse (int methodId, Object[] args) { switch (methodId) { + case MOVE_REQUIRES_SERVER_SWITCH: + ((ZoneMoveListener)listener).moveRequiresServerSwitch( + (String)args[0], (int[])args[1]); + return; + case MOVE_SUCCEEDED: ((ZoneMoveListener)listener).moveSucceeded( ((Integer)args[0]).intValue(), (PlaceConfig)args[1], (ZoneSummary)args[2]); diff --git a/src/java/com/threerings/whirled/zone/peer/data/HostedZone.java b/src/java/com/threerings/whirled/zone/peer/data/HostedZone.java new file mode 100644 index 00000000..80dd9848 --- /dev/null +++ b/src/java/com/threerings/whirled/zone/peer/data/HostedZone.java @@ -0,0 +1,38 @@ +package com.threerings.whirled.zone.peer.data; + +import com.threerings.io.SimpleStreamableObject; + +import com.threerings.util.Name; + +import com.threerings.presents.dobj.DSet; + +/** + * Represents a zone hosted on some node. + */ +public class HostedZone extends SimpleStreamableObject + implements DSet.Entry +{ + /** The unique ID for the zone in question. */ + public int zoneId; + + /** The name of the zone being hosted. */ + public Name name; + + public HostedZone () + { + } + + public HostedZone (int zoneId, Name name) + { + this.zoneId = zoneId; + this.name = name; + } + + // from DSet.Entry + public Comparable getKey () + { + return zoneId; + } + + +} diff --git a/src/java/com/threerings/whirled/zone/peer/server/PeeredZoneManager.java b/src/java/com/threerings/whirled/zone/peer/server/PeeredZoneManager.java new file mode 100644 index 00000000..1d016080 --- /dev/null +++ b/src/java/com/threerings/whirled/zone/peer/server/PeeredZoneManager.java @@ -0,0 +1,11 @@ +package com.threerings.whirled.zone.peer.server; + +import com.threerings.whirled.zone.peer.server.PeeredZoneRegistry.PeerZoneShutdownListener; +import com.threerings.whirled.zone.server.ZoneManager; + +public interface PeeredZoneManager + extends ZoneManager +{ + /** Registers a shutdown listener with the zone. */ + public void setShutdownListener (int zoneId, PeerZoneShutdownListener shutdowner); +} diff --git a/src/java/com/threerings/whirled/zone/peer/server/PeeredZoneRegistry.java b/src/java/com/threerings/whirled/zone/peer/server/PeeredZoneRegistry.java new file mode 100644 index 00000000..ec6456dc --- /dev/null +++ b/src/java/com/threerings/whirled/zone/peer/server/PeeredZoneRegistry.java @@ -0,0 +1,193 @@ +package com.threerings.whirled.zone.peer.server; + +import com.google.inject.Inject; +import com.samskivert.util.ResultListener; +import com.samskivert.util.Tuple; + +import com.threerings.presents.data.ClientObject; +import com.threerings.presents.data.InvocationCodes; +import com.threerings.presents.server.InvocationException; +import com.threerings.presents.server.InvocationManager; + +import com.threerings.crowd.data.BodyObject; +import com.threerings.crowd.server.LocationManager; + +import com.threerings.whirled.server.SceneRegistry; +import com.threerings.whirled.zone.client.ZoneService; +import com.threerings.whirled.zone.data.ZoneCodes; +import com.threerings.whirled.zone.data.ZoneSummary; +import com.threerings.whirled.zone.peer.data.HostedZone; +import com.threerings.whirled.zone.server.ZoneManager; +import com.threerings.whirled.zone.server.ZoneMoveHandler; +import com.threerings.whirled.zone.server.ZoneRegistry; +import com.threerings.whirled.zone.server.ZoneManager.ResolutionListener; + +import static com.threerings.whirled.zone.Log.log; + +public abstract class PeeredZoneRegistry extends ZoneRegistry +{ + /** + * Peer-awareness for finding out about zones. + */ + public static interface PeerZoneResolutionListener extends ResolutionListener + { + /** + * Called when the zone is already hosted on another node. + */ + public void zoneOnNode (Tuple nodeInfo); + } + + /** + * Peered zones should be sure to call back to one of these when they're going away. + */ + public static interface PeerZoneShutdownListener + { + public void zoneDidShutdown (int zoneId); + } + + /** + * A ZoneMoveHandler that can receive the sceneOnNode() callback. + */ + public abstract static class PeerZoneMoveHandler extends ZoneMoveHandler + implements PeerZoneResolutionListener + { + public PeerZoneMoveHandler ( + LocationManager locman, ZoneManager zmgr, SceneRegistry scReg, BodyObject body, + int sceneId, int sceneVer, ZoneService.ZoneMoveListener listener) + { + super(locman, zmgr, scReg, body, sceneId, sceneVer, listener); + } + } + + protected abstract PeerZoneMoveHandler createMoveHandler (LocationManager locman, + ZoneManager zmgr, SceneRegistry scReg, BodyObject body, int sceneId, int sceneVer, + ZoneService.ZoneMoveListener listener); + + @Inject public PeeredZoneRegistry (InvocationManager invmgr, ZonePeerManager peerMgr) + { + super(invmgr); + _peerMgr = peerMgr; + } + + public void registerZoneManager (byte zoneType, ZoneManager manager) + { + if (!(manager instanceof PeeredZoneManager)) { + throw new IllegalArgumentException("All ZoneManagers received by PeeredZoneRegistry " + + "must be PeeredZoneManagers"); + } + super.registerZoneManager(zoneType, manager); + } + + @Override + public PeeredZoneManager getZoneManager (int qualifiedZoneId) + { + return (PeeredZoneManager)super.getZoneManager(qualifiedZoneId); + } + + @Override + public void moveTo (ClientObject caller, int zoneId, int sceneId, + int sceneVer, ZoneService.ZoneMoveListener listener) + throws InvocationException + { + if (!(caller instanceof BodyObject)) { + log.warning("Request to switch zones by non-BodyObject " + + "[clobj=" + caller.getClass() + "]."); + throw new InvocationException(ZoneCodes.INTERNAL_ERROR); + } + BodyObject body = (BodyObject)caller; + + PeerZoneMoveHandler handler = createMoveHandler(_locman, getZoneManager(zoneId), + _screg, body, sceneId, sceneVer, listener); + + resolvePeerZone(zoneId, handler); + } + + public void resolveZone (int zoneId, ResolutionListener listener) + { + PeeredZoneManager zmgr = getZoneManager(zoneId); + if (zmgr == null) { + log.warning("Trying to resolve a zone for which we have no manager", "zoneId", zoneId); + listener.zoneFailedToResolve(zoneId, new Exception(InvocationCodes.INTERNAL_ERROR)); + } else { + zmgr.resolveZone(zoneId, listener); + } + } + + /** + * Resolve a zone, or return the information on the peer on which it's hosted. + */ + public void resolvePeerZone (final int zoneId, final PeerZoneResolutionListener listener) + { + // check to see if the destination zone is already hosted on a server + Tuple nodeInfo = _peerMgr.getZoneHost(zoneId); + + // if it's already hosted... + if (nodeInfo != null) { + // it's hosted on this server! It should already be resolved... + if (_peerMgr.getNodeObject().nodeName.equals(nodeInfo.left)) { + resolveZone(zoneId, listener); + } else { + listener.zoneOnNode(nodeInfo); // somewhere else, pass the buck + } + return; + } + + // otherwise the zone is not resolved here nor there; so we claim the zone by acquiring a + // distributed lock and then resolve it locally + _peerMgr.acquireLock(ZonePeerManager.getZoneLock(zoneId), new ResultListener() { + public void requestCompleted (String nodeName) { + if (_peerMgr.getNodeObject().nodeName.equals(nodeName)) { + log.debug("Got lock, resolving zone", "zoneId", zoneId); + resolveZone(zoneId, new ResolutionListener() { + public void zoneWasResolved (ZoneSummary zonesum) { + releaseLock(); + _peerMgr.zoneDidStartup(zonesum.zoneId, zonesum.name); + PeerZoneShutdownListener shutdowner = new PeerZoneShutdownListener() { + public void zoneDidShutdown (int zoneId) { + _peerMgr.zoneDidShutdown(zoneId); + } + }; + getZoneManager(zonesum.zoneId).setShutdownListener( + zonesum.zoneId, shutdowner); + listener.zoneWasResolved(zonesum); + } + public void zoneFailedToResolve (int zoneId, Exception reason) { + releaseLock(); + listener.zoneFailedToResolve(zoneId, reason); + } + protected void releaseLock () { + _peerMgr.releaseLock(ZonePeerManager.getZoneLock(zoneId), + new ResultListener.NOOP()); + } + }); + + } else { + // we didn't get the lock, so let's see what happened by re-checking + Tuple nodeInfo = _peerMgr.getZoneHost(zoneId); + if (nodeName == null || nodeInfo == null || !nodeName.equals(nodeInfo.left)) { + log.warning("Zone resolved on wacked-out node?", + "zoneId", zoneId, "nodeName", nodeName, "nodeInfo", nodeInfo); + listener.zoneFailedToResolve(zoneId, + new Exception("Zone on bogus host node")); + } else { + listener.zoneOnNode(nodeInfo); // somewhere else + } + } + } + public void requestFailed (Exception cause) { + log.warning("Failed to acquire zone resolution lock", "id", zoneId, cause); + listener.zoneFailedToResolve(zoneId, cause); + } + }); + } + + /** + * Pass on that the zone has shutdown. + */ + public void zoneDidShutdown (int zoneId) + { + _peerMgr.zoneDidShutdown(zoneId); + } + + protected ZonePeerManager _peerMgr; +} diff --git a/src/java/com/threerings/whirled/zone/peer/server/ZoneNodeObject.java b/src/java/com/threerings/whirled/zone/peer/server/ZoneNodeObject.java new file mode 100644 index 00000000..2eefa153 --- /dev/null +++ b/src/java/com/threerings/whirled/zone/peer/server/ZoneNodeObject.java @@ -0,0 +1,73 @@ +package com.threerings.whirled.zone.peer.server; + +import javax.annotation.Generated; +import com.threerings.presents.dobj.DSet; +import com.threerings.crowd.peer.data.CrowdNodeObject; + +import com.threerings.whirled.zone.peer.data.HostedZone; + +public class ZoneNodeObject extends CrowdNodeObject +{ + // AUTO-GENERATED: FIELDS START + /** The field name of the hostedZones field. */ + @Generated(value={"com.threerings.presents.tools.GenDObjectTask"}) + public static final String HOSTED_ZONES = "hostedZones"; + // AUTO-GENERATED: FIELDS END + + /** Contains info on all zones hosted by this server. */ + public DSet hostedZones = DSet.newDSet(); + + + // AUTO-GENERATED: METHODS START + /** + * Requests that the specified entry be added to the + * hostedZones set. The set will not change until the event is + * actually propagated through the system. + */ + @Generated(value={"com.threerings.presents.tools.GenDObjectTask"}) + public void addToHostedZones (HostedZone elem) + { + requestEntryAdd(HOSTED_ZONES, hostedZones, elem); + } + + /** + * Requests that the entry matching the supplied key be removed from + * the hostedZones set. The set will not change until the + * event is actually propagated through the system. + */ + @Generated(value={"com.threerings.presents.tools.GenDObjectTask"}) + public void removeFromHostedZones (Comparable key) + { + requestEntryRemove(HOSTED_ZONES, hostedZones, key); + } + + /** + * Requests that the specified entry be updated in the + * hostedZones set. The set will not change until the event is + * actually propagated through the system. + */ + @Generated(value={"com.threerings.presents.tools.GenDObjectTask"}) + public void updateHostedZones (HostedZone elem) + { + requestEntryUpdate(HOSTED_ZONES, hostedZones, elem); + } + + /** + * Requests that the hostedZones 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 setHostedZones (DSet value) + { + requestAttributeChange(HOSTED_ZONES, value, this.hostedZones); + DSet clone = (value == null) ? null : value.clone(); + this.hostedZones = clone; + } + // AUTO-GENERATED: METHODS END +} diff --git a/src/java/com/threerings/whirled/zone/peer/server/ZonePeerManager.java b/src/java/com/threerings/whirled/zone/peer/server/ZonePeerManager.java new file mode 100644 index 00000000..05df2051 --- /dev/null +++ b/src/java/com/threerings/whirled/zone/peer/server/ZonePeerManager.java @@ -0,0 +1,80 @@ +package com.threerings.whirled.zone.peer.server; + +import com.google.common.base.Function; +import com.google.inject.Inject; +import com.samskivert.util.Lifecycle; +import com.samskivert.util.Tuple; + +import com.threerings.util.Name; + +import com.threerings.presents.peer.data.NodeObject; +import com.threerings.crowd.peer.server.CrowdPeerManager; + +import com.threerings.whirled.zone.peer.data.HostedZone; + +import static com.threerings.whirled.zone.Log.log; + +public abstract class ZonePeerManager extends CrowdPeerManager +{ + /** + * Creates an uninitialized peer manager. + */ + @Inject public ZonePeerManager (Lifecycle cycle) + { + super(cycle); + } + + @Override // from CrowdPeerManager + protected NodeObject createNodeObject () + { + return new ZoneNodeObject(); + } + + /** Returns a lock used to claim resolution of the specified scene. */ + public static NodeObject.Lock getZoneLock (int sceneId) + { + return new NodeObject.Lock("ZoneHost", sceneId); + } + + /** + * Returns the node name of the peer that is hosting the specified scene, or null if no peer + * has published that they are hosting the scene. + */ + public Tuple getZoneHost (final int zoneId) + { + return lookupNodeDatum(new NodeFunc>() { + public Tuple apply (ZoneNodeObject nodeobj) { + HostedZone info = nodeobj.hostedZones.get(zoneId); + return (info == null) ? null : Tuple.newTuple(nodeobj.nodeName, info); + } + }); + } + + /** + * Called by the ZoneManager when it is hosting a zone. + */ + public void zoneDidStartup (int zoneId, Name name) + { + log.debug("Hosting zone", "id", zoneId, "name", name); + ((ZoneNodeObject)_nodeobj).addToHostedZones(new HostedZone(zoneId, name)); + } + + /** + * Called by the ZoneManager when it is no longer hosting a scene. + */ + public void zoneDidShutdown (int zoneId) + { + log.debug("No longer hosting zone", "id", zoneId); + ((ZoneNodeObject)_nodeobj).removeFromHostedZones(zoneId); + } + + /** Useful with {@link #lookupNodeDatum}. */ + public static abstract class NodeFunc implements Function + { + public abstract T apply (ZoneNodeObject mnobj); + + public T apply (NodeObject nodeobj) { + return apply((ZoneNodeObject)nodeobj); + } + } +}