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;
@@ -12,16 +12,21 @@ import com.threerings.parlor.data.GameConfig;
* is adjusted by the invited player and proposed back to the inviting
* player.
*/
public interface InvitationObserver
public interface InvitationResponseObserver
{
/**
* 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);
/**
* 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
* the reason for their rejection, or the empty string if no message
* was provided.
@@ -32,6 +37,8 @@ public interface InvitationObserver
* Called if the invitation was countered with an alternate game
* configuration.
*
* @param inviteId the unique id of the invitation for which we
* received a response.
* @param config the game configuration proposed by the invited
* 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;
import java.util.ArrayList;
import java.util.Iterator;
import com.samskivert.util.HashIntMap;
import com.threerings.parlor.Log;
@@ -18,6 +20,32 @@ import com.threerings.parlor.util.ParlorContext;
*/
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
* supplied game config.
@@ -25,29 +53,89 @@ public class ParlorDirector
* @param invitee the user to invite.
* @param config the configuration of the game to which the user is
* being invited.
* @param observer the entity that wants to know about accepted,
* rejected or countered invitations.
* @param observer the entity that will be notified if this invitation
* is accepted, rejected or countered.
*
* @return a unique id associated with this invitation that can be
* used to discern between various outstanding invitations by the
* invitation observer.
*/
public int invite (ParlorContext ctx, String invitee,
GameConfig config, InvitationObserver observer)
public int invite (String invitee, GameConfig config,
InvitationResponseObserver observer)
{
// generate the invocation service request
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
Invitation invite = new Invitation(invitee, config, observer);
_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 (
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
* to our invitation.
* to our invitation by either accepting, rejecting or countering it.
*
* @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_REJECTED}.
* @param message in the case of a rejected invitation, a message
* provided by the invited user explaining the rejection. The empty
* string if no explanation was provided.
* ParlorService#INVITATION_REJECTED} or {@link
* ParlorService#INVITATION_COUNTERED}.
* @param arg in the case of a rejected invitation, a string
* 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 (
int remoteId, int code, String message)
public void handleRespondInviteNotification (
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
* 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
// it in the pending table
Invitation invite = (Invitation)_submittedInvites.get(invid);
Invitation invite = (Invitation)_submittedInvites.remove(invid);
if (invite == null) {
Log.warning("Received accepted notification for non-existent " +
"invitation request!? [invid=" + invid +
@@ -115,8 +239,16 @@ public class ParlorDirector
// now that we know the invitation's unique id, keep track of it
invite.remoteId = remoteId;
// and put it in the new table
_pendingInvites.put(remoteId, invite);
// if the invitation was cancelled before we heard back about it,
// 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
{
/** 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
* caller with some sort of unique id at the time the request is
* generated. */
public int localId = _localInvitationId++;
public int inviteId = _localInvitationId++;
/** The unique id for this invitation (as assigned by the
* server). This is -1 until we receive an acknowledgement from
* the server that our invitation was delivered. */
public int remoteId = -1;
/** The name of the user that was invited. */
public String invitee;
/** The name of the other user involved in this invitation. */
public String opponent;
/** The configuration of the game to be created. */
public GameConfig config;
/** The invitation observer that created this invitation. */
public InvitationObserver observer;
/** The entity to notify when we receive a response for this
* 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
* that it was received by the server. */
public boolean aborted = false;
public boolean cancelled = false;
/** Constructs a new invitation request. */
public Invitation (String invitee, GameConfig config,
InvitationObserver observer)
/** Constructs a new invitation record. */
public Invitation (String opponent, GameConfig config,
InvitationResponseObserver observer)
{
this.invitee = invitee;
this.opponent = opponent;
this.config = config;
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,
* keyed on invocation request id. */
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;
@@ -24,17 +24,17 @@ public class ParlorService
/** The module name for the parlor services. */
public static final String MODULE = "parlor";
/** The message identifier for an invitation creation request. */
public static final String INVITE_REQUEST = "Invite";
/** The message identifier for an invitation creation request or
* notification. */
public static final String INVITE_ID = "Invite";
/** The message identifier for an invitation notification. */
public static final String INVITE_NOTIFICATION = "Invite";
/** The message identifier for an invitation cancellation request or
* notification. */
public static final String CANCEL_INVITE_ID = "CancelInvite";
/** The message identifier for an invitation response request. */
public static final String RESPONSE_REQUEST = "Response";
/** The message identifier for an invitation response notification. */
public static final String RESPONSE_NOTIFICATION = "Response";
/** The message identifier for an invitation response request or
* notification. */
public static final String RESPOND_INVITE_ID = "RespondInvite";
/** The response code for an accepted invitation. */
public static final int INVITATION_ACCEPTED = 0;
@@ -42,17 +42,8 @@ public class ParlorService
/** The response code for a rejected invitation. */
public static final int INVITATION_REJECTED = 1;
/** The message identifier for an counter invitation request. */
public static final String COUNTER_REQUEST = "Counter";
/** 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";
/** The response code for a countered invitation. */
public static final int INVITATION_COUNTERED = 2;
/**
* 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 config a game config object detailing the type and
* configuration of the game to be created.
* @param rsptarget the parlor director reference that will receive and
* process the response.
* @param rsptarget the object reference that will receive and process
* the response.
*
* @return the invocation request id of the generated request.
*/
@@ -77,6 +68,61 @@ public class ParlorService
Object[] args = new Object[] { invitee, config };
Log.info("Sending invite request [to=" + invitee +
", 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;
@@ -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. */
protected ParlorManager _pmgr;
}