diff --git a/src/java/com/threerings/micasa/client/LogonPanel.java b/src/java/com/threerings/micasa/client/LogonPanel.java
index 3034295c9..38784f3be 100644
--- a/src/java/com/threerings/micasa/client/LogonPanel.java
+++ b/src/java/com/threerings/micasa/client/LogonPanel.java
@@ -205,7 +205,7 @@ public class LogonPanel extends JPanel
String password = new String(_password.getPassword()).trim();
String server = _ctx.getClient().getHostname();
- int port = _ctx.getClient().getPort();
+ int port = _ctx.getClient().getPorts()[0];
String msg = MessageBundle.tcompose("m.logging_on",
server, String.valueOf(port));
_status.append(_msgs.xlate(msg) + "\n");
diff --git a/src/java/com/threerings/micasa/client/MiCasaApp.java b/src/java/com/threerings/micasa/client/MiCasaApp.java
index 1a7678841..e81b34381 100644
--- a/src/java/com/threerings/micasa/client/MiCasaApp.java
+++ b/src/java/com/threerings/micasa/client/MiCasaApp.java
@@ -1,5 +1,5 @@
//
-// $Id: MiCasaApp.java,v 1.15 2004/08/27 02:12:49 mdb Exp $
+// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
@@ -67,7 +67,7 @@ public class MiCasaApp
_client.init(_frame);
}
- public void run (String server, int port, String username, String password)
+ public void run (String server, String username, String password)
{
// position everything and show the frame
_frame.setSize(800, 600);
@@ -76,9 +76,8 @@ public class MiCasaApp
Client client = _client.getContext().getClient();
- // pass them on to the client
- Log.info("Using [server=" + server + ", port=" + port + "].");
- client.setServer(server, port);
+ Log.info("Using [server=" + server + ".");
+ client.setServer(server, Client.DEFAULT_SERVER_PORTS);
// configure the client with some credentials and logon
if (username != null && password != null) {
@@ -95,18 +94,8 @@ public class MiCasaApp
if (args.length > 0) {
server = args[0];
}
-
- int port = Client.DEFAULT_SERVER_PORT;
- if (args.length > 1) {
- try {
- port = Integer.parseInt(args[1]);
- } catch (NumberFormatException nfe) {
- Log.warning("Invalid port specification '" + args[1] + "'.");
- }
- }
-
- String username = (args.length > 2) ? args[2] : null;
- String password = (args.length > 3) ? args[3] : null;
+ String username = (args.length > 1) ? args[1] : null;
+ String password = (args.length > 2) ? args[2] : null;
MiCasaApp app = new MiCasaApp();
try {
@@ -118,7 +107,7 @@ public class MiCasaApp
}
// and run it
- app.run(server, port, username, password);
+ app.run(server, username, password);
}
protected MiCasaClient _client;
diff --git a/src/java/com/threerings/micasa/client/MiCasaApplet.java b/src/java/com/threerings/micasa/client/MiCasaApplet.java
index 03a5cdcf4..f9ef0ff48 100644
--- a/src/java/com/threerings/micasa/client/MiCasaApplet.java
+++ b/src/java/com/threerings/micasa/client/MiCasaApplet.java
@@ -1,5 +1,5 @@
//
-// $Id: MiCasaApplet.java,v 1.9 2004/08/27 02:12:49 mdb Exp $
+// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
@@ -59,7 +59,7 @@ public class MiCasaApplet extends Applet
Client client = _client.getContext().getClient();
// indicate which server to which we should connect
- client.setServer(server, Client.DEFAULT_SERVER_PORT);
+ client.setServer(server, Client.DEFAULT_SERVER_PORTS);
// create and set our credentials
client.setCredentials(
diff --git a/src/java/com/threerings/micasa/client/MiCasaClient.java b/src/java/com/threerings/micasa/client/MiCasaClient.java
index 3f7510abd..6a7304024 100644
--- a/src/java/com/threerings/micasa/client/MiCasaClient.java
+++ b/src/java/com/threerings/micasa/client/MiCasaClient.java
@@ -67,7 +67,7 @@ public class MiCasaClient
createContextServices();
// for test purposes, hardcode the server info
- _client.setServer("localhost", Client.DEFAULT_SERVER_PORT);
+ _client.setServer("localhost", Client.DEFAULT_SERVER_PORTS);
// keep this for later
_frame = frame;
diff --git a/src/java/com/threerings/micasa/server/MiCasaServer.java b/src/java/com/threerings/micasa/server/MiCasaServer.java
index c743b5bde..801572f8a 100644
--- a/src/java/com/threerings/micasa/server/MiCasaServer.java
+++ b/src/java/com/threerings/micasa/server/MiCasaServer.java
@@ -1,5 +1,5 @@
//
-// $Id: MiCasaServer.java,v 1.9 2004/08/27 02:12:52 mdb Exp $
+// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
@@ -23,7 +23,6 @@ package com.threerings.micasa.server;
import com.threerings.crowd.server.CrowdServer;
import com.threerings.parlor.server.ParlorManager;
-import com.threerings.presents.client.Client;
import com.threerings.micasa.Log;
import com.threerings.micasa.lobby.LobbyRegistry;
@@ -61,20 +60,6 @@ public class MiCasaServer extends CrowdServer
Log.info("MiCasa server initialized.");
}
- /**
- * Returns the port on which the connection manager will listen for
- * client connections.
- */
- protected int getListenPort ()
- {
- int port = Client.DEFAULT_SERVER_PORT;
- try {
- port = Integer.parseInt(System.getProperty("port"));
- } catch (Exception e) {
- }
- return port;
- }
-
public static void main (String[] args)
{
MiCasaServer server = new MiCasaServer();
diff --git a/src/java/com/threerings/micasa/simulator/client/SimulatorApp.java b/src/java/com/threerings/micasa/simulator/client/SimulatorApp.java
index eb2989de0..7d65ff1a2 100644
--- a/src/java/com/threerings/micasa/simulator/client/SimulatorApp.java
+++ b/src/java/com/threerings/micasa/simulator/client/SimulatorApp.java
@@ -113,26 +113,14 @@ public class SimulatorApp
{
// configure and display the main frame
JFrame frame = _frame.getFrame();
- // position everything and show the frame
frame.setSize(800, 600);
SwingUtil.centerWindow(frame);
frame.setVisible(true);
// start up the client
Client client = _client.getParlorContext().getClient();
-
- // obtain the port information from system properties
- int port = Client.DEFAULT_SERVER_PORT;
- String portstr = System.getProperty("port");
- if (portstr != null) {
- try {
- port = Integer.parseInt(portstr);
- } catch (NumberFormatException nfe) {
- Log.warning("Invalid port specification '" + portstr + "'.");
- }
- }
- Log.info("Connecting to localhost:" + port + ".");
- client.setServer("localhost", port);
+ Log.info("Connecting to localhost.");
+ client.setServer("localhost", Client.DEFAULT_SERVER_PORTS);
// we want to exit when we logged off or failed to log on
client.addClientObserver(new ClientAdapter() {
diff --git a/src/java/com/threerings/presents/client/Client.java b/src/java/com/threerings/presents/client/Client.java
index 4541bb3f1..4990298e4 100644
--- a/src/java/com/threerings/presents/client/Client.java
+++ b/src/java/com/threerings/presents/client/Client.java
@@ -42,9 +42,9 @@ import com.threerings.presents.net.PongResponse;
*/
public class Client
{
- /** The default port on which the server listens for client
+ /** The default ports on which the server listens for client
* connections. */
- public static final int DEFAULT_SERVER_PORT = 47624;
+ public static final int[] DEFAULT_SERVER_PORTS = { 47624 };
/**
* Constructs a client object with the supplied credentials and
@@ -115,15 +115,15 @@ public class Client
}
/**
- * Configures the client to communicate with the server on the
- * supplied hostname/port combination.
+ * Configures the client to communicate with the server on the supplied
+ * hostname and set of ports (which will be tried in succession).
*
* @see #logon
*/
- public void setServer (String hostname, int port)
+ public void setServer (String hostname, int[] ports)
{
_hostname = hostname;
- _port = port;
+ _ports = ports;
}
/**
@@ -148,9 +148,9 @@ public class Client
* Returns the port on which this client is currently configured to
* connect to the server.
*/
- public int getPort ()
+ public int[] getPorts ()
{
- return _port;
+ return _ports;
}
/**
@@ -519,6 +519,19 @@ public class Client
}
}
+ /**
+ * Called by the {@link Communicator} if it is experiencing trouble logging
+ * on but is still trying fallback strategies.
+ */
+ protected void reportLogonTribulations (final LogonException cause)
+ {
+ _runQueue.postRunnable(new Runnable() {
+ public void run () {
+ notifyObservers(CLIENT_FAILED_TO_LOGON, cause);
+ }
+ });
+ }
+
/**
* Called by the invocation director when it successfully subscribes
* to the client object immediately following logon.
@@ -731,8 +744,8 @@ public class Client
/** The game server host. */
protected String _hostname;
- /** The port on which we connect to the game server. */
- protected int _port;
+ /** The ports on which we connect to the game server. */
+ protected int[] _ports;
/** Our list of client observers. */
protected ObserverList _observers =
diff --git a/src/java/com/threerings/presents/client/ClientObserver.java b/src/java/com/threerings/presents/client/ClientObserver.java
index bc20e4bb2..7996fb17a 100644
--- a/src/java/com/threerings/presents/client/ClientObserver.java
+++ b/src/java/com/threerings/presents/client/ClientObserver.java
@@ -1,5 +1,5 @@
//
-// $Id: ClientObserver.java,v 1.9 2004/08/27 02:20:18 mdb Exp $
+// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
@@ -53,6 +53,14 @@ public interface ClientObserver extends SessionObserver
* Called if anything fails during the logon attempt. This could be a
* network failure, authentication failure or otherwise. The exception
* provided will indicate the cause of the failure.
+ *
+ * @param cause an exception indicating the cause of the logon failure.
+ * Note: this may be a {@link LogonException} and if so, the
+ * caller must check {@link LogonException#isStillInProgress} to
+ * find out if the logon process has totally failed or if we are simply
+ * reporting intermediate status (we might be falling back to an
+ * alternative port or delaying our auto-retry attempt due to server
+ * overload).
*/
public void clientFailedToLogon (Client client, Exception cause);
diff --git a/src/java/com/threerings/presents/client/Communicator.java b/src/java/com/threerings/presents/client/Communicator.java
index d93fab00b..1698ad773 100644
--- a/src/java/com/threerings/presents/client/Communicator.java
+++ b/src/java/com/threerings/presents/client/Communicator.java
@@ -27,10 +27,12 @@ import java.io.InterruptedIOException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
+import java.net.ConnectException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import com.samskivert.swing.RuntimeAdjust;
+import com.samskivert.util.IntListUtil;
import com.samskivert.util.LoopingThread;
import com.samskivert.util.Queue;
import com.samskivert.util.StringUtil;
@@ -41,6 +43,7 @@ import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.presents.Log;
+import com.threerings.presents.data.AuthCodes;
import com.threerings.presents.dobj.DObjectManager;
import com.threerings.presents.net.AuthRequest;
import com.threerings.presents.net.AuthResponse;
@@ -429,17 +432,34 @@ public class Communicator
// look up the address of the target server
InetAddress host = InetAddress.getByName(_client.getHostname());
- int port = _client.getPort();
- // establish a socket connection to said server
- Log.debug("Connecting [host=" + host + ", port=" + port + "].");
- InetSocketAddress addr = new InetSocketAddress(host, port);
- try {
- _channel = SocketChannel.open(addr);
- } catch (IOException ioe) {
- Log.warning("Error opening [addr=" + addr + "].");
- throw ioe; // rethrow
+ // obtain the list of available ports on which to attempt our
+ // client connection and determine our preferred port
+ String pportKey = _client.getHostname() + ".preferred_port";
+ int[] ports = _client.getPorts();
+ int pport = PresentsPrefs.config.getValue(pportKey, ports[0]);
+ int ppidx = Math.max(0, IntListUtil.indexOf(ports, pport));
+
+ // try connecting on each of the ports in succession
+ for (int ii = 0; ii < ports.length; ii++) {
+ int port = ports[(ii+ppidx)%ports.length];
+ Log.info("Connecting [host=" + host + ", port=" + port + "].");
+ InetSocketAddress addr = new InetSocketAddress(host, port);
+ try {
+ _channel = SocketChannel.open(addr);
+ PresentsPrefs.config.setValue(pportKey, port);
+ break;
+ } catch (IOException ioe) {
+ if (ioe instanceof ConnectException && ii < ports.length) {
+ _client.reportLogonTribulations(
+ new LogonException(
+ AuthCodes.TRYING_NEXT_PORT, true));
+ continue; // try the next port
+ }
+ throw ioe;
+ }
}
+
_channel.configureBlocking(true);
// our messages are framed (preceded by their length), so we
diff --git a/src/java/com/threerings/presents/client/LogonException.java b/src/java/com/threerings/presents/client/LogonException.java
index 3c0951503..d35e87c94 100644
--- a/src/java/com/threerings/presents/client/LogonException.java
+++ b/src/java/com/threerings/presents/client/LogonException.java
@@ -1,5 +1,5 @@
//
-// $Id: LogonException.java,v 1.4 2004/08/27 02:20:18 mdb Exp $
+// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
@@ -33,6 +33,34 @@ public class LogonException extends Exception
*/
public LogonException (String code)
{
- super(code);
+ this(code, false);
}
+
+ /**
+ * Constructs a logon exception with the supplied logon failure code.
+ *
+ * @param stillInProgress indicates that the logon attempt has not totally
+ * failed, rather we are still trying but want to report that our initial
+ * attempt did not work and that we're falling back to alternative methods.
+ */
+ public LogonException (String code, boolean stillInProgress)
+ {
+ super(code);
+ _stillInProgress = stillInProgress;
+ }
+
+ /**
+ * Returns true if this exception is reporting an intermediate status
+ * rather than total logon failure. The client may be falling back to an
+ * alternative port or delaying an auto-retry attempt due to server
+ * overload. If this method returns true, the client should not
+ * allow additional calls to {@link Client#logon} as the current attempt is
+ * still in progress.
+ */
+ public boolean isStillInProgress ()
+ {
+ return _stillInProgress;
+ }
+
+ protected boolean _stillInProgress;
}
diff --git a/src/java/com/threerings/presents/data/AuthCodes.java b/src/java/com/threerings/presents/data/AuthCodes.java
index 39a87a46a..cc1ffc655 100644
--- a/src/java/com/threerings/presents/data/AuthCodes.java
+++ b/src/java/com/threerings/presents/data/AuthCodes.java
@@ -1,5 +1,5 @@
//
-// $Id: AuthCodes.java,v 1.3 2004/08/27 02:20:19 mdb Exp $
+// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
@@ -39,4 +39,8 @@ public interface AuthCodes
/** A code indicating that the server is not available at the moment. */
public static final String SERVER_UNAVAILABLE = "m.server_unavailable";
+
+ /** A code indicating that we failed to connect to the server on a port and
+ * are trying the next port in the list. */
+ public static final String TRYING_NEXT_PORT = "m.trying_next_port";
}
diff --git a/src/java/com/threerings/presents/server/PresentsServer.java b/src/java/com/threerings/presents/server/PresentsServer.java
index 0bcf6bc3c..206bf3e43 100644
--- a/src/java/com/threerings/presents/server/PresentsServer.java
+++ b/src/java/com/threerings/presents/server/PresentsServer.java
@@ -153,7 +153,7 @@ public class PresentsServer
*/
protected int[] getListenPorts ()
{
- return new int[] { Client.DEFAULT_SERVER_PORT };
+ return Client.DEFAULT_SERVER_PORTS;
}
/**
diff --git a/tests/src/java/com/threerings/crowd/client/JabberApp.java b/tests/src/java/com/threerings/crowd/client/JabberApp.java
index a98b04a81..50ddcc021 100644
--- a/tests/src/java/com/threerings/crowd/client/JabberApp.java
+++ b/tests/src/java/com/threerings/crowd/client/JabberApp.java
@@ -50,8 +50,8 @@ public class JabberApp
_client.init(_frame);
}
- public void run (String server, int port, String username, String password,
- int roomId)
+ public void run (
+ String server, String username, String password, int roomId)
{
// position everything and show the frame
_frame.setSize(800, 600);
@@ -62,8 +62,8 @@ public class JabberApp
Client client = _client.getContext().getClient();
// pass them on to the client
- Log.info("Using [server=" + server + ", port=" + port + "].");
- client.setServer(server, port);
+ Log.info("Using [server=" + server + "].");
+ client.setServer(server, Client.DEFAULT_SERVER_PORTS);
// configure the client with some credentials and logon
if (username != null && password != null) {
@@ -80,18 +80,8 @@ public class JabberApp
if (args.length > 0) {
server = args[0];
}
-
- int port = Client.DEFAULT_SERVER_PORT;
- if (args.length > 1) {
- try {
- port = Integer.parseInt(args[1]);
- } catch (NumberFormatException nfe) {
- Log.warning("Invalid port specification '" + args[1] + "'.");
- }
- }
-
- String username = (args.length > 2) ? args[2] : null;
- String password = (args.length > 3) ? args[3] : null;
+ String username = (args.length > 1) ? args[1] : null;
+ String password = (args.length > 2) ? args[2] : null;
int roomId = 2;
if (args.length > 4) {
try {
@@ -111,7 +101,7 @@ public class JabberApp
}
// and run it
- app.run(server, port, username, password, roomId);
+ app.run(server, username, password, roomId);
}
protected JabberClient _client;
diff --git a/tests/src/java/com/threerings/crowd/client/JabberClient.java b/tests/src/java/com/threerings/crowd/client/JabberClient.java
index 5ffc171d5..f2b4c5244 100644
--- a/tests/src/java/com/threerings/crowd/client/JabberClient.java
+++ b/tests/src/java/com/threerings/crowd/client/JabberClient.java
@@ -63,7 +63,7 @@ public class JabberClient
createContextServices();
// for test purposes, hardcode the server info
- _client.setServer("localhost", Client.DEFAULT_SERVER_PORT);
+ _client.setServer("localhost", Client.DEFAULT_SERVER_PORTS);
_client.addClientObserver(this);
// keep this for later
diff --git a/tests/src/java/com/threerings/crowd/client/TestClient.java b/tests/src/java/com/threerings/crowd/client/TestClient.java
index 89ff526b7..9bf6e3667 100644
--- a/tests/src/java/com/threerings/crowd/client/TestClient.java
+++ b/tests/src/java/com/threerings/crowd/client/TestClient.java
@@ -55,7 +55,7 @@ public class TestClient
_client.addClientObserver(this);
// for test purposes, hardcode the server info
- _client.setServer("localhost", Client.DEFAULT_SERVER_PORT);
+ _client.setServer("localhost", Client.DEFAULT_SERVER_PORTS);
}
public void run ()
diff --git a/tests/src/java/com/threerings/jme/client/JabberApp.java b/tests/src/java/com/threerings/jme/client/JabberApp.java
index 663709519..ebb28092c 100644
--- a/tests/src/java/com/threerings/jme/client/JabberApp.java
+++ b/tests/src/java/com/threerings/jme/client/JabberApp.java
@@ -95,13 +95,13 @@ public class JabberApp extends JmeApp
return true;
}
- public void run (String server, int port, String username, String password)
+ public void run (String server, String username, String password)
{
Client client = _client.getContext().getClient();
// pass them on to the client
- Log.info("Using [server=" + server + ", port=" + port + "].");
- client.setServer(server, port);
+ Log.info("Using [server=" + server + ".");
+ client.setServer(server, Client.DEFAULT_SERVER_PORTS);
// configure the client with some credentials and logon
if (username != null && password != null) {
@@ -136,15 +136,6 @@ public class JabberApp extends JmeApp
server = args[0];
}
- int port = Client.DEFAULT_SERVER_PORT;
- if (args.length > 1) {
- try {
- port = Integer.parseInt(args[1]);
- } catch (NumberFormatException nfe) {
- Log.warning("Invalid port specification '" + args[1] + "'.");
- }
- }
-
// load up the default BUI stylesheet
try {
InputStream stin = JabberApp.class.getClassLoader().
@@ -157,12 +148,12 @@ public class JabberApp extends JmeApp
System.exit(-1);
}
- String username = (args.length > 2) ? args[2] : null;
- String password = (args.length > 3) ? args[3] : null;
+ String username = (args.length > 1) ? args[1] : null;
+ String password = (args.length > 2) ? args[2] : null;
JabberApp app = new JabberApp();
app.init();
- app.run(server, port, username, password);
+ app.run(server, username, password);
}
protected JabberClient _client;
diff --git a/tests/src/java/com/threerings/jme/client/JabberClient.java b/tests/src/java/com/threerings/jme/client/JabberClient.java
index ad8817faf..2fc854f94 100644
--- a/tests/src/java/com/threerings/jme/client/JabberClient.java
+++ b/tests/src/java/com/threerings/jme/client/JabberClient.java
@@ -67,7 +67,7 @@ public class JabberClient
createContextServices(app);
// for test purposes, hardcode the server info
- _client.setServer("localhost", Client.DEFAULT_SERVER_PORT);
+ _client.setServer("localhost", Client.DEFAULT_SERVER_PORTS);
_client.addClientObserver(this);
}
diff --git a/tests/src/java/com/threerings/presents/client/TestClient.java b/tests/src/java/com/threerings/presents/client/TestClient.java
index 589708334..0836db116 100644
--- a/tests/src/java/com/threerings/presents/client/TestClient.java
+++ b/tests/src/java/com/threerings/presents/client/TestClient.java
@@ -154,7 +154,7 @@ public class TestClient
Client client = new Client(creds, tclient);
tclient.setClient(client);
client.addClientObserver(tclient);
- client.setServer("localhost", Client.DEFAULT_SERVER_PORT);
+ client.setServer("localhost", Client.DEFAULT_SERVER_PORTS);
client.logon();
// start up our event processing loop
tclient.run();