All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.threerings.whirled.zone.peer.server.PeeredZoneRegistry Maven / Gradle / Ivy

The newest version!
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/vilya/
//
// 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.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;
    }

    @Override
    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;

        } else {
            resolveNewZone(zoneId, listener);
        }
    }

    /**
     * Resolve a zone that's not yet hosted.
     */
    protected void resolveNewZone (final int zoneId, final PeerZoneResolutionListener listener)
    {
        // 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) {
                resolveZoneForNode(zoneId, nodeName, listener);
            }
            public void requestFailed (Exception cause) {
                log.warning("Failed to acquire zone resolution lock", "id", zoneId, cause);
                listener.zoneFailedToResolve(zoneId, cause);
            }
        });
    }

    /**
     * Resolve a zone that's hosted or to-be-hosted on a zone.
     */
    protected void resolveZoneForNode (final int zoneId, String nodeName,
        final PeerZoneResolutionListener listener)
    {
        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
            }
        }
    }

    /**
     * Pass on that the zone has shutdown.
     */
    public void zoneDidShutdown (int zoneId)
    {
        _peerMgr.zoneDidShutdown(zoneId);
    }

    protected ZonePeerManager _peerMgr;
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy