Revamped "persona selection" support. Things now actually work, and in as
sensible a way as we can hope for. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@1717 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: ClientManager.java,v 1.22 2002/09/16 23:34:25 mdb Exp $
|
||||
// $Id: ClientManager.java,v 1.23 2002/09/19 23:36:59 mdb Exp $
|
||||
|
||||
package com.threerings.presents.server;
|
||||
|
||||
@@ -86,12 +86,12 @@ public class ClientManager implements ConnectionObserver
|
||||
|
||||
/**
|
||||
* Returns the client instance that manages the client session for the
|
||||
* specified username or null if that client is not currently
|
||||
* specified credentials or null if that client is not currently
|
||||
* connected to the server.
|
||||
*/
|
||||
public PresentsClient getClient (String username)
|
||||
public PresentsClient getClient (Credentials creds)
|
||||
{
|
||||
return (PresentsClient)_usermap.get(username);
|
||||
return (PresentsClient)_usermap.get(creds);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,7 +101,17 @@ public class ClientManager implements ConnectionObserver
|
||||
*/
|
||||
public ClientObject getClientObject (String username)
|
||||
{
|
||||
return (ClientObject)_objmap.get(username);
|
||||
return (ClientObject)_objmap.get(toKey(username));
|
||||
}
|
||||
|
||||
/**
|
||||
* We convert usernames to lower case in the username to client object
|
||||
* mapping so that we can pass arbitrarily cased usernames (like those
|
||||
* that might be typed in by a "user") straight on through.
|
||||
*/
|
||||
protected final String toKey (String username)
|
||||
{
|
||||
return username.toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,14 +125,15 @@ public class ClientManager implements ConnectionObserver
|
||||
String username, ClientResolutionListener listener)
|
||||
{
|
||||
// look to see if the client object is already resolved
|
||||
ClientObject clobj = (ClientObject)_objmap.get(username);
|
||||
String key = toKey(username);
|
||||
ClientObject clobj = (ClientObject)_objmap.get(key);
|
||||
if (clobj != null) {
|
||||
listener.clientResolved(username, clobj);
|
||||
return;
|
||||
}
|
||||
|
||||
// look to see if it's currently being resolved
|
||||
ClientResolver clr = (ClientResolver)_penders.get(username);
|
||||
ClientResolver clr = (ClientResolver)_penders.get(key);
|
||||
if (clr != null) {
|
||||
// throw this guy onto the bandwagon
|
||||
clr.addResolutionListener(listener);
|
||||
@@ -154,10 +165,11 @@ public class ClientManager implements ConnectionObserver
|
||||
String username, ClientObject clobj)
|
||||
{
|
||||
// stuff the object into the mapping table
|
||||
_objmap.put(username, clobj);
|
||||
String key = toKey(username);
|
||||
_objmap.put(key, clobj);
|
||||
|
||||
// and remove the resolution listener
|
||||
_penders.remove(username);
|
||||
_penders.remove(key);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -172,11 +184,12 @@ public class ClientManager implements ConnectionObserver
|
||||
{
|
||||
// we only remove the mapping if there's not a session in progress
|
||||
// (which is indicated by an entry in the locks table)
|
||||
if (_locks.contains(username)) {
|
||||
String key = toKey(username);
|
||||
if (_locks.contains(key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ClientObject clobj = (ClientObject)_objmap.remove(username);
|
||||
ClientObject clobj = (ClientObject)_objmap.remove(key);
|
||||
if (clobj != null) {
|
||||
PresentsServer.omgr.destroyObject(clobj.getOid());
|
||||
} else {
|
||||
@@ -185,45 +198,6 @@ public class ClientManager implements ConnectionObserver
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remaps a client from its old username to the specified new
|
||||
* username. The client must end its session using the new username.
|
||||
* This most likely shouldn't be called anywhere except from {@link
|
||||
* PresentsClient#setUsername}.
|
||||
*
|
||||
* @return true if the remapping succeeded, false if it failed.
|
||||
*/
|
||||
protected synchronized boolean remapClient (
|
||||
String oldname, String newname)
|
||||
{
|
||||
// make sure they are already mapped
|
||||
PresentsClient client = (PresentsClient)_usermap.remove(oldname);
|
||||
if (client == null) {
|
||||
Log.warning("Aiya! Can't remap non-existent user " +
|
||||
"[oldname=" + oldname + ", newname=" + newname + "].");
|
||||
return false;
|
||||
}
|
||||
|
||||
// map them under their new name
|
||||
_usermap.put(newname, client);
|
||||
|
||||
// release their old lock and create a lock for their new name
|
||||
releaseClient(oldname);
|
||||
lockClient(newname);
|
||||
|
||||
// update their client object mapping
|
||||
ClientObject clobj = (ClientObject)_objmap.remove(oldname);
|
||||
if (clobj == null) {
|
||||
Log.warning("Aiya! Unable to unmap old client object when " +
|
||||
"remapping user [oldname=" + oldname +
|
||||
", newname=" + newname + "]. Hoping for the best.");
|
||||
} else {
|
||||
_objmap.put(newname, clobj);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* When a client object becomes part of an active session, this method
|
||||
* should be called to ensure that it is not unloaded by any entities
|
||||
@@ -231,30 +205,31 @@ public class ClientManager implements ConnectionObserver
|
||||
* automatically when a real user starts a session by establishing a
|
||||
* network connection with the server. If a client session is managed
|
||||
* via some other mechanism (bots managed by the server, for example),
|
||||
* this method and its corresponding {@link #releaseClient} should be
|
||||
* called at the beginning and end of the faked client session
|
||||
* respectively.
|
||||
* this method and its corresponding {@link #releaseClientObject}
|
||||
* should be called at the beginning and end of the faked client
|
||||
* session respectively.
|
||||
*/
|
||||
public synchronized void lockClient (String username)
|
||||
public synchronized void lockClientObject (String username)
|
||||
{
|
||||
if (_locks.contains(username)) {
|
||||
String key = toKey(username);
|
||||
if (_locks.contains(key)) {
|
||||
Log.warning("Requested to lock already locked user " +
|
||||
"[username=" + username + "].");
|
||||
Thread.dumpStack();
|
||||
|
||||
} else {
|
||||
_locks.add(username);
|
||||
_locks.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases a client object when their session has ended.
|
||||
*
|
||||
* @see #lockClient
|
||||
* @see #lockClientObject
|
||||
*/
|
||||
public synchronized void releaseClient (String username)
|
||||
public synchronized void releaseClientObject (String username)
|
||||
{
|
||||
if (!_locks.remove(username)) {
|
||||
if (!_locks.remove(toKey(username))) {
|
||||
Log.warning("Requested to unlock a user that was not locked " +
|
||||
"[username=" + username + "].");
|
||||
Thread.dumpStack();
|
||||
@@ -268,8 +243,8 @@ public class ClientManager implements ConnectionObserver
|
||||
Credentials creds = req.getCredentials();
|
||||
String username = creds.getUsername();
|
||||
|
||||
// see if there's a client already registered with this username
|
||||
PresentsClient client = (PresentsClient)_usermap.get(username);
|
||||
// see if a client is already registered with these credentials
|
||||
PresentsClient client = (PresentsClient)_usermap.get(creds);
|
||||
|
||||
if (client != null) {
|
||||
Log.info("Session resumed [username=" + username +
|
||||
@@ -283,13 +258,10 @@ public class ClientManager implements ConnectionObserver
|
||||
try {
|
||||
// create a client and start up its session
|
||||
client = (PresentsClient)_clientClass.newInstance();
|
||||
client.startSession(this, username, conn);
|
||||
|
||||
// lock this client for the duration of this session
|
||||
lockClient(username);
|
||||
client.startSession(this, creds, conn);
|
||||
|
||||
// map their client instance
|
||||
_usermap.put(username, client);
|
||||
_usermap.put(creds, client);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.warning("Failed to instantiate client instance to " +
|
||||
@@ -350,9 +322,11 @@ public class ClientManager implements ConnectionObserver
|
||||
*/
|
||||
synchronized void clientDidEndSession (PresentsClient client)
|
||||
{
|
||||
Credentials creds = client.getCredentials();
|
||||
String username = client.getUsername();
|
||||
|
||||
// remove the client from the username map
|
||||
PresentsClient rc = (PresentsClient)_usermap.remove(username);
|
||||
PresentsClient rc = (PresentsClient)_usermap.remove(creds);
|
||||
|
||||
// sanity check just because we can
|
||||
if (rc == null) {
|
||||
@@ -372,9 +346,7 @@ public class ClientManager implements ConnectionObserver
|
||||
Log.info("Ending session [client=" + client + "].");
|
||||
}
|
||||
|
||||
// release the client session
|
||||
releaseClient(username);
|
||||
// and unmap (and destroy) their client object
|
||||
// unmap (and destroy) the client object
|
||||
unmapClientObject(username);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: PresentsClient.java,v 1.37 2002/09/18 01:10:27 shaper Exp $
|
||||
// $Id: PresentsClient.java,v 1.38 2002/09/19 23:36:59 mdb Exp $
|
||||
|
||||
package com.threerings.presents.server;
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.threerings.presents.dobj.Subscriber;
|
||||
|
||||
import com.threerings.presents.net.BootstrapData;
|
||||
import com.threerings.presents.net.BootstrapNotification;
|
||||
import com.threerings.presents.net.Credentials;
|
||||
import com.threerings.presents.net.EventNotification;
|
||||
import com.threerings.presents.net.UpstreamMessage;
|
||||
|
||||
@@ -54,6 +55,32 @@ public class PresentsClient
|
||||
implements Subscriber, EventListener, MessageHandler,
|
||||
ClientResolutionListener
|
||||
{
|
||||
/** Used by {@link #setUsername} to report success or failure. */
|
||||
public static interface UserChangeListener
|
||||
{
|
||||
/** Called when the new client object has been resolved and the
|
||||
* new client object reported to the client, but the old one has
|
||||
* not yet been destroyed. Any events delivered on this callback
|
||||
* to the old client object will be delivered. */
|
||||
public void changeReported (ClientObject newObj);
|
||||
|
||||
/** Called when the user change is completed, the old client
|
||||
* object is destroyed and all updates are committed. */
|
||||
public void changeCompleted (ClientObject newObj);
|
||||
|
||||
/** Called if some failure occurs during the user change
|
||||
* process. */
|
||||
public void changeFailed (Exception cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the credentials used to authenticate this client.
|
||||
*/
|
||||
public Credentials getCredentials ()
|
||||
{
|
||||
return _creds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the username with which this client instance is associated.
|
||||
*/
|
||||
@@ -79,17 +106,58 @@ public class PresentsClient
|
||||
* services when the user logs on, but anything else that has had its
|
||||
* grubby mits on the username will be left to its own devices, hence
|
||||
* the care that must be exercised when using this method.
|
||||
*
|
||||
* @param ucl an entity that will (optionally) be notified when the
|
||||
* username conversion process is complete.
|
||||
*/
|
||||
public void setUsername (String username)
|
||||
public void setUsername (String username, final UserChangeListener ucl)
|
||||
{
|
||||
// remap the client object in the client manager
|
||||
if (_cmgr.remapClient(_username, username)) {
|
||||
// change our internal business
|
||||
_username = username;
|
||||
} else {
|
||||
Log.warning("Unable to remap username [client=" + this +
|
||||
", newname=" + username + "].");
|
||||
}
|
||||
ClientResolutionListener clr = new ClientResolutionListener() {
|
||||
public void clientResolved (String username, ClientObject clobj) {
|
||||
// let the client know that the rug has been yanked out
|
||||
// from under their ass
|
||||
Object[] args = new Object[] { new Integer(clobj.getOid()) };
|
||||
_clobj.postMessage(ClientObject.CLOBJ_CHANGED, args);
|
||||
|
||||
// let the caller know that we've got some new business
|
||||
if (ucl != null) {
|
||||
ucl.changeReported(clobj);
|
||||
}
|
||||
|
||||
// release our old client object
|
||||
_cmgr.releaseClientObject(_username);
|
||||
|
||||
// unmap the old client object; this will destroy it and
|
||||
// clear out our username mapping
|
||||
_cmgr.unmapClientObject(_username);
|
||||
|
||||
// update our internal fields
|
||||
_username = username;
|
||||
_clobj = clobj;
|
||||
|
||||
// lock our new client object
|
||||
_cmgr.lockClientObject(_username);
|
||||
|
||||
// let our listener know we're groovy
|
||||
if (ucl != null) {
|
||||
ucl.changeCompleted(_clobj);
|
||||
}
|
||||
}
|
||||
|
||||
public void resolutionFailed (String username, Exception reason) {
|
||||
Log.warning("Unable to resolve new client object " +
|
||||
"[oldname=" + _username + ", newname=" + username +
|
||||
", reason=" + reason + "].");
|
||||
|
||||
// let our listener know we're hosed
|
||||
if (ucl != null) {
|
||||
ucl.changeFailed(reason);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// resolve the new client object
|
||||
_cmgr.resolveClientObject(username, clr);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,14 +173,34 @@ public class PresentsClient
|
||||
* connection instance and client object and begins a client session.
|
||||
*/
|
||||
protected void startSession (
|
||||
ClientManager cmgr, String username, Connection conn)
|
||||
ClientManager cmgr, Credentials creds, Connection conn)
|
||||
{
|
||||
_cmgr = cmgr;
|
||||
_username = username;
|
||||
_creds = creds;
|
||||
setConnection(conn);
|
||||
|
||||
// obtain our starting username
|
||||
assignStartingUsername();
|
||||
|
||||
// obtain a lock for our username->client object mapping while we
|
||||
// have an active session
|
||||
cmgr.lockClientObject(_username);
|
||||
|
||||
// resolve our client object before we get fully underway
|
||||
cmgr.resolveClientObject(username, this);
|
||||
cmgr.resolveClientObject(_username, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is factored out to allow derived classes to use a different
|
||||
* starting username than the one supplied in the user's credentials.
|
||||
* Generally one only wants to munge the starting username if the user
|
||||
* will subsequently choose a "screen name" and it is desirable to
|
||||
* avoid collision between the authentication user namespace and the
|
||||
* screen namespace.
|
||||
*/
|
||||
protected void assignStartingUsername ()
|
||||
{
|
||||
_username = _creds.getUsername();
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
@@ -307,6 +395,9 @@ public class PresentsClient
|
||||
// about to generate
|
||||
clearSubscrips();
|
||||
|
||||
// release our locked client object
|
||||
_cmgr.releaseClientObject(_username);
|
||||
|
||||
// then let the client manager know what's up (it will take care
|
||||
// of destroying our client object for us)
|
||||
_cmgr.clientDidEndSession(this);
|
||||
@@ -616,6 +707,7 @@ public class PresentsClient
|
||||
}
|
||||
|
||||
protected ClientManager _cmgr;
|
||||
protected Credentials _creds;
|
||||
protected String _username;
|
||||
protected Connection _conn;
|
||||
protected ClientObject _clobj;
|
||||
|
||||
Reference in New Issue
Block a user