Do not attempt to do things in our constructor if we're not connected. This
allows games to create a HostCoordinator in their constructor and still not fail unrecoverably to display outside the client environment. git-svn-id: svn+ssh://src.earth.threerings.net/vilya/trunk@211 c613c5cb-e716-0410-b11b-feb51c14d237
This commit is contained in:
@@ -21,57 +21,49 @@
|
|||||||
|
|
||||||
package com.threerings.ezgame {
|
package com.threerings.ezgame {
|
||||||
|
|
||||||
import flash.events.EventDispatcher;
|
import flash.events.EventDispatcher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
HostCoordinator provides methods for determining whether the current
|
* HostCoordinator provides methods for determining whether the current client should consider
|
||||||
client should consider itself the authority in dealing with game state
|
* itself the authority in dealing with game state (for example, when dealing out cards, or setting
|
||||||
(for example, when dealing out cards, or setting up the game board).
|
* up the game board).
|
||||||
|
*
|
||||||
Current hosting state can be retrieved from the /status/ property.
|
* Current hosting state can be retrieved from the /status/ property. Please note that hosting
|
||||||
Please note that hosting authority can be transferred between clients
|
* authority can be transferred between clients at any moment, so all clients should be prepared to
|
||||||
at any moment, so all clients should be prepared to suddenly take
|
* suddenly take over host responsibilities.
|
||||||
over host responsibilities.
|
*
|
||||||
|
* Additionally, the client can register itself for notifications about changes to the /status/
|
||||||
Additionally, the client can register itself for notifications
|
* property - see HostEvent.
|
||||||
about changes to the /status/ property - see HostEvent.
|
*
|
||||||
|
* Usage example:
|
||||||
Usage example:
|
*
|
||||||
|
* <pre>
|
||||||
<pre>
|
* _coord = new HostCoordinator (_gameCtrl);
|
||||||
_coord = new HostCoordinator (_gameCtrl);
|
* _coord.addEventListener (HostEvent.CLAIMED, firstHostHandler);
|
||||||
_coord.addEventListener (HostEvent.CLAIMED, firstHostHandler);
|
* _coord.addEventListener (HostEvent.CHANGED, hostChangeHandler);
|
||||||
_coord.addEventListener (HostEvent.CHANGED, hostChangeHandler);
|
*
|
||||||
|
* ...
|
||||||
...
|
*
|
||||||
|
* function firstHostHandler (event : HostEvent) {
|
||||||
function firstHostHandler (event : HostEvent)
|
* if (_coord.status == HostCoordinator.STATUS_HOST) {
|
||||||
{
|
* // I'm the first host in the game - I should set up the initial board, etc.
|
||||||
if (_coord.status == HostCoordinator.STATUS_HOST)
|
* }
|
||||||
{
|
* }
|
||||||
// I'm the first host in the game - I should set up the initial board, etc.
|
*
|
||||||
}
|
* function hostChangeHandler (event : HostEvent) {
|
||||||
}
|
* if (_coord.status == HostCoordinator.STATUS_HOST) {
|
||||||
|
* // I just became the host - do whatever is needed.
|
||||||
function hostChangeHandler (event : HostEvent)
|
* }
|
||||||
{
|
* }
|
||||||
if (_coord.status == HostCoordinator.STATUS_HOST)
|
* </pre>
|
||||||
{
|
*/
|
||||||
// I just became the host - do whatever is needed.
|
public class HostCoordinator extends EventDispatcher
|
||||||
}
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
*/
|
|
||||||
public class HostCoordinator
|
|
||||||
extends EventDispatcher
|
|
||||||
implements OccupantChangedListener, PropertyChangedListener
|
implements OccupantChangedListener, PropertyChangedListener
|
||||||
{
|
{
|
||||||
// CONSTANTS
|
// PUBLIC INTERFACE
|
||||||
|
|
||||||
/** Status constant, means that the current status is unknown, either because
|
/** Status constant, means that the current status is unknown, either because the data had not
|
||||||
the data had not yet arrived from the server, or because nobody claims
|
* yet arrived from the server, or because nobody claims to be host for this game. */
|
||||||
to be host for this game. */
|
|
||||||
public static const STATUS_UNKNOWN : String = "STATUS_UNKNOWN";
|
public static const STATUS_UNKNOWN : String = "STATUS_UNKNOWN";
|
||||||
|
|
||||||
/** Status constant, means that this client is the current host. */
|
/** Status constant, means that this client is the current host. */
|
||||||
@@ -79,148 +71,129 @@ public class HostCoordinator
|
|||||||
|
|
||||||
/** Status constant, means that some other client is the current host. */
|
/** Status constant, means that some other client is the current host. */
|
||||||
public static const STATUS_NOT_HOST : String = "STATUS_NOT_HOST";
|
public static const STATUS_NOT_HOST : String = "STATUS_NOT_HOST";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// PUBLIC INTERFACE
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Constructor, expects an initialized instance of EZGameControl.
|
* Constructor, expects an initialized instance of EZGameControl. If the optional showDebug
|
||||||
|
* flag is set, it will display host info as chat messages (only useful for debugging).
|
||||||
If the optional showDebug flag is set, it will display host info
|
|
||||||
as chat messages (only useful for debugging).
|
|
||||||
*/
|
*/
|
||||||
public function HostCoordinator (control : EZGameControl, showDebug : Boolean = false)
|
public function HostCoordinator (control : EZGameControl, showDebug : Boolean = false)
|
||||||
{
|
{
|
||||||
_control = control;
|
_control = control;
|
||||||
_control.registerListener (this);
|
_control.registerListener(this);
|
||||||
|
|
||||||
_showDebug = showDebug;
|
_showDebug = showDebug;
|
||||||
|
|
||||||
// Try set host role if it's not already claimed (i.e. null)
|
if (_control.isConnected()) {
|
||||||
debugLog ("Trying to claim host role.");
|
// Try set host role if it's not already claimed (i.e. null)
|
||||||
debugLog ("Current status: " + status);
|
debugLog("Trying to claim host role.");
|
||||||
tryReplaceHost (null);
|
debugLog("Current status: " + status);
|
||||||
|
tryReplaceHost(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Retrieves current host coordination status, as one of the STATUS_*
|
* Retrieves current host coordination status, as one of the STATUS_* constants.
|
||||||
constants.
|
*
|
||||||
|
* Note: do not save the value of this property. Hosting status can change at any moment: when
|
||||||
Note: do not save the value of this property. Hosting status
|
* the current host quits the game, any client can suddenly become the new host.
|
||||||
can change at any moment: when the current host quits the game,
|
*/
|
||||||
any client can suddenly become the new host.
|
|
||||||
l */
|
|
||||||
public function get status () : String
|
public function get status () : String
|
||||||
{
|
{
|
||||||
var hostId : Number = getHostId ();
|
var hostId : Number = getHostId();
|
||||||
if (hostId == _control.getMyId ()) { return STATUS_HOST; }
|
if (hostId == _control.getMyId()) {
|
||||||
if (hostId > 0) { return STATUS_NOT_HOST; }
|
return STATUS_HOST;
|
||||||
return STATUS_UNKNOWN;
|
}
|
||||||
|
if (hostId > 0) {
|
||||||
|
return STATUS_NOT_HOST;
|
||||||
|
}
|
||||||
|
return STATUS_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// EVENT HANDLERS
|
// EVENT HANDLERS
|
||||||
|
|
||||||
/** From OccupantChangedListener: keep track of people coming in */
|
// from interface OccupantChangedListener
|
||||||
public function occupantEntered (event : OccupantChangedEvent) : void
|
public function occupantEntered (event : OccupantChangedEvent) : void
|
||||||
{
|
{
|
||||||
debugHostStatus ();
|
debugHostStatus ();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** From OccupantChangedListener: keep track of people leaving */
|
// from interface OccupantChangedListener
|
||||||
public function occupantLeft (event : OccupantChangedEvent) : void
|
public function occupantLeft (event : OccupantChangedEvent) : void
|
||||||
{
|
{
|
||||||
// If the occupant who just left was the host, every client will
|
// If the occupant who just left was the host, every client will get a shot at trying to
|
||||||
// get a shot at trying to claim their role.
|
// claim their role.
|
||||||
debugLog ("My ID: " + _control.getMyId ());
|
debugLog("My ID: " + _control.getMyId());
|
||||||
debugLog ("Occupant left: " + event.occupantId);
|
debugLog("Occupant left: " + event.occupantId);
|
||||||
debugLog ("Current host: " + getHostId());
|
debugLog("Current host: " + getHostId());
|
||||||
|
|
||||||
if (getHostId () == event.occupantId) // Elvis has left the building
|
if (getHostId() == event.occupantId) { // Elvis has left the building
|
||||||
{
|
debugLog("Removing the old host...");
|
||||||
debugLog ("Removing the old host...");
|
// Clear the old host value, and put myself in their place. Only the first client will
|
||||||
// Clear the old host value, and put myself in their place.
|
// succeed in doing this.
|
||||||
// Only the first client will succeed in doing this.
|
|
||||||
tryReplaceHost (event.occupantId);
|
tryReplaceHost (event.occupantId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** From PropertyChangedListener: keep track of changing host values */
|
// from interface PropertyChangedListener
|
||||||
public function propertyChanged (event : PropertyChangedEvent) : void
|
public function propertyChanged (event : PropertyChangedEvent) : void
|
||||||
{
|
{
|
||||||
if (event.name == HOST_NAME)
|
if (event.name == HOST_NAME) {
|
||||||
{
|
debugLog("Host variable changed to: " + event.newValue);
|
||||||
debugLog ("Host variable changed to: " + event.newValue);
|
|
||||||
|
|
||||||
// if the host was not known before, dispatch the claimed event
|
|
||||||
if (event.oldValue == null &&
|
|
||||||
event.newValue != null)
|
|
||||||
{
|
|
||||||
var claimed : HostEvent = new HostEvent (HostEvent.CLAIMED, _control);
|
|
||||||
this.dispatchEvent (claimed);
|
|
||||||
}
|
|
||||||
|
|
||||||
// always dispatch the changed event
|
|
||||||
var changed : HostEvent = new HostEvent (HostEvent.CHANGED, _control);
|
|
||||||
this.dispatchEvent (changed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// PRIVATE FUNCTIONS
|
// if the host was not known before, dispatch the claimed event
|
||||||
|
if (event.oldValue == null && event.newValue != null) {
|
||||||
|
this.dispatchEvent(new HostEvent(HostEvent.CLAIMED, _control));
|
||||||
|
}
|
||||||
|
|
||||||
|
// always dispatch the changed event
|
||||||
|
this.dispatchEvent(new HostEvent(HostEvent.CHANGED, _control));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IMPLEMENTATION DETAILS
|
||||||
|
|
||||||
/** Get the current host ID (will be 0 if there is no authoritative host) */
|
/** Get the current host ID (will be 0 if there is no authoritative host) */
|
||||||
private function getHostId () : Number
|
protected function getHostId () : Number
|
||||||
{
|
{
|
||||||
var hostvalue : Object = _control.get (HOST_NAME);
|
var hostvalue : Object = _control.get(HOST_NAME);
|
||||||
if (hostvalue != null && hostvalue is Number)
|
if (hostvalue != null && hostvalue is Number) {
|
||||||
{
|
|
||||||
return hostvalue as Number;
|
return hostvalue as Number;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Helper function: tries to set the host variable to the client's id,
|
/** Helper function: tries to set the host variable to the client's id, if the current value is
|
||||||
if the current value is equal to /hostId/. If the value of null for
|
* equal to /hostId/. If the value of null for hostId has the meaning of "set me as host only
|
||||||
hostId has the meaning of "set me as host only if the role has not
|
* if the role has not been claimed yet". */
|
||||||
been claimed yet". */
|
protected function tryReplaceHost (hostId : Object) : void
|
||||||
private function tryReplaceHost (hostId : Object) : void
|
|
||||||
{
|
{
|
||||||
_control.testAndSet (HOST_NAME, _control.getMyId (), hostId);
|
_control.testAndSet (HOST_NAME, _control.getMyId (), hostId);
|
||||||
debugHostStatus ();
|
debugHostStatus ();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Debug only */
|
/** Debug only */
|
||||||
private function debugLog (message : String) : void
|
protected function debugLog (message : String) : void
|
||||||
{
|
{
|
||||||
if (_showDebug) { _control.localChat (message); }
|
if (_showDebug) {
|
||||||
|
_control.localChat (message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Debug only */
|
/** Debug only */
|
||||||
private function debugHostStatus () : void
|
protected function debugHostStatus () : void
|
||||||
{
|
{
|
||||||
debugLog (
|
debugLog((status == STATUS_HOST ? "I am" : "I'm not") +
|
||||||
(status == STATUS_HOST ? "I am" : "I'm not") +
|
" the host (id " + getHostId() + ", mine is " + _control.getMyId() + ")");
|
||||||
" the host (id " + getHostId() + ", mine is " + _control.getMyId() + ")");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// PRIVATE VALUES
|
|
||||||
|
|
||||||
/** Magic key that stores current authoritative host */
|
/** Magic key that stores current authoritative host */
|
||||||
private var HOST_NAME : String = "HOST_COORDINATOR_AUTHORITATIVE_HOST";
|
protected var HOST_NAME : String = "HOST_COORDINATOR_AUTHORITATIVE_HOST";
|
||||||
|
|
||||||
/** Controller storage */
|
/** Controller storage */
|
||||||
private var _control : EZGameControl = null;
|
protected var _control : EZGameControl = null;
|
||||||
|
|
||||||
/** Debug flag */
|
/** Debug flag */
|
||||||
private var _showDebug : Boolean;
|
protected var _showDebug : Boolean;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user