Back-end facilities for table matchmaking.
git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@508 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// $Id: ParlorCodes.java,v 1.6 2001/10/19 02:04:29 mdb Exp $
|
// $Id: ParlorCodes.java,v 1.7 2001/10/19 22:02:36 mdb Exp $
|
||||||
|
|
||||||
package com.threerings.parlor.client;
|
package com.threerings.parlor.client;
|
||||||
|
|
||||||
@@ -84,4 +84,18 @@ public interface ParlorCodes extends InvocationCodes
|
|||||||
* by the invocation services to a call to {@link
|
* by the invocation services to a call to {@link
|
||||||
* ParlorDirector#handleJoinFailed}. */
|
* ParlorDirector#handleJoinFailed}. */
|
||||||
public static final String JOIN_FAILED_RESPONSE = "JoinFailed";
|
public static final String JOIN_FAILED_RESPONSE = "JoinFailed";
|
||||||
|
|
||||||
|
/** An error code returned when a user requests to join a table that
|
||||||
|
* doesn't exist. */
|
||||||
|
public static final String NO_SUCH_TABLE = "m.no_such_table";
|
||||||
|
|
||||||
|
/** An error code returned when a user requests to join a table at a
|
||||||
|
* position that is not valid. */
|
||||||
|
public static final String INVALID_TABLE_POSITION =
|
||||||
|
"m.invalid_table_position";
|
||||||
|
|
||||||
|
/** An error code returned when a user requests to join a table in a
|
||||||
|
* position that is already occupied. */
|
||||||
|
public static final String TABLE_POSITION_OCCUPIED =
|
||||||
|
"m.table_position_occupied";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// $Id: Table.java,v 1.1 2001/10/19 02:04:29 mdb Exp $
|
// $Id: Table.java,v 1.2 2001/10/19 22:02:36 mdb Exp $
|
||||||
|
|
||||||
package com.threerings.parlor.data;
|
package com.threerings.parlor.data;
|
||||||
|
|
||||||
@@ -12,17 +12,27 @@ import com.samskivert.util.StringUtil;
|
|||||||
import com.threerings.presents.dobj.DSet;
|
import com.threerings.presents.dobj.DSet;
|
||||||
import com.threerings.presents.dobj.io.ValueMarshaller;
|
import com.threerings.presents.dobj.io.ValueMarshaller;
|
||||||
|
|
||||||
|
import com.threerings.parlor.client.ParlorCodes;
|
||||||
import com.threerings.parlor.game.GameConfig;
|
import com.threerings.parlor.game.GameConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents a table that is being used to matchmake a game by
|
* This class represents a table that is being used to matchmake a game by
|
||||||
* the Parlor services.
|
* the Parlor services.
|
||||||
*/
|
*/
|
||||||
public class Table implements DSet.Element
|
public class Table
|
||||||
|
implements DSet.Element, ParlorCodes
|
||||||
{
|
{
|
||||||
/** The unique identifier for this table. */
|
/** The unique identifier for this table. */
|
||||||
public Integer tableId;
|
public Integer tableId;
|
||||||
|
|
||||||
|
/** The object id of the lobby object with which this table is
|
||||||
|
* associated. */
|
||||||
|
public int lobbyOid;
|
||||||
|
|
||||||
|
/** The oid of the game that was created from this table or -1 if the
|
||||||
|
* table is still in matchmaking mode. */
|
||||||
|
public int gameOid;
|
||||||
|
|
||||||
/** An array of the usernames of the occupants of this table (some
|
/** An array of the usernames of the occupants of this table (some
|
||||||
* slots may not be filled. */
|
* slots may not be filled. */
|
||||||
public String[] occupants;
|
public String[] occupants;
|
||||||
@@ -31,6 +41,39 @@ public class Table implements DSet.Element
|
|||||||
* instance will also implement {@link TableConfig}. */
|
* instance will also implement {@link TableConfig}. */
|
||||||
public GameConfig config;
|
public GameConfig config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new table instance, and assigns it the next monotonically
|
||||||
|
* increasing table id. The supplied config instance must implement
|
||||||
|
* {@link TableConfig} or a {@link ClassCastException} will be thrown.
|
||||||
|
*
|
||||||
|
* @param lobbyOid the object id of the lobby in which this table is
|
||||||
|
* to live.
|
||||||
|
* @param config the configuration of the game being matchmade by this
|
||||||
|
* table.
|
||||||
|
*/
|
||||||
|
public Table (int lobbyOid, GameConfig config)
|
||||||
|
{
|
||||||
|
// assign a unique table id
|
||||||
|
tableId = new Integer(++_tableIdCounter);
|
||||||
|
|
||||||
|
// keep track of our lobby oid
|
||||||
|
this.lobbyOid = lobbyOid;
|
||||||
|
|
||||||
|
// keep a casted reference around
|
||||||
|
_tconfig = (TableConfig)config;
|
||||||
|
this.config = config;
|
||||||
|
|
||||||
|
// make room for the maximum number of players
|
||||||
|
occupants = new String[_tconfig.getMaximumPlayers()];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a blank table instance, suitable for unserialization.
|
||||||
|
*/
|
||||||
|
public Table ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A convenience function for accessing the table id as an int.
|
* A convenience function for accessing the table id as an int.
|
||||||
*/
|
*/
|
||||||
@@ -39,6 +82,75 @@ public class Table implements DSet.Element
|
|||||||
return tableId.intValue();
|
return tableId.intValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Once a table is ready to play (see {@link #readyToPlay}), the
|
||||||
|
* players array can be fetched using this method. It will return an
|
||||||
|
* array containing the usernames of all of the players in the game,
|
||||||
|
* sized properly and with each player in the appropriate position.
|
||||||
|
*/
|
||||||
|
public String[] getPlayers ()
|
||||||
|
{
|
||||||
|
// count up the players
|
||||||
|
int pcount = 0;
|
||||||
|
for (int i = 0; i < occupants.length; i++) {
|
||||||
|
if (!StringUtil.blank(occupants[i])) {
|
||||||
|
pcount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create and populate the players array
|
||||||
|
String[] players = new String[pcount];
|
||||||
|
pcount = 0;
|
||||||
|
for (int i = 0; i < occupants.length; i++) {
|
||||||
|
if (!StringUtil.blank(occupants[i])) {
|
||||||
|
players[pcount++] = occupants[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return players;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests to seat the specified user at the specified position in
|
||||||
|
* this table.
|
||||||
|
*
|
||||||
|
* @return null if the user was successfully seated, a string error
|
||||||
|
* code explaining the failure if the user was not able to be seated
|
||||||
|
* at that position.
|
||||||
|
*/
|
||||||
|
public String setOccupant (int position, String username)
|
||||||
|
{
|
||||||
|
// find out how many positions we have for occupation
|
||||||
|
int maxpos = _tconfig.getDesiredPlayers();
|
||||||
|
// if there is no desired number of players, use the max
|
||||||
|
if (maxpos == -1) {
|
||||||
|
maxpos = _tconfig.getMaximumPlayers();
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure the requested position is a valid one
|
||||||
|
if (position >= maxpos) {
|
||||||
|
return INVALID_TABLE_POSITION;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure the requested position is not already occupied
|
||||||
|
if (!StringUtil.blank(occupants[position])) {
|
||||||
|
return TABLE_POSITION_OCCUPIED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise all is well, stick 'em in
|
||||||
|
occupants[position] = username;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this table has occupants in all of the desired
|
||||||
|
* positions and should be started.
|
||||||
|
*/
|
||||||
|
public boolean readyToStart ()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// documentation inherited
|
// documentation inherited
|
||||||
public Object getKey ()
|
public Object getKey ()
|
||||||
{
|
{
|
||||||
@@ -50,6 +162,8 @@ public class Table implements DSet.Element
|
|||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
out.writeInt(getTableId());
|
out.writeInt(getTableId());
|
||||||
|
out.writeInt(lobbyOid);
|
||||||
|
out.writeInt(gameOid);
|
||||||
ValueMarshaller.writeTo(out, occupants);
|
ValueMarshaller.writeTo(out, occupants);
|
||||||
ValueMarshaller.writeTo(out, config);
|
ValueMarshaller.writeTo(out, config);
|
||||||
}
|
}
|
||||||
@@ -59,8 +173,11 @@ public class Table implements DSet.Element
|
|||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
tableId = new Integer(in.readInt());
|
tableId = new Integer(in.readInt());
|
||||||
|
gameOid = in.readInt();
|
||||||
|
lobbyOid = in.readInt();
|
||||||
occupants = (String[])ValueMarshaller.readFrom(in);
|
occupants = (String[])ValueMarshaller.readFrom(in);
|
||||||
config = (GameConfig)ValueMarshaller.readFrom(in);
|
config = (GameConfig)ValueMarshaller.readFrom(in);
|
||||||
|
_tconfig = (TableConfig)config;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -69,7 +186,15 @@ public class Table implements DSet.Element
|
|||||||
public String toString ()
|
public String toString ()
|
||||||
{
|
{
|
||||||
return "[tableId=" + tableId +
|
return "[tableId=" + tableId +
|
||||||
|
", lobbyOid=" + lobbyOid +
|
||||||
|
", gameOid=" + gameOid +
|
||||||
", occupants=" + StringUtil.toString(occupants) +
|
", occupants=" + StringUtil.toString(occupants) +
|
||||||
", config=" + config + "]";
|
", config=" + config + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** A casted reference of our game config object. */
|
||||||
|
protected TableConfig _tconfig;
|
||||||
|
|
||||||
|
/** A counter for assigning table ids. */
|
||||||
|
protected static int _tableIdCounter = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,26 @@
|
|||||||
//
|
//
|
||||||
// $Id: ParlorManager.java,v 1.11 2001/10/19 02:04:29 mdb Exp $
|
// $Id: ParlorManager.java,v 1.12 2001/10/19 22:02:36 mdb Exp $
|
||||||
|
|
||||||
package com.threerings.parlor.server;
|
package com.threerings.parlor.server;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
import com.samskivert.util.Config;
|
import com.samskivert.util.Config;
|
||||||
import com.samskivert.util.HashIntMap;
|
import com.samskivert.util.HashIntMap;
|
||||||
|
import com.samskivert.util.StringUtil;
|
||||||
|
|
||||||
import com.threerings.presents.server.InvocationManager;
|
import com.threerings.presents.server.InvocationManager;
|
||||||
import com.threerings.presents.server.ServiceFailedException;
|
import com.threerings.presents.server.ServiceFailedException;
|
||||||
|
|
||||||
import com.threerings.crowd.data.BodyObject;
|
import com.threerings.crowd.data.BodyObject;
|
||||||
|
import com.threerings.crowd.data.PlaceObject;
|
||||||
import com.threerings.crowd.server.CrowdServer;
|
import com.threerings.crowd.server.CrowdServer;
|
||||||
|
import com.threerings.crowd.server.PlaceManager;
|
||||||
|
import com.threerings.crowd.server.PlaceRegistry;
|
||||||
|
|
||||||
import com.threerings.parlor.Log;
|
import com.threerings.parlor.Log;
|
||||||
import com.threerings.parlor.client.ParlorCodes;
|
import com.threerings.parlor.client.ParlorCodes;
|
||||||
|
import com.threerings.parlor.data.Table;
|
||||||
import com.threerings.parlor.data.TableConfig;
|
import com.threerings.parlor.data.TableConfig;
|
||||||
import com.threerings.parlor.data.TableLobbyObject;
|
import com.threerings.parlor.data.TableLobbyObject;
|
||||||
import com.threerings.parlor.game.GameConfig;
|
import com.threerings.parlor.game.GameConfig;
|
||||||
@@ -27,7 +34,7 @@ import com.threerings.parlor.game.GameManager;
|
|||||||
* game.
|
* game.
|
||||||
*/
|
*/
|
||||||
public class ParlorManager
|
public class ParlorManager
|
||||||
implements ParlorCodes
|
implements ParlorCodes, PlaceRegistry.CreationObserver
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Initializes the parlor manager. This should be called by the server
|
* Initializes the parlor manager. This should be called by the server
|
||||||
@@ -179,7 +186,7 @@ public class ParlorManager
|
|||||||
// started up (which is done by the place registry once the
|
// started up (which is done by the place registry once the
|
||||||
// game object creation has completed)
|
// game object creation has completed)
|
||||||
GameManager gmgr = (GameManager)
|
GameManager gmgr = (GameManager)
|
||||||
CrowdServer.plreg.createPlace(invite.config);
|
CrowdServer.plreg.createPlace(invite.config, null);
|
||||||
|
|
||||||
// provide the game manager with some initialization info
|
// provide the game manager with some initialization info
|
||||||
String[] players = new String[] {
|
String[] players = new String[] {
|
||||||
@@ -216,7 +223,38 @@ public class ParlorManager
|
|||||||
GameConfig config)
|
GameConfig config)
|
||||||
throws ServiceFailedException
|
throws ServiceFailedException
|
||||||
{
|
{
|
||||||
return -1;
|
// fetch the place object in which we'll be creating a table
|
||||||
|
try {
|
||||||
|
TableLobbyObject tlobj = (TableLobbyObject)
|
||||||
|
CrowdServer.omgr.getObject(placeOid);
|
||||||
|
|
||||||
|
// make sure the game config implements TableConfig
|
||||||
|
if (!(config instanceof TableConfig)) {
|
||||||
|
Log.warning("Requested to matchmake a non-table game " +
|
||||||
|
"using the table services [creator=" + creator +
|
||||||
|
", ploid=" + placeOid +
|
||||||
|
", config=" + config + "].");
|
||||||
|
throw new ServiceFailedException(INTERNAL_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a brand spanking new table
|
||||||
|
Table table = new Table(placeOid, config);
|
||||||
|
|
||||||
|
// and stick it into the table lobby object
|
||||||
|
tlobj.addToTables(table);
|
||||||
|
|
||||||
|
// also stick the table into our tables table
|
||||||
|
_tables.put(table.getTableId(), table);
|
||||||
|
|
||||||
|
// finally let the caller know what the new table id is
|
||||||
|
return table.getTableId();
|
||||||
|
|
||||||
|
} catch (ClassCastException cce) {
|
||||||
|
Log.warning("Requested to create table in non-table-lobby " +
|
||||||
|
"[creator=" + creator + ", ploid=" + placeOid +
|
||||||
|
", config=" + config + ", cce=" + cce + "].");
|
||||||
|
throw new ServiceFailedException(INTERNAL_ERROR);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -243,6 +281,104 @@ public class ParlorManager
|
|||||||
public void joinTable (BodyObject joiner, int tableId, int position)
|
public void joinTable (BodyObject joiner, int tableId, int position)
|
||||||
throws ServiceFailedException
|
throws ServiceFailedException
|
||||||
{
|
{
|
||||||
|
// look the table up
|
||||||
|
Table table = (Table)_tables.get(tableId);
|
||||||
|
if (table == null) {
|
||||||
|
throw new ServiceFailedException(NO_SUCH_TABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure the lobby for this table is still around
|
||||||
|
TableLobbyObject tlobj = (TableLobbyObject)
|
||||||
|
CrowdServer.omgr.getObject(table.lobbyOid);
|
||||||
|
if (tlobj == null) {
|
||||||
|
Log.warning("User tried to join table whose lobby has " +
|
||||||
|
"disappeared [table=" + table +
|
||||||
|
", joiner=" + joiner + "].");
|
||||||
|
throw new ServiceFailedException(INTERNAL_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
// request that the user be added to the table at that position
|
||||||
|
String error = table.setOccupant(position, joiner.username);
|
||||||
|
// if that failed, report the error
|
||||||
|
if (error != null) {
|
||||||
|
throw new ServiceFailedException(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine whether or not it's time to start the game
|
||||||
|
if (table.readyToStart()) {
|
||||||
|
// create the game manager
|
||||||
|
GameManager gmgr =
|
||||||
|
createGameManager(table.config, table.getPlayers());
|
||||||
|
|
||||||
|
// and map this table to this game manager so that we can
|
||||||
|
// update the table with the game in progress once it's
|
||||||
|
// created
|
||||||
|
_pendingTables.put(gmgr, table);
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the table in the lobby
|
||||||
|
tlobj.updateTables(table);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when we're ready to create a game (either an invitation has
|
||||||
|
* been accepted or a table is ready to start. If there is a problem
|
||||||
|
* creating the game manager, it will be reported in the logs.
|
||||||
|
*
|
||||||
|
* @return a reference to the newly created game manager or null if
|
||||||
|
* something choked during the creation or initialization process.
|
||||||
|
*/
|
||||||
|
protected GameManager createGameManager (
|
||||||
|
GameConfig config, String[] players)
|
||||||
|
{
|
||||||
|
GameManager gmgr = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
Log.info("Creating game manager [config=" + config + "].");
|
||||||
|
|
||||||
|
// create the game manager and begin it's initialization
|
||||||
|
// process. the game manager will take care of notifying the
|
||||||
|
// players that the game has been created once it has been
|
||||||
|
// started up (which is done by the place registry once the
|
||||||
|
// game object creation has completed)
|
||||||
|
gmgr = (GameManager)CrowdServer.plreg.createPlace(config, this);
|
||||||
|
|
||||||
|
// provide the game manager with some initialization info
|
||||||
|
gmgr.setPlayers(players);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.warning("Unable to create game manager [config=" + config +
|
||||||
|
", players=" + StringUtil.toString(players) + "].");
|
||||||
|
Log.logStackTrace(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return gmgr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by the place registry when the game object has been created
|
||||||
|
* and provided to the game manager. We use this to map game object
|
||||||
|
* oids back to table records when we create games from tables.
|
||||||
|
*/
|
||||||
|
public void placeCreated (PlaceObject plobj, PlaceManager pmgr)
|
||||||
|
{
|
||||||
|
// see if this place manager is associated with a table
|
||||||
|
Table table = (Table)_pendingTables.get(pmgr);
|
||||||
|
if (table != null) {
|
||||||
|
// update the table with the newly created game object
|
||||||
|
table.gameOid = plobj.getOid();
|
||||||
|
|
||||||
|
// and then update the lobby object that contains the table
|
||||||
|
TableLobbyObject tlobj = (TableLobbyObject)
|
||||||
|
CrowdServer.omgr.getObject(table.lobbyOid);
|
||||||
|
if (tlobj == null) {
|
||||||
|
Log.warning("Can't update in-play table as lobby's gone " +
|
||||||
|
"missing [table=" + table +
|
||||||
|
", gameOid=" + plobj.getOid() + "].");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tlobj.updateTables(table);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -293,6 +429,13 @@ public class ParlorManager
|
|||||||
/** The table of pending invitations. */
|
/** The table of pending invitations. */
|
||||||
protected HashIntMap _invites = new HashIntMap();
|
protected HashIntMap _invites = new HashIntMap();
|
||||||
|
|
||||||
|
/** The table of pending tables. */
|
||||||
|
protected HashIntMap _tables = new HashIntMap();
|
||||||
|
|
||||||
|
/** A table of tables that have games that have been created but not
|
||||||
|
* yet started. */
|
||||||
|
protected HashMap _pendingTables = new HashMap();
|
||||||
|
|
||||||
/** A counter used to generate unique identifiers for invitation
|
/** A counter used to generate unique identifiers for invitation
|
||||||
* records. */
|
* records. */
|
||||||
protected static int _nextInviteId = 0;
|
protected static int _nextInviteId = 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user