From 246987893d092b0f94bf8bb77b6851869d12e5df Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Tue, 23 Oct 2001 20:23:29 +0000 Subject: [PATCH] More progress on table services (can leave a table, things start correctly now, and much much more). git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@529 542714f4-19e9-0310-aa3c-eee0fc999fb1 --- .../threerings/parlor/client/ParlorCodes.java | 14 ++- .../parlor/client/ParlorService.java | 26 ++++- .../parlor/client/SeatednessObserver.java | 20 ++++ .../parlor/client/TableDirector.java | 106 ++++++++++++++++-- .../com/threerings/parlor/data/Table.java | 61 +++++++++- .../threerings/parlor/data/TableConfig.java | 7 +- .../parlor/server/ParlorManager.java | 82 +++++++++++--- .../parlor/server/ParlorProvider.java | 27 ++++- 8 files changed, 312 insertions(+), 31 deletions(-) create mode 100644 src/java/com/threerings/parlor/client/SeatednessObserver.java diff --git a/src/java/com/threerings/parlor/client/ParlorCodes.java b/src/java/com/threerings/parlor/client/ParlorCodes.java index 98d9755a7..6eb03d6f2 100644 --- a/src/java/com/threerings/parlor/client/ParlorCodes.java +++ b/src/java/com/threerings/parlor/client/ParlorCodes.java @@ -1,5 +1,5 @@ // -// $Id: ParlorCodes.java,v 1.8 2001/10/23 02:22:16 mdb Exp $ +// $Id: ParlorCodes.java,v 1.9 2001/10/23 20:23:29 mdb Exp $ package com.threerings.parlor.client; @@ -80,6 +80,14 @@ public interface ParlorCodes extends InvocationCodes * TableManager#handleJoinFailed}. */ public static final String JOIN_FAILED_RESPONSE = "JoinFailed"; + /** The message identifier for a leave table request. */ + public static final String LEAVE_TABLE_REQUEST = "LeaveTable"; + + /** The response identifier for a leave failed response. This is + * mapped by the invocation services to a call to {@link + * TableManager#handleLeaveFailed}. */ + public static final String LEAVE_FAILED_RESPONSE = "LeaveFailed"; + /** 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"; @@ -93,4 +101,8 @@ public interface ParlorCodes extends InvocationCodes * position that is already occupied. */ public static final String TABLE_POSITION_OCCUPIED = "m.table_position_occupied"; + + /** An error code returned when a user requests to leave a table that + * they were not sitting at in the first place. */ + public static final String NOT_AT_TABLE = "m.not_at_table"; } diff --git a/src/java/com/threerings/parlor/client/ParlorService.java b/src/java/com/threerings/parlor/client/ParlorService.java index 97e69759f..638eaf878 100644 --- a/src/java/com/threerings/parlor/client/ParlorService.java +++ b/src/java/com/threerings/parlor/client/ParlorService.java @@ -1,5 +1,5 @@ // -// $Id: ParlorService.java,v 1.8 2001/10/23 02:22:16 mdb Exp $ +// $Id: ParlorService.java,v 1.9 2001/10/23 20:23:29 mdb Exp $ package com.threerings.parlor.client; @@ -156,4 +156,28 @@ public class ParlorService implements ParlorCodes return invdir.invoke( MODULE_NAME, JOIN_TABLE_REQUEST, args, rsptarget); } + + /** + * You probably don't want to call this directly, but want to call + * {@link TableManager#leaveTable}. Requests that the current user + * be removed from the specified table. + * + * @param client a connected, operational client instance. + * @param tableId the unique id of the table from which this user + * wishes to be removed. + * @param rsptarget the object reference that will receive and process + * the response. + * + * @return the invocation request id of the generated request. + */ + public static int leaveTable ( + Client client, int tableId, Object rsptarget) + { + InvocationDirector invdir = client.getInvocationDirector(); + Object[] args = new Object[] { new Integer(tableId) }; + Log.info("Sending leave table request " + + "[tableId=" + tableId + "]."); + return invdir.invoke( + MODULE_NAME, LEAVE_TABLE_REQUEST, args, rsptarget); + } } diff --git a/src/java/com/threerings/parlor/client/SeatednessObserver.java b/src/java/com/threerings/parlor/client/SeatednessObserver.java new file mode 100644 index 000000000..d593c34a0 --- /dev/null +++ b/src/java/com/threerings/parlor/client/SeatednessObserver.java @@ -0,0 +1,20 @@ +// +// $Id: SeatednessObserver.java,v 1.1 2001/10/23 20:23:29 mdb Exp $ + +package com.threerings.parlor.client; + +/** + * Entites that wish to hear about when we sit down at a table or stand up + * from a table can implement this interface and register themselves with + * the {@link TableManager}. + */ +public interface SeatednessObserver +{ + /** + * Called when this client sits down at or stands up from a table. + * + * @param isSeated true if the client is now seated at a table, false + * if they are now no longer seated at a table. + */ + public void seatednessDidChange (boolean isSeated); +} diff --git a/src/java/com/threerings/parlor/client/TableDirector.java b/src/java/com/threerings/parlor/client/TableDirector.java index 17a9c0346..ff37adf64 100644 --- a/src/java/com/threerings/parlor/client/TableDirector.java +++ b/src/java/com/threerings/parlor/client/TableDirector.java @@ -1,8 +1,10 @@ // -// $Id: TableDirector.java,v 1.1 2001/10/23 02:22:16 mdb Exp $ +// $Id: TableDirector.java,v 1.2 2001/10/23 20:23:29 mdb Exp $ package com.threerings.parlor.client; +import java.util.ArrayList; + import com.threerings.presents.dobj.ElementAddedEvent; import com.threerings.presents.dobj.ElementUpdatedEvent; import com.threerings.presents.dobj.ElementRemovedEvent; @@ -86,6 +88,35 @@ public class TableManager _lobby = null; } + /** + * Requests that the specified observer be added to the list of + * observers that are notified when this client sits down at or stands + * up from a table. + */ + public void addSeatednessObserver (SeatednessObserver observer) + { + _seatedObservers.add(observer); + } + + /** + * Requests that the specified observer be removed from to the list of + * observers that are notified when this client sits down at or stands + * up from a table. + */ + public void removeSeatednessObserver (SeatednessObserver observer) + { + _seatedObservers.remove(observer); + } + + /** + * Returns true if this client is currently seated at a table, false + * if they are not. + */ + public boolean isSeated () + { + return (_ourTable != null); + } + /** * Sends a request to create a table with the specified game * configuration. This user will become the owner of this table and @@ -139,15 +170,35 @@ public class TableManager _ctx.getClient(), tableId, position, this); } + /** + * Sends a request to leave the specified table at which we are + * presumably seated. The response will be communicated via the {@link + * TableObserver} interface. + */ + public void leaveTable (int tableId) + { + // make sure we're currently in a place + if (_lobby == null) { + Log.warning("Requested to leave a table but we're not " + + "currently in a place [tableId=" + tableId + "]."); + return; + } + + // go ahead and issue the create request + ParlorService.leaveTable(_ctx.getClient(), tableId, this); + } + // documentation inherited public void elementAdded (ElementAddedEvent event) { if (event.getName().equals(_tableField)) { Table table = (Table)event.getElement(); - _observer.tableAdded(table); // check to see if we just joined a table - checkForOurTable(table); + checkSeatedness(table); + + // now let the observer know what's up + _observer.tableAdded(table); } } @@ -156,10 +207,12 @@ public class TableManager { if (event.getName().equals(_tableField)) { Table table = (Table)event.getElement(); - _observer.tableUpdated(table); // check to see if we just joined or left a table - checkForOurTable(table); + checkSeatedness(table); + + // now let the observer know what's up + _observer.tableUpdated(table); } } @@ -168,12 +221,15 @@ public class TableManager { if (event.getName().equals(_tableField)) { Integer tableId = (Integer)event.getKey(); - _observer.tableRemoved(tableId.intValue()); // check to see if our table just disappeared if (_ourTable != null && tableId.equals(_ourTable.tableId)) { _ourTable = null; + notifySeatedness(false); } + + // now let the observer know what's up + _observer.tableRemoved(tableId.intValue()); } } @@ -217,8 +273,10 @@ public class TableManager * Checks to see if we're a member of this table and notes it as our * table, if so. */ - protected void checkForOurTable (Table table) + protected void checkSeatedness (Table table) { + Table oldTable = _ourTable; + // if this is the same table as our table, clear out our table // reference and allow it to be added back if we are still in the // table @@ -231,7 +289,35 @@ public class TableManager for (int i = 0; i < table.occupants.length; i++) { if (self.username.equals(table.occupants[i])) { _ourTable = table; - return; + break; + } + } + + // if nothing changed, bail now + if (oldTable == _ourTable || + (oldTable != null && oldTable.equals(_ourTable))) { + return; + } + + // otherwise notify the observers + notifySeatedness(_ourTable != null); + } + + /** + * Notifies the seatedness observers of a seatedness change. + */ + protected void notifySeatedness (boolean isSeated) + { + int slength = _seatedObservers.size(); + for (int i = 0; i < slength; i++) { + SeatednessObserver observer = (SeatednessObserver) + _seatedObservers.get(i); + try { + observer.seatednessDidChange(isSeated); + } catch (Exception e) { + Log.warning("Observer choked in seatednessDidChange() " + + "[observer=" + observer + "]."); + Log.logStackTrace(e); } } } @@ -250,4 +336,8 @@ public class TableManager /** The table of which we are a member if any. */ protected Table _ourTable; + + /** An array of entities that want to hear about when we stand up or + * sit down. */ + protected ArrayList _seatedObservers = new ArrayList(); } diff --git a/src/java/com/threerings/parlor/data/Table.java b/src/java/com/threerings/parlor/data/Table.java index 5ce707723..566b250fc 100644 --- a/src/java/com/threerings/parlor/data/Table.java +++ b/src/java/com/threerings/parlor/data/Table.java @@ -1,5 +1,5 @@ // -// $Id: Table.java,v 1.4 2001/10/23 02:22:16 mdb Exp $ +// $Id: Table.java,v 1.5 2001/10/23 20:23:29 mdb Exp $ package com.threerings.parlor.data; @@ -31,7 +31,7 @@ public class Table /** The oid of the game that was created from this table or -1 if the * table is still in matchmaking mode. */ - public int gameOid; + public int gameOid = -1; /** An array of the usernames of the occupants of this table (some * slots may not be filled. */ @@ -143,13 +143,66 @@ public class Table return null; } + /** + * Requests that the specified user be removed from their seat at this + * table. + * + * @return true if the user was seated at the table and has now been + * removed, false if the user was never seated at the table in the + * first place. + */ + public boolean clearOccupant (String username) + { + for (int i = 0; i < occupants.length; i++) { + if (username.equals(occupants[i])) { + occupants[i] = ""; + return true; + } + } + return false; + } + /** * Returns true if this table has occupants in all of the desired * positions and should be started. */ public boolean readyToStart () { - return false; + int need = _tconfig.getMinimumPlayers(); + if (need == -1) { + need = _tconfig.getMaximumPlayers(); + } + + // make sure the first "need" players are filled in + for (int i = 0; i < need; i++) { + if (StringUtil.blank(occupants[i])) { + return false; + } + } + + return true; + } + + /** + * Returns true if there is no one sitting at this table. + */ + public boolean isEmpty () + { + for (int i = 0; i < occupants.length; i++) { + if (!StringUtil.blank(occupants[i])) { + return false; + } + } + return true; + } + + /** + * Returns true if this table is in play, false if it is still being + * matchmade. + */ + public boolean inPlay () + { + return gameOid != -1; } // documentation inherited @@ -174,8 +227,8 @@ public class Table throws IOException { tableId = new Integer(in.readInt()); - gameOid = in.readInt(); lobbyOid = in.readInt(); + gameOid = in.readInt(); occupants = (String[])ValueMarshaller.readFrom(in); config = (GameConfig)ValueMarshaller.readFrom(in); _tconfig = (TableConfig)config; diff --git a/src/java/com/threerings/parlor/data/TableConfig.java b/src/java/com/threerings/parlor/data/TableConfig.java index 3bf5f8a7b..b5a71d556 100644 --- a/src/java/com/threerings/parlor/data/TableConfig.java +++ b/src/java/com/threerings/parlor/data/TableConfig.java @@ -1,5 +1,5 @@ // -// $Id: TableConfig.java,v 1.1 2001/10/19 02:04:29 mdb Exp $ +// $Id: TableConfig.java,v 1.2 2001/10/23 20:23:29 mdb Exp $ package com.threerings.parlor.data; @@ -27,4 +27,9 @@ public interface TableConfig * creator any time after the minimum number of players has arrived. */ public int getDesiredPlayers (); + + /** + * Sets the desired number of players to the specified value. + */ + public void setDesiredPlayers (int desiredPlayers); } diff --git a/src/java/com/threerings/parlor/server/ParlorManager.java b/src/java/com/threerings/parlor/server/ParlorManager.java index e9cb513db..797fb8b85 100644 --- a/src/java/com/threerings/parlor/server/ParlorManager.java +++ b/src/java/com/threerings/parlor/server/ParlorManager.java @@ -1,5 +1,5 @@ // -// $Id: ParlorManager.java,v 1.13 2001/10/23 02:22:17 mdb Exp $ +// $Id: ParlorManager.java,v 1.14 2001/10/23 20:23:29 mdb Exp $ package com.threerings.parlor.server; @@ -208,9 +208,9 @@ public class ParlorManager * will be created. * * @param creator the body object that will own the new table. - * @param placeOid the place object id of the place (lobby) in which - * this table should be created. The place object specified must - * implement the {@link TableLobbyObject} interface. + * @param lobbyOid the place object id of the lobby in which this + * table should be created. The place object specified must implement + * the {@link TableLobbyObject} interface. * @param config the configuration of the game to be created. * * @return the id of the newly created table. @@ -219,26 +219,26 @@ public class ParlorManager * not able processed for some reason. The explanation will be * provided in the message data of the exception. */ - public int createTable (BodyObject creator, int placeOid, + public int createTable (BodyObject creator, int lobbyOid, GameConfig config) throws ServiceFailedException { // fetch the place object in which we'll be creating a table try { TableLobbyObject tlobj = (TableLobbyObject) - CrowdServer.omgr.getObject(placeOid); + CrowdServer.omgr.getObject(lobbyOid); // 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 + + ", loboid=" + lobbyOid + ", config=" + config + "]."); throw new ServiceFailedException(INTERNAL_ERROR); } // create a brand spanking new table - Table table = new Table(placeOid, config); + Table table = new Table(lobbyOid, config); // stick the creator into position zero String error = table.setOccupant(0, creator.username); @@ -261,7 +261,7 @@ public class ParlorManager } catch (ClassCastException cce) { Log.warning("Requested to create table in non-table-lobby " + - "[creator=" + creator + ", ploid=" + placeOid + + "[creator=" + creator + ", loboid=" + lobbyOid + ", config=" + config + ", cce=" + cce + "]."); throw new ServiceFailedException(INTERNAL_ERROR); } @@ -282,11 +282,9 @@ public class ParlorManager * @param tableId the id of the table to be joined. * @param position the position at which to join the table. * - * @return the id of the newly created table. - * - * @exception ServiceFailedException thrown if the table creation was - * not able processed for some reason. The explanation will be - * provided in the message data of the exception. + * @exception ServiceFailedException thrown if the joining was not + * able processed for some reason. The explanation will be provided in + * the message data of the exception. */ public void joinTable (BodyObject joiner, int tableId, int position) throws ServiceFailedException @@ -330,6 +328,60 @@ public class ParlorManager tlobj.updateTables(table); } + /** + * Requests that the specified user be removed from the specified + * table. If the user successfully leaves the table, the function will + * return normally. If they are not able to leave for some reason + * (they aren't sitting at the table, etc.), a {@link + * ServiceFailedException} will be thrown with a message code that + * describes the reason for failure. + * + * @param leaver the body object of the user that wishes to leave the + * table. + * @param tableId the id of the table to be left. + * + * @exception ServiceFailedException thrown if the leaving was not + * able processed for some reason. The explanation will be provided in + * the message data of the exception. + */ + public void leaveTable (BodyObject leaver, int tableId) + 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 leave table whose lobby has " + + "disappeared [table=" + table + + ", leaver=" + leaver + "]."); + throw new ServiceFailedException(INTERNAL_ERROR); + } + + // request that the user be removed from the table + if (!table.clearOccupant(leaver.username)) { + throw new ServiceFailedException(NOT_AT_TABLE); + } + + // check to see if we just removed the last person from the table + if (table.isEmpty()) { + Log.info("Clearing empty table."); + // remove the table from our tables table and from the lobby + _tables.remove(tableId); + tlobj.removeFromTables(table.tableId); + + } else { + Log.info("Updating sparser 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 @@ -373,7 +425,7 @@ public class ParlorManager public void placeCreated (PlaceObject plobj, PlaceManager pmgr) { // see if this place manager is associated with a table - Table table = (Table)_pendingTables.get(pmgr); + Table table = (Table)_pendingTables.remove(pmgr); if (table != null) { // update the table with the newly created game object table.gameOid = plobj.getOid(); diff --git a/src/java/com/threerings/parlor/server/ParlorProvider.java b/src/java/com/threerings/parlor/server/ParlorProvider.java index 69f503a2b..f372cfd24 100644 --- a/src/java/com/threerings/parlor/server/ParlorProvider.java +++ b/src/java/com/threerings/parlor/server/ParlorProvider.java @@ -1,5 +1,5 @@ // -// $Id: ParlorProvider.java,v 1.8 2001/10/19 02:04:29 mdb Exp $ +// $Id: ParlorProvider.java,v 1.9 2001/10/23 20:23:29 mdb Exp $ package com.threerings.parlor.server; @@ -141,6 +141,31 @@ public class ParlorProvider } } + /** + * Processes a request from the client to leave an existing table. + */ + public void handleLeaveTableRequest ( + BodyObject source, int invid, int tableId) + { + Log.info("Handling leave table request [source=" + source + + ", invid=" + invid + ", tableId=" + tableId + "]."); + + String rsp = null; + + try { + // pass the request on to the table manager + _pmgr.leaveTable(source, tableId); + // there is normally no response. the client will see + // themselves removed from the table they just left + + } catch (ServiceFailedException sfe) { + // the exception message is the code indicating the reason for + // the creation rejection + sendResponse(source, invid, LEAVE_FAILED_RESPONSE, + sfe.getMessage()); + } + } + /** A reference to the parlor manager we're working with. */ protected ParlorManager _pmgr; }