diff --git a/src/java/com/threerings/parlor/Log.java b/src/java/com/threerings/parlor/Log.java new file mode 100644 index 000000000..3ab0583fc --- /dev/null +++ b/src/java/com/threerings/parlor/Log.java @@ -0,0 +1,38 @@ +// +// $Id: Log.java,v 1.1 2001/10/01 02:56:35 mdb Exp $ + +package com.threerings.parlor; + +/** + * A placeholder class that contains a reference to the log object used by + * the Parlor services. + */ +public class Log +{ + public static com.samskivert.util.Log log = + new com.samskivert.util.Log("parlor"); + + /** Convenience function. */ + public static void debug (String message) + { + log.debug(message); + } + + /** Convenience function. */ + public static void info (String message) + { + log.info(message); + } + + /** Convenience function. */ + public static void warning (String message) + { + log.warning(message); + } + + /** Convenience function. */ + public static void logStackTrace (Throwable t) + { + log.logStackTrace(com.samskivert.util.Log.WARNING, t); + } +} diff --git a/src/java/com/threerings/parlor/client/InvitationResponseObserver.java b/src/java/com/threerings/parlor/client/InvitationResponseObserver.java new file mode 100644 index 000000000..9205e5454 --- /dev/null +++ b/src/java/com/threerings/parlor/client/InvitationResponseObserver.java @@ -0,0 +1,37 @@ +// +// $Id: InvitationResponseObserver.java,v 1.1 2001/10/01 02:56:35 mdb Exp $ + +package com.threerings.parlor.client; + +/** + * A client entity that wishes to generate invitations for games must + * implement this interface. An invitation can be accepted, rejected or + * countered. A countered invitation is one where the game configuration + * is adjusted by the invited player and proposed back to the inviting + * player. + */ +public interface InvitationObserver +{ + /** + * Called if the invitation was accepted. + */ + public void invitationAccepted (int inviteId); + + /** + * Called if the invitation was rejected. + * + * @param message a message provided by the rejecting user explaining + * the reason for their rejection, or the empty string if no message + * was provided. + */ + public void invitationRejected (int inviteId, String message); + + /** + * Called if the invitation was countered with an alternate game + * configuration. + * + * @param config the game configuration proposed by the invited + * player. + */ + public void invitationCountered (int inviteId, GameConfig config); +} diff --git a/src/java/com/threerings/parlor/client/ParlorDirector.java b/src/java/com/threerings/parlor/client/ParlorDirector.java new file mode 100644 index 000000000..8bceafd3e --- /dev/null +++ b/src/java/com/threerings/parlor/client/ParlorDirector.java @@ -0,0 +1,183 @@ +// +// $Id: ParlorDirector.java,v 1.1 2001/10/01 02:56:35 mdb Exp $ + +package com.threerings.parlor.client; + +import com.samskivert.util.HashIntMap; + +import com.threerings.parlor.Log; +import com.threerings.parlor.data.GameConfig; +import com.threerings.parlor.util.ParlorContext; + +/** + * The parlor director manages the client side of the game configuration + * and matchmaking processes. It is also the entity that is listening for + * game start notifications which it then dispatches the client entity + * that will actually create and display the user interface for the game + * that started. + */ +public class ParlorDirector +{ + /** + * Requests that the named user be invited to a game described by the + * supplied game config. + * + * @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. + * + * @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) + { + // generate the invocation service request + int invid = ParlorService.invite( + 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; + } + + public void counter (ParlorContext ctx, int inviteId, + GameConfig config, InvitationObserver observer) + { + } + + /** + * Called by the invocation services when another user has invited us + * to play a game. + * + * @param remoteId the unique indentifier for this invitation (used + * when countering or responding). + * @param inviter the username of the inviting user. + * @param config the configuration information for the game to which + * we've been invited. + */ + public void handleInviteNotification ( + int remoteId, String inviter, GameConfig config) + { + } + + /** + * Called by the invocation services when another user has responded + * to our invitation. + * + * @param remoteId the unique indentifier for the invitation. + * @param code the response code {@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. + */ + public void handleResponseNotification ( + int remoteId, int code, String message) + { + } + + /** + * Called by the invocation services when another user has invited us + * to play a game. + * + * @param inviter the username of the inviting user. + * @param config the configuration information for the game to which + * we've been invited. + */ + public void handleInviteNotification (String inviter, GameConfig config) + { + } + + /** + * Called by the invocation services when an invitation request was + * received by the server and delivered to the intended invitee. + * + * @param invid the invocation id of the invitation request. + */ + public void handleInviteReceived (int invid, int remoteId) + { + // remove the invitation record from the submitted table and put + // it in the pending table + Invitation invite = (Invitation)_submittedInvites.get(invid); + if (invite == null) { + Log.warning("Received accepted notification for non-existent " + + "invitation request!? [invid=" + invid + + ", remoteId=" + remoteId + "]."); + return; + } + + // 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); + } + + /** + * Called by the invocation services when an invitation request failed + * or was rejected for some reason. + * + * @param invid the invocation id of the invitation request. + * @param reason a reason code explaining the rejection or failure. + */ + public void handleInviteFailed (int invid, String reason) + { + } + + protected static class Invitation + { + /** A unique id for this invitation assigned on the client which + * is only used to allow the invitation observers to discriminate + * between multiple outstanding invitations. We'd use the remote + * id here except that that is not known until we receive the + * 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++; + + /** 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 configuration of the game to be created. */ + public GameConfig config; + + /** The invitation observer that created this invitation. */ + public InvitationObserver observer; + + /** A flag indicating that we were requested to abort this + * invitation before we even heard back with an acknowledgement + * that it was received by the server. */ + public boolean aborted = false; + + /** Constructs a new invitation request. */ + public Invitation (String invitee, GameConfig config, + InvitationObserver observer) + { + this.invitee = invitee; + this.config = config; + this.observer = observer; + } + } + + /** A table of submitted (but not acknowledged) invitation requests, + * keyed on invocation request id. */ + protected HashIntMap _submittedInvites = new HashIntMap(); + + /** A table of acknowledged (but not yet accepted or refused) + * invitation requests, keyed on invitation id. */ + protected HashIntMap _pendingInvites = new HashIntMap(); + + /** A counter used to assign a unique id to every invitation. */ + protected static int _localInvitationId = 0; +} diff --git a/src/java/com/threerings/parlor/client/ParlorService.java b/src/java/com/threerings/parlor/client/ParlorService.java new file mode 100644 index 000000000..8c3bf302c --- /dev/null +++ b/src/java/com/threerings/parlor/client/ParlorService.java @@ -0,0 +1,82 @@ +// +// $Id: ParlorService.java,v 1.1 2001/10/01 02:56:35 mdb Exp $ + +package com.threerings.parlor.client; + +import com.threerings.cocktail.cher.client.Client; +import com.threerings.cocktail.cher.client.InvocationManager; + +import com.threerings.parlor.Log; +import com.threerings.parlor.data.GameConfig; + +/** + * This class provides an interface to the various parlor services that + * are directly invokable by the client (by means of the invocation + * services). Presently these services are limited to the various + * matchmaking mechanisms. It is unlikely that client code will want to + * make direct use of this class, instead they would make use of the + * programmatic interface provided by the {@link ParlorDirector}. + * + * @see ParlorDirector + */ +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 notification. */ + public static final String INVITE_NOTIFICATION = "Invite"; + + /** 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 response code for an accepted invitation. */ + public static final int INVITATION_ACCEPTED = 0; + + /** 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"; + + /** + * You probably don't want to call this directly, but want to generate + * your invitation request via {@link ParlorDirector#invite}. Requests + * that an invitation be delivered to the named user, requesting that + * they join the inviting user in a game, the details of which are + * specified in the supplied game config object. + * + * @param client a connected, operational client instance. + * @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. + * + * @return the invocation request id of the generated request. + */ + public static int invite (Client client, String invitee, + GameConfig config, Object rsptarget) + { + InvocationManager invmgr = client.getInvocationManager(); + Object[] args = new Object[] { invitee, config }; + Log.info("Sending invite request [to=" + invitee + + ", cfg=" + config + "]."); + return invmgr.invoke(MODULE, INVITE_REQUEST, args, rsptarget); + } +} diff --git a/src/java/com/threerings/parlor/game/GameConfig.java b/src/java/com/threerings/parlor/game/GameConfig.java new file mode 100644 index 000000000..6ef02d6c6 --- /dev/null +++ b/src/java/com/threerings/parlor/game/GameConfig.java @@ -0,0 +1,44 @@ +// +// $Id: GameConfig.java,v 1.1 2001/10/01 02:56:35 mdb Exp $ + +package com.threerings.parlor.data; + +import java.io.IOException; +import java.io.DataInputStream; +import java.io.DataOutputStream; + +import com.threerings.cocktail.cher.io.Streamable; + +/** + * The game config class encapsulates the configuration information for a + * particular type of game. The hierarchy of game config objects mimics + * the hierarchy of game managers and the client provides a game config + * object to the game manager at game creation time which is used to set + * the game's configuration options. + * + *

