More progress on parlor services (mostly invitation handling).

git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@365 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Michael Bayne
2001-10-01 05:07:13 +00:00
parent 27adf03bc1
commit 23ccfde6c8
5 changed files with 344 additions and 59 deletions
@@ -0,0 +1,33 @@
//
// $Id: InvitationHandler.java,v 1.1 2001/10/01 05:07:13 mdb Exp $
package com.threerings.parlor.client;
import com.threerings.parlor.data.GameConfig;
/**
* A client entity that wishes to handle invitations received by other
* clients should implement this interface and register itself with the
* parlor director. It will subsequently be notified of any incoming
* invitations. It is also responsible for handling cancelled invitations.
*/
public interface InvitationHandler
{
/**
* Called when an invitation is received from another player.
*
* @param inviteId this invitation's unique id.
* @param inviter the username of the user that sent the invitation.
* @param config the configuration of the game to which we are being
* invited.
*/
public void invitationReceived (int inviteId, String inviter,
GameConfig config);
/**
* Called when an invitation is cancelled by the inviting player.
*
* @param inviteId this invitation's unique id.
*/
public void invitationCancelled (int inviteId);
}
@@ -1,5 +1,5 @@
// //
// $Id: InvitationResponseObserver.java,v 1.2 2001/10/01 02:57:37 mdb Exp $ // $Id: InvitationResponseObserver.java,v 1.3 2001/10/01 05:07:13 mdb Exp $
package com.threerings.parlor.client; package com.threerings.parlor.client;
@@ -12,16 +12,21 @@ import com.threerings.parlor.data.GameConfig;
* is adjusted by the invited player and proposed back to the inviting * is adjusted by the invited player and proposed back to the inviting
* player. * player.
*/ */
public interface InvitationObserver public interface InvitationResponseObserver
{ {
/** /**
* Called if the invitation was accepted. * Called if the invitation was accepted.
*
* @param inviteId the unique id of the invitation for which we
* received a response.
*/ */
public void invitationAccepted (int inviteId); public void invitationAccepted (int inviteId);
/** /**
* Called if the invitation was rejected. * Called if the invitation was rejected.
* *
* @param inviteId the unique id of the invitation for which we
* received a response.
* @param message a message provided by the rejecting user explaining * @param message a message provided by the rejecting user explaining
* the reason for their rejection, or the empty string if no message * the reason for their rejection, or the empty string if no message
* was provided. * was provided.
@@ -32,6 +37,8 @@ public interface InvitationObserver
* Called if the invitation was countered with an alternate game * Called if the invitation was countered with an alternate game
* configuration. * configuration.
* *
* @param inviteId the unique id of the invitation for which we
* received a response.
* @param config the game configuration proposed by the invited * @param config the game configuration proposed by the invited
* player. * player.
*/ */
@@ -1,8 +1,10 @@
// //
// $Id: ParlorDirector.java,v 1.1 2001/10/01 02:56:35 mdb Exp $ // $Id: ParlorDirector.java,v 1.2 2001/10/01 05:07:13 mdb Exp $
package com.threerings.parlor.client; package com.threerings.parlor.client;
import java.util.ArrayList;
import java.util.Iterator;
import com.samskivert.util.HashIntMap; import com.samskivert.util.HashIntMap;
import com.threerings.parlor.Log; import com.threerings.parlor.Log;
@@ -18,6 +20,32 @@ import com.threerings.parlor.util.ParlorContext;
*/ */
public class ParlorDirector public class ParlorDirector
{ {
/**
* Constructs a parlor director and provides it with the parlor
* context that it can use to access the client services that it needs
* to provide its own services. Only one parlor director should be
* active in the client at one time and it should be made available
* via the parlor context.
*
* @param ctx the parlor context in use by the client.
*/
public ParlorDirector (ParlorContext ctx)
{
_ctx = ctx;
}
/**
* Sets the invitation handler, which is the entity that will be
* notified when we receive incoming invitation notifications and when
* invitations have been cancelled.
*
* @param handler our new invitation handler.
*/
public void setInvitationHandler (InvitationHandler handler)
{
_handler = handler;
}
/** /**
* Requests that the named user be invited to a game described by the * Requests that the named user be invited to a game described by the
* supplied game config. * supplied game config.
@@ -25,29 +53,89 @@ public class ParlorDirector
* @param invitee the user to invite. * @param invitee the user to invite.
* @param config the configuration of the game to which the user is * @param config the configuration of the game to which the user is
* being invited. * being invited.
* @param observer the entity that wants to know about accepted, * @param observer the entity that will be notified if this invitation
* rejected or countered invitations. * is accepted, rejected or countered.
* *
* @return a unique id associated with this invitation that can be * @return a unique id associated with this invitation that can be
* used to discern between various outstanding invitations by the * used to discern between various outstanding invitations by the
* invitation observer. * invitation observer.
*/ */
public int invite (ParlorContext ctx, String invitee, public int invite (String invitee, GameConfig config,
GameConfig config, InvitationObserver observer) InvitationResponseObserver observer)
{ {
// generate the invocation service request // generate the invocation service request
int invid = ParlorService.invite( int invid = ParlorService.invite(
ctx.getClient(), invitee, config, this); _ctx.getClient(), invitee, config, this);
// create an invitation record and put it in the submitted table // create an invitation record and put it in the submitted table
Invitation invite = new Invitation(invitee, config, observer); Invitation invite = new Invitation(invitee, config, observer);
_submittedInvites.put(invid, invite); _submittedInvites.put(invid, invite);
return invite.localId; return invite.inviteId;
} }
public void counter (ParlorContext ctx, int inviteId, /**
GameConfig config, InvitationObserver observer) * Counters a received invitation with an invitation with different
* game configuration parameters.
*
* @param inviteId the id of the received invitation.
* @param config the updated game configuration.
* @param observer the entity that will be notified if this
* counter-invitation is accepted, rejected or countered.
*/
public void counter (int inviteId, GameConfig config,
InvitationResponseObserver observer)
{ {
// look up the invitation record (oh the two separate key spaces
// humanity)
Invitation invite = getInviteByLocalId(inviteId);
if (invite == null) {
// complain if we didn't find a matching invitation
Log.warning("Received request to counter non-existent " +
"invitation [inviteId=" + inviteId +
", config=" + config + "].");
return;
}
// update the invitation record with the observer (who will
// eventually be hearing back from the other client about their
// counter-invitation)
invite.observer = observer;
// generate the invocation service request
ParlorService.respond(_ctx.getClient(), invite.remoteId,
ParlorService.INVITATION_COUNTERED,
config, this);
}
/**
* Issues a request to cancel an outstanding invitation.
*
* @param inviteId the id of the invitation to cancel.
*/
public void cancel (int inviteId)
{
// look up the invitation record (oh the two separate key spaces
// humanity)
Invitation invite = getInviteByLocalId(inviteId);
if (invite == null) {
// complain if we didn't find a matching invitation
Log.warning("Received request to cancel non-existent " +
"invitation [inviteId=" + inviteId + "].");
return;
}
// if the invitation has not yet been acknowleged by the server,
// we make a note that it should be cancelled when we do receive
// the acknowlegement
if (invite.remoteId == -1) {
invite.cancelled = true;
} else {
// otherwise, generate the invocation service request
ParlorService.cancel(_ctx.getClient(), invite.remoteId, this);
// and remove it from the pending table
_pendingInvites.remove(invite.remoteId);
}
} }
/** /**
@@ -63,23 +151,59 @@ public class ParlorDirector
public void handleInviteNotification ( public void handleInviteNotification (
int remoteId, String inviter, GameConfig config) int remoteId, String inviter, GameConfig config)
{ {
// create an invitation record for this invitation
Invitation invite = new Invitation(inviter, config, null);
invite.remoteId = remoteId;
// put it in the pending invitations table
_pendingInvites.put(remoteId, invite);
try {
// notify the invitation handler of the incoming invitation
_handler.invitationReceived(invite.inviteId, inviter, config);
} catch (Exception e) {
Log.warning("Invitation handler choked on invite " +
"notification [inviteId=" + invite.inviteId +
", inviter=" + inviter +
", config=" + config + "].");
Log.logStackTrace(e);
}
} }
/** /**
* Called by the invocation services when another user has responded * Called by the invocation services when another user has responded
* to our invitation. * to our invitation by either accepting, rejecting or countering it.
* *
* @param remoteId the unique indentifier for the invitation. * @param remoteId the unique indentifier for the invitation.
* @param code the response code {@link * @param code the response code, either {@link
* ParlorService#INVITATION_ACCEPTED} or {@link * ParlorService#INVITATION_ACCEPTED} or {@link
* ParlorService#INVITATION_REJECTED}. * ParlorService#INVITATION_REJECTED} or {@link
* @param message in the case of a rejected invitation, a message * ParlorService#INVITATION_COUNTERED}.
* provided by the invited user explaining the rejection. The empty * @param arg in the case of a rejected invitation, a string
* string if no explanation was provided. * containing a message provided by the invited user explaining the
* reason for rejection (the empty string if no explanation was
* provided). In the case of a countered invitation, a new game config
* object with the modified game configuration.
*/ */
public void handleResponseNotification ( public void handleRespondInviteNotification (
int remoteId, int code, String message) int remoteId, int code, Object arg)
{ {
// look up the invitation record for this invitation
Invitation invite = (Invitation)_pendingInvites.get(remoteId);
if (invite == null) {
Log.warning("Have no record of invitation for which we " +
"received a response?! [remoteId=" + remoteId +
", code=" + code + ", arg=" + arg + "].");
return;
}
// make sure we have an observer to notify
if (invite.observer == null) {
Log.warning("No observer registered for invitation " +
invite + ".");
return;
}
} }
/** /**
@@ -90,7 +214,7 @@ public class ParlorDirector
* @param config the configuration information for the game to which * @param config the configuration information for the game to which
* we've been invited. * we've been invited.
*/ */
public void handleInviteNotification (String inviter, GameConfig config) public void handleCancelInviteNotification (String inviter, GameConfig config)
{ {
} }
@@ -104,7 +228,7 @@ public class ParlorDirector
{ {
// remove the invitation record from the submitted table and put // remove the invitation record from the submitted table and put
// it in the pending table // it in the pending table
Invitation invite = (Invitation)_submittedInvites.get(invid); Invitation invite = (Invitation)_submittedInvites.remove(invid);
if (invite == null) { if (invite == null) {
Log.warning("Received accepted notification for non-existent " + Log.warning("Received accepted notification for non-existent " +
"invitation request!? [invid=" + invid + "invitation request!? [invid=" + invid +
@@ -115,8 +239,16 @@ public class ParlorDirector
// now that we know the invitation's unique id, keep track of it // now that we know the invitation's unique id, keep track of it
invite.remoteId = remoteId; invite.remoteId = remoteId;
// and put it in the new table // if the invitation was cancelled before we heard back about it,
_pendingInvites.put(remoteId, invite); // we need to send off a cancellation request now
if (invite.cancelled) {
// generate the invocation service request to cancel it
ParlorService.cancel(_ctx.getClient(), invite.remoteId, this);
} else {
// otherwise, put it in the new table
_pendingInvites.put(remoteId, invite);
}
} }
/** /**
@@ -130,6 +262,10 @@ public class ParlorDirector
{ {
} }
/**
* The invitation class is used to track information related to
* outstanding invitations generated by or targeted to this client.
*/
protected static class Invitation protected static class Invitation
{ {
/** A unique id for this invitation assigned on the client which /** A unique id for this invitation assigned on the client which
@@ -139,37 +275,82 @@ public class ParlorDirector
* acknowlegedment from the server and we need to provide the * acknowlegedment from the server and we need to provide the
* caller with some sort of unique id at the time the request is * caller with some sort of unique id at the time the request is
* generated. */ * generated. */
public int localId = _localInvitationId++; public int inviteId = _localInvitationId++;
/** The unique id for this invitation (as assigned by the /** The unique id for this invitation (as assigned by the
* server). This is -1 until we receive an acknowledgement from * server). This is -1 until we receive an acknowledgement from
* the server that our invitation was delivered. */ * the server that our invitation was delivered. */
public int remoteId = -1; public int remoteId = -1;
/** The name of the user that was invited. */ /** The name of the other user involved in this invitation. */
public String invitee; public String opponent;
/** The configuration of the game to be created. */ /** The configuration of the game to be created. */
public GameConfig config; public GameConfig config;
/** The invitation observer that created this invitation. */ /** The entity to notify when we receive a response for this
public InvitationObserver observer; * invitation. */
public InvitationResponseObserver observer;
/** A flag indicating that we were requested to abort this /** A flag indicating that we were requested to cancel this
* invitation before we even heard back with an acknowledgement * invitation before we even heard back with an acknowledgement
* that it was received by the server. */ * that it was received by the server. */
public boolean aborted = false; public boolean cancelled = false;
/** Constructs a new invitation request. */ /** Constructs a new invitation record. */
public Invitation (String invitee, GameConfig config, public Invitation (String opponent, GameConfig config,
InvitationObserver observer) InvitationResponseObserver observer)
{ {
this.invitee = invitee; this.opponent = opponent;
this.config = config; this.config = config;
this.observer = observer; this.observer = observer;
} }
/** Returns a string representation of this invitation record. */
public String toString ()
{
return "[inviteId=" + inviteId + ", remoteId=" + remoteId +
", opponent=" + opponent + ", config=" + config +
", observer=" + observer + ", cancelled=" + cancelled + "]";
}
} }
/**
* Looks up an invitation by its local unique identifier. Oh the dual
* uid space humanity!
*/
protected Invitation getInviteByLocalId (int inviteId)
{
// first search the pending invites which is where we're most
// likely to find it
Iterator iter = _pendingInvites.values().iterator();
while (iter.hasNext()) {
Invitation match = (Invitation)iter.next();
if (match.inviteId == inviteId) {
return match;
}
}
// if that didn't work, look in the submitted invites
iter = _submittedInvites.values().iterator();
while (iter.hasNext()) {
Invitation match = (Invitation)iter.next();
if (match.inviteId == inviteId) {
return match;
}
}
// il n'existe pas
return null;
}
/** An active parlor context. */
protected ParlorContext _ctx;
/** The entity that has registered itself to handle incoming
* invitation notifications. */
protected InvitationHandler _handler;
/** A table of submitted (but not acknowledged) invitation requests, /** A table of submitted (but not acknowledged) invitation requests,
* keyed on invocation request id. */ * keyed on invocation request id. */
protected HashIntMap _submittedInvites = new HashIntMap(); protected HashIntMap _submittedInvites = new HashIntMap();
@@ -1,5 +1,5 @@
// //
// $Id: ParlorService.java,v 1.1 2001/10/01 02:56:35 mdb Exp $ // $Id: ParlorService.java,v 1.2 2001/10/01 05:07:13 mdb Exp $
package com.threerings.parlor.client; package com.threerings.parlor.client;
@@ -24,17 +24,17 @@ public class ParlorService
/** The module name for the parlor services. */ /** The module name for the parlor services. */
public static final String MODULE = "parlor"; public static final String MODULE = "parlor";
/** The message identifier for an invitation creation request. */ /** The message identifier for an invitation creation request or
public static final String INVITE_REQUEST = "Invite"; * notification. */
public static final String INVITE_ID = "Invite";
/** The message identifier for an invitation notification. */ /** The message identifier for an invitation cancellation request or
public static final String INVITE_NOTIFICATION = "Invite"; * notification. */
public static final String CANCEL_INVITE_ID = "CancelInvite";
/** The message identifier for an invitation response request. */ /** The message identifier for an invitation response request or
public static final String RESPONSE_REQUEST = "Response"; * notification. */
public static final String RESPOND_INVITE_ID = "RespondInvite";
/** The message identifier for an invitation response notification. */
public static final String RESPONSE_NOTIFICATION = "Response";
/** The response code for an accepted invitation. */ /** The response code for an accepted invitation. */
public static final int INVITATION_ACCEPTED = 0; public static final int INVITATION_ACCEPTED = 0;
@@ -42,17 +42,8 @@ public class ParlorService
/** The response code for a rejected invitation. */ /** The response code for a rejected invitation. */
public static final int INVITATION_REJECTED = 1; public static final int INVITATION_REJECTED = 1;
/** The message identifier for an counter invitation request. */ /** The response code for a countered invitation. */
public static final String COUNTER_REQUEST = "Counter"; public static final int INVITATION_COUNTERED = 2;
/** The message identifier for an countered invitation notification. */
public static final String COUNTER_NOTIFICATION = "Counter";
/** The message identifier for an rescind invitation request. */
public static final String RESCIND_REQUEST = "Rescind";
/** The message identifier for an rescinded invitation notification. */
public static final String RESCIND_NOTIFICATION = "Rescind";
/** /**
* You probably don't want to call this directly, but want to generate * You probably don't want to call this directly, but want to generate
@@ -65,8 +56,8 @@ public class ParlorService
* @param invitee the username of the user to be invited. * @param invitee the username of the user to be invited.
* @param config a game config object detailing the type and * @param config a game config object detailing the type and
* configuration of the game to be created. * configuration of the game to be created.
* @param rsptarget the parlor director reference that will receive and * @param rsptarget the object reference that will receive and process
* process the response. * the response.
* *
* @return the invocation request id of the generated request. * @return the invocation request id of the generated request.
*/ */
@@ -77,6 +68,61 @@ public class ParlorService
Object[] args = new Object[] { invitee, config }; Object[] args = new Object[] { invitee, config };
Log.info("Sending invite request [to=" + invitee + Log.info("Sending invite request [to=" + invitee +
", cfg=" + config + "]."); ", cfg=" + config + "].");
return invmgr.invoke(MODULE, INVITE_REQUEST, args, rsptarget); return invmgr.invoke(MODULE, INVITE_ID, args, rsptarget);
}
/**
* You probably don't want to call this directly, but want to call
* {@link ParlorDirector#counter}. Requests that a counter-invitation be
* delivered with the specified parameters.
*
* @param client a connected, operational client instance.
* @param inviteId the unique id previously assigned by the server to
* this invitation.
* @param code the response code to use in responding to the
* invitation.
* @param arg the argument associated with the response (a string
* message from the player explaining why the response was refused in
* the case of an invitation refusal or an updated game configuration
* object in the case of a counter-invitation, or null in the case of
* an accepted invitation).
* @param rsptarget the object reference that will receive and process
* the response.
*
* @return the invocation request id of the generated request.
*/
public static int respond (Client client, int inviteId, int code,
Object arg, Object rsptarget)
{
InvocationManager invmgr = client.getInvocationManager();
Object[] args = new Object[] {
new Integer(inviteId), new Integer(code), null };
// we can't have a null argument so we use the empty string
args[2] = (arg == null) ? "" : arg;
Log.info("Sending invitation response [inviteId=" + inviteId +
", code=" + code + ", arg=" + arg + "].");
return invmgr.invoke(MODULE, RESPOND_INVITE_ID, args, rsptarget);
}
/**
* You probably don't want to call this directly, but want to call
* {@link ParlorDirector#cancel}. Requests that an outstanding
* invitation be cancelled.
*
* @param client a connected, operational client instance.
* @param inviteId the unique id previously assigned by the server to
* this invitation.
* @param rsptarget the object reference that will receive and process
* the response.
*
* @return the invocation request id of the generated request.
*/
public static int cancel (Client client, int inviteId, Object rsptarget)
{
InvocationManager invmgr = client.getInvocationManager();
Object[] args = new Object[] { new Integer(inviteId) };
Log.info("Sending invitation cancellation " +
"[inviteId=" + inviteId + "].");
return invmgr.invoke(MODULE, CANCEL_INVITE_ID, args, rsptarget);
} }
} }
@@ -1,5 +1,5 @@
// //
// $Id: ParlorProvider.java,v 1.1 2001/10/01 02:56:35 mdb Exp $ // $Id: ParlorProvider.java,v 1.2 2001/10/01 05:07:13 mdb Exp $
package com.threerings.parlor.server; package com.threerings.parlor.server;
@@ -38,6 +38,24 @@ public class ParlorProvider extends InvocationProvider
{ {
} }
/**
* Processes a request from the client to respond to an outstanding
* invitation by accepting, refusing, or countering it.
*/
public void handleRepsondInviteRequest (
BodyObject source, int invid, int inviteId, int code, Object arg)
{
}
/**
* Processes a request from the client to cancel an outstanding
* invitation.
*/
public void handleCancelInviteRequest (
BodyObject source, int invid, int inviteId)
{
}
/** A reference to the parlor manager we're working with. */ /** A reference to the parlor manager we're working with. */
protected ParlorManager _pmgr; protected ParlorManager _pmgr;
} }