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:
Michael Bayne
2007-02-24 00:46:40 +00:00
parent 03876838b3
commit 6808911f65
+104 -131
View File
@@ -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;
} }
} }