A game that has specific configuration needs would extend this + * class (or an appropriate subclass) adding it's configuration + * information, and also overriding {@link #writeTo} and {@link #readFrom} + * to provide code to effect the necessary serialization and + * unserialization of the added fields. + * + *

The implementation of the client-side of the game can either use + * the code provided for obtaining configuration parameters from the user, + * or provide its own specialized configuration interface. It need only + * produce a game config object when the configuration process is complete + * and provide that to the server along with the game creation request. + */ +public class GameConfig implements Streamable +{ + // documentation inherited + public void writeTo (DataOutputStream out) + throws IOException + { + } + + // documentation inherited + public void readFrom (DataInputStream in) + throws IOException + { + } +} diff --git a/src/java/com/threerings/parlor/game/GameManager.java b/src/java/com/threerings/parlor/game/GameManager.java new file mode 100644 index 000000000..0579c7384 --- /dev/null +++ b/src/java/com/threerings/parlor/game/GameManager.java @@ -0,0 +1,16 @@ +// +// $Id: GameManager.java,v 1.1 2001/10/01 02:56:35 mdb Exp $ + +package com.threerings.parlor.server; + +import java.util.Properties; + +import com.threerings.cocktail.party.server.PlaceManager; +import com.threerings.cocktail.party.server.PlaceRegistry; + +public class GameManager extends PlaceManager +{ + public void init (PlaceRegistry registry, Properties config) + { + } +} diff --git a/src/java/com/threerings/parlor/game/GameObject.dobj b/src/java/com/threerings/parlor/game/GameObject.dobj new file mode 100644 index 000000000..0993741aa --- /dev/null +++ b/src/java/com/threerings/parlor/game/GameObject.dobj @@ -0,0 +1,22 @@ +// +// $Id: GameObject.dobj,v 1.1 2001/10/01 02:56:35 mdb Exp $ + +package com.threerings.parlor.data; + +import com.threerings.cocktail.party.data.PlaceObject; + +/** + * A game object hosts the shared data associated with a game played by + * one or more players. The game object extends the place object so that + * the game can act as a place where players actually go when playing the + * game. Only very basic information is maintained in the base game + * object. It serves as the base for a hierarchy of game object + * derivatives that handle basic gameplay for a suite of different game + * types (ie. turn based games, party games, board games, card games, + * etc.). + */ +public class GameObject extends PlaceObject +{ + /** Indicates whether or not this game is rated. */ + public boolean isRated; +} diff --git a/src/java/com/threerings/parlor/server/ParlorManager.java b/src/java/com/threerings/parlor/server/ParlorManager.java new file mode 100644 index 000000000..10cfb91fa --- /dev/null +++ b/src/java/com/threerings/parlor/server/ParlorManager.java @@ -0,0 +1,35 @@ +// +// $Id: ParlorManager.java,v 1.1 2001/10/01 02:56:35 mdb Exp $ + +package com.threerings.parlor.server; + +import com.samskivert.util.Config; + +import com.threerings.cocktail.cher.server.InvocationManager; +import com.threerings.parlor.client.ParlorService; + +/** + * The parlor manager is responsible for the parlor services in + * aggregate. This includes maintaining the registry of active games, + * handling the necessary coordination for the matchmaking services and + * anything else that falls outside the scope of an actual in-progress + * game. + */ +public class ParlorManager +{ + /** + * Initializes the parlor manager. This should be called by the server + * that is making use of the parlor services on the single instance of + * parlor manager that it has created. + * + * @param config the configuration object in use by this server. + * @param invmgr a reference to the invocation manager in use by this + * server. + */ + public void init (Config config, InvocationManager invmgr) + { + // register our invocation provider + ParlorProvider pprov = new ParlorProvider(this); + invmgr.registerProvider(ParlorService.MODULE, pprov); + } +} diff --git a/src/java/com/threerings/parlor/server/ParlorProvider.java b/src/java/com/threerings/parlor/server/ParlorProvider.java new file mode 100644 index 000000000..1c5a1600d --- /dev/null +++ b/src/java/com/threerings/parlor/server/ParlorProvider.java @@ -0,0 +1,43 @@ +// +// $Id: ParlorProvider.java,v 1.1 2001/10/01 02:56:35 mdb Exp $ + +package com.threerings.parlor.server; + +import com.threerings.cocktail.cher.server.InvocationProvider; +import com.threerings.cocktail.party.data.BodyObject; + +import com.threerings.parlor.data.GameConfig; + +/** + * The parlor provider handles the server side of the various Parlor + * services that are made available for direct invocation by the client. + * Primarily these are the matchmaking mechanisms. + */ +public class ParlorProvider extends InvocationProvider +{ + /** + * Constructs a parlor provider instance which will be used to handle + * all parlor-related invocation service requests. This is + * automatically taken care of by the parlor manager, so no other + * entity need instantiate and register a parlor provider. + * + * @param pmgr a reference to the parlor manager active in this + * server. + */ + public ParlorProvider (ParlorManager pmgr) + { + _pmgr = pmgr; + } + + /** + * Processes a request from the client to invite another user to play + * a game. + */ + public void handleInviteRequest ( + BodyObject source, int invid, String invitee, GameConfig config) + { + } + + /** A reference to the parlor manager we're working with. */ + protected ParlorManager _pmgr; +} diff --git a/src/java/com/threerings/parlor/util/ParlorContext.java b/src/java/com/threerings/parlor/util/ParlorContext.java new file mode 100644 index 000000000..6c0328718 --- /dev/null +++ b/src/java/com/threerings/parlor/util/ParlorContext.java @@ -0,0 +1,19 @@ +// +// $Id: ParlorContext.java,v 1.1 2001/10/01 02:56:35 mdb Exp $ + +package com.threerings.parlor.util; + +import com.threerings.cocktail.party.util.PartyContext; +import com.threerings.parlor.client.ParlorDirector; + +/** + * The parlor context provides access to the various managers, etc. that + * are needed by the parlor client code. + */ +public interface ParlorContext extends PartyContext +{ + /** + * Returns a reference to the parlor director. + */ + public ParlorDirector getParlorDirector (); +}