From 77e3f22d908dfa0269d8ccfa74f8a8e0096b3d48 Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Fri, 13 Jul 2007 21:57:55 +0000 Subject: [PATCH] Added moveToServer() which does pretty much what it says. Boy I love writing everything in two languages. Especially when one doesn't support inner classes and has non-C syntax just to make life easier for parser writers. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4762 542714f4-19e9-0310-aa3c-eee0fc999fb1 --- .../com/threerings/presents/client/Client.as | 25 ++++- .../threerings/presents/client/Client.java | 97 ++++++++++++++++++- 2 files changed, 116 insertions(+), 6 deletions(-) diff --git a/src/as/com/threerings/presents/client/Client.as b/src/as/com/threerings/presents/client/Client.as index 829e63f18..420d350fc 100644 --- a/src/as/com/threerings/presents/client/Client.as +++ b/src/as/com/threerings/presents/client/Client.as @@ -31,6 +31,7 @@ import flash.utils.Timer; import com.threerings.util.MethodQueue; import com.threerings.util.ObserverList; +import com.threerings.presents.client.InvocationService_ConfirmListener; import com.threerings.presents.data.ClientObject; import com.threerings.presents.data.InvocationCodes; @@ -270,6 +271,27 @@ public class Client extends EventDispatcher return true; } + /** + * Transitions a logged on client from its current server to the specified new server. + * Currently this simply logs the client off of its current server (if it is logged on) and + * logs it onto the new server, but in the future we may aim to do something fancier. + * + *

If we fail to connect to the new server, the client will not be automatically + * reconnected to the old server. It will be in a logged off state. However, it will be + * reconfigured with the hostname and ports of the old server so that the caller can notify the + * user of the failure and then simply call {@link #logon} to attempt to reconnect to the old + * server. + * + * @param observer an observer that will be notified when we have successfully logged onto the + * other server, or if the move failed. + */ + public function moveToServer (hostname :String, ports :Array, + obs :InvocationService_ConfirmListener) :void + { + // the server switcher will take care of everything for us + new ServerSwitcher(this, hostname, ports, obs).switchServers(); + } + /** * Requests that the client log off of the server to which it is connected. * @@ -299,8 +321,7 @@ public class Client extends EventDispatcher return true; } - public function gotBootstrap (data :BootstrapData, omgr :DObjectManager) - :void + public function gotBootstrap (data :BootstrapData, omgr :DObjectManager) :void { log.debug("Got bootstrap " + data + "."); diff --git a/src/java/com/threerings/presents/client/Client.java b/src/java/com/threerings/presents/client/Client.java index b74f20ac4..d03983c02 100644 --- a/src/java/com/threerings/presents/client/Client.java +++ b/src/java/com/threerings/presents/client/Client.java @@ -27,8 +27,9 @@ import com.samskivert.util.Interval; import com.samskivert.util.ObserverList; import com.samskivert.util.RunAnywhere; import com.samskivert.util.RunQueue; +import com.samskivert.util.StringUtil; -import com.threerings.presents.Log; +import com.threerings.presents.data.AuthCodes; import com.threerings.presents.data.ClientObject; import com.threerings.presents.data.InvocationCodes; import com.threerings.presents.dobj.DEvent; @@ -40,6 +41,8 @@ import com.threerings.presents.net.Credentials; import com.threerings.presents.net.PingRequest; import com.threerings.presents.net.PongResponse; +import static com.threerings.presents.Log.log; + /** * Through the client object, a connection to the system is established and maintained. The client * object maintains two separate threads (a reader and a writer) by which all network traffic is @@ -108,6 +111,7 @@ public class Client * ports (which will be tried in succession). * * @see #logon + * @see #moveToServer */ public void setServer (String hostname, int[] ports) { @@ -393,6 +397,26 @@ public class Client return true; } + /** + * Transitions a logged on client from its current server to the specified new server. + * Currently this simply logs the client off of its current server (if it is logged on) and + * logs it onto the new server, but in the future we may aim to do something fancier. + * + *

If we fail to connect to the new server, the client will not be automatically + * reconnected to the old server. It will be in a logged off state. However, it will be + * reconfigured with the hostname and ports of the old server so that the caller can notify the + * user of the failure and then simply call {@link #logon} to attempt to reconnect to the old + * server. + * + * @param observer an observer that will be notified when we have successfully logged onto the + * other server, or if the move failed. + */ + public void moveToServer (String hostname, int[] ports, InvocationService.ConfirmListener obs) + { + // the server switcher will take care of everything for us + new ServerSwitcher(hostname, ports, obs).switchServers(); + } + /** * Requests that the client log off of the server to which it is connected. * @@ -406,7 +430,7 @@ public class Client { // if we have no communicator, we're not logged on anyway if (_comm == null) { - Log.warning("Ignoring request to logoff because we're not logged on."); + log.warning("Ignoring request to logoff because we're not logged on."); return true; } @@ -464,7 +488,7 @@ public class Client */ protected void gotBootstrap (BootstrapData data, DObjectManager omgr) { - Log.debug("Got bootstrap " + data + "."); + log.fine("Got bootstrap " + data + "."); // keep these around for interested parties _bstrap = data; @@ -509,7 +533,7 @@ public class Client if (_dcalc != null) { // if our current calculator is done, clear it out if (_dcalc.isDone()) { - Log.debug("Time offset from server: " + _serverDelta + "ms."); + log.fine("Time offset from server: " + _serverDelta + "ms."); _dcalc = null; } else if (_dcalc.shouldSendPing()) { // otherwise, send another ping @@ -742,6 +766,71 @@ public class Client protected boolean _rejected; } + /** Handles the process of switching between servers. See {@link #moveToServer}. */ + protected class ServerSwitcher extends ClientAdapter + { + public ServerSwitcher (String hostname, int[] ports, InvocationService.ConfirmListener obs) + { + _hostname = hostname; + _ports = ports; + _observer = obs; + } + + public void switchServers () + { + addClientObserver(this); + if (!isLoggedOn()) { + // if we're not logged on right now, just do the switch immediately + clientDidClear(Client.this); + + } else { + // note our current connection information so that we can restore it if our logon + // attempt fails + _oldHostname = Client.this._hostname; + _oldPorts = Client.this._ports; + + // otherwise logoff and wait for all of our callbacks to clear + logoff(true); + } + } + + public void clientDidClear (Client client) + { + // configure the client to point to the new server and logon + setServer(_hostname, _ports); + + if (!logon()) { + log.warning("logon() failed during server switch? [hostname=" + _hostname + + ", ports=" + StringUtil.toString(_ports) + "]."); + clientFailedToLogon(Client.this, new Exception("logon() failed?")); + } + } + + public void clientDidLogon (Client client) + { + removeClientObserver(this); + if (_observer != null) { + _observer.requestProcessed(); + } + } + + public void clientFailedToLogon (Client client, Exception cause) + { + removeClientObserver(this); + if (_oldHostname != null) { // restore our previous server and ports + setServer(_oldHostname, _oldPorts); + } + if (_observer != null) { + _observer.requestFailed((cause instanceof LogonException) ? + cause.getMessage() : AuthCodes.SERVER_ERROR); + } + } + + protected String _hostname, _oldHostname; + protected int[] _ports, _oldPorts; + protected InvocationService.ConfirmListener _observer; + } + /** The credentials we used to authenticate with the server. */ protected Credentials _creds;