Yay for rabbit holes. Revamped the table services to decouple them from places.

While I was in there, I extracted the communication between the client and the
TableManager into a new-style "embedded in the TableLobbyObject" service
instead of wonkily routing everything through the global ParlorService and the
ParlorProvider (which got merged into the ParlorManager as a part of this
rabbit holery). The GG build will break... I will fix it.


git-svn-id: svn+ssh://src.earth.threerings.net/vilya/trunk@255 c613c5cb-e716-0410-b11b-feb51c14d237
This commit is contained in:
Michael Bayne
2007-03-21 21:38:31 +00:00
parent f61250f75b
commit b71007a909
24 changed files with 936 additions and 793 deletions
@@ -26,16 +26,13 @@ import com.threerings.util.Name;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.InvocationService;
import com.threerings.parlor.data.TableConfig;
import com.threerings.parlor.game.data.GameConfig;
/**
* Provides an interface to the various parlor 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}.
* Provides an interface to the various parlor 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}.
*/
public interface ParlorService extends InvocationService
{
@@ -51,124 +48,49 @@ public interface ParlorService extends InvocationService
}
/**
* 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
* 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 config a game config object detailing the type and configuration of the game to be
* created.
* @param listener will receive and process the response.
*/
public void invite (Client client, Name invitee, GameConfig config,
InviteListener listener);
/**
* You probably don't want to call this directly, but want to call one
* of {@link Invitation#accept}, {@link Invitation#refuse}, or {@link
* Invitation#counter}. Requests that an invitation response be
* delivered with the specified parameters.
* You probably don't want to call this directly, but want to call one of {@link
* Invitation#accept}, {@link Invitation#refuse}, or {@link Invitation#counter}. Requests that
* an invitation response 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 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 listener will receive and process the response.
*/
public void respond (Client client, int inviteId, int code, Object arg,
InvocationListener listener);
/**
* You probably don't want to call this directly, but want to call
* {@link Invitation#cancel}. Requests that an outstanding
* invitation be cancelled.
* You probably don't want to call this directly, but want to call {@link
* Invitation#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 inviteId the unique id previously assigned by the server to this invitation.
* @param listener will receive and process the response.
*/
public void cancel (Client client, int inviteId,
InvocationListener listener);
public void cancel (Client client, int inviteId, InvocationListener listener);
/**
* Used to communicate responses to {@link #createTable}, {@link
* #joinTable}, and {@link #leaveTable} requests.
* Requests to start a single player game with the specified game configuration.
*/
public static interface TableListener extends InvocationListener
{
public void tableCreated (int tableId);
}
/**
* You probably don't want to call this directly, but want to call
* {@link TableDirector#createTable}. Requests that a new table be
* created.
*
* @param client a connected, operational client instance.
* @param lobbyOid the oid of the lobby that will contain the newly
* created table.
* @param tableConfig the table configuration parameters.
* @param config the game config for the game to be matchmade by the
* table.
* @param listener will receive and process the response.
*/
public void createTable (Client client, int lobbyOid,
TableConfig tableConfig, GameConfig config, TableListener listener);
/**
* You probably don't want to call this directly, but want to call
* {@link TableDirector#joinTable}. Requests that the current user
* be added to the specified table at the specified position.
*
* @param client a connected, operational client instance.
* @param lobbyOid the oid of the lobby that contains the table.
* @param tableId the unique id of the table to which this user wishes
* to be added.
* @param position the position at the table to which this user desires
* to be added.
* @param listener will receive and process the response.
*/
public void joinTable (Client client, int lobbyOid, int tableId,
int position, InvocationListener listener);
/**
* You probably don't want to call this directly, but want to call
* {@link TableDirector#leaveTable}. Requests that the current user be
* removed from the specified table.
*
* @param client a connected, operational client instance.
* @param lobbyOid the oid of the lobby that contains the table.
* @param tableId the unique id of the table from which this user
* wishes to be removed.
* @param listener will receive and process the response.
*/
public void leaveTable (Client client, int lobbyOid, int tableId,
InvocationListener listener);
/**
* You probably don't want to call this directly, but want to call
* {@link TableDirector#startTableNow}. Requests that the specified
* table be started now, even if all seats are not occupied. This
* will always fail if called by any other player than that seated
* in position 0 (usually the creator).
*/
public void startTableNow (Client client, int lobbyOid, int tableId,
InvocationListener listener);
/**
* Requests to start a single player game with the specified game
* configuration.
*/
public void startSolitaire (Client client, GameConfig config,
ConfirmListener listener);
public void startSolitaire (Client client, GameConfig config, ConfirmListener listener);
}
@@ -44,39 +44,32 @@ import com.threerings.parlor.game.data.GameConfig;
import com.threerings.parlor.util.ParlorContext;
/**
* As tables are created and managed within the scope of a place (a
* lobby), we want to fold the table management functionality into the
* standard hierarchy of place controllers that deal with place-related
* functionality on the client. Thus, instead of forcing places that
* expect to have tables to extend a <code>TableLobbyController</code> or
* something similar, we instead provide the table director which can be
* instantiated by the place controller (or specific table related views)
* to handle the table matchmaking services.
* As tables are created and managed within the scope of a place (a lobby), we want to fold the
* table management functionality into the standard hierarchy of place controllers that deal with
* place-related functionality on the client. Thus, instead of forcing places that expect to have
* tables to extend a <code>TableLobbyController</code> or something similar, we instead provide
* the table director which can be instantiated by the place controller (or specific table related
* views) to handle the table matchmaking services.
*
* <p> Entites that do so, will need to implement the {@link
* TableObserver} interface so that the table director can notify them
* when table related things happen.
* <p> Entites that do so, will need to implement the {@link TableObserver} interface so that the
* table director can notify them when table related things happen.
*
* <p> The table services expect that the place object being used as a
* lobby in which the table matchmaking takes place implements the {@link
* TableLobbyObject} interface.
* <p> The table services expect that the place object being used as a lobby in which the table
* matchmaking takes place implements the {@link TableLobbyObject} interface.
*/
public class TableDirector extends BasicDirector
implements SetListener, ParlorService.TableListener
implements SetListener, TableService.ResultListener
{
/**
* Creates a new table director to manage tables with the specified
* observer which will receive callbacks when interesting table
* related things happen.
* Creates a new table director to manage tables with the specified observer which will receive
* callbacks when interesting table related things happen.
*
* @param ctx the parlor context in use by the client.
* @param tableField the field name of the distributed set that
* contains the tables we will be managing.
* @param observer the entity that will receive callbacks when things
* happen to the tables.
* @param tableField the field name of the distributed set that contains the tables we will be
* managing.
* @param observer the entity that will receive callbacks when things happen to the tables.
*/
public TableDirector (
ParlorContext ctx, String tableField, TableObserver observer)
public TableDirector (ParlorContext ctx, String tableField, TableObserver observer)
{
super(ctx);
@@ -87,39 +80,33 @@ public class TableDirector extends BasicDirector
}
/**
* This must be called by the entity that uses the table director when
* the using entity prepares to enter and display a place. It is
* assumed that the client is already subscribed to the provided place
* object.
* This must be called by the entity that uses the table director when the using entity
* prepares to enter and display a place. It is assumed that the client is already subscribed
* to the provided place object.
*/
public void willEnterPlace (PlaceObject place)
{
// the place should be a TableLobbyObject
_tlobj = (TableLobbyObject) place;
_lobby = place;
_tlobj = (TableLobbyObject)place;
// add ourselves as a listener to the place object
place.addListener(this);
}
/**
* This must be called by the entity that uses the table director when
* the using entity has left and is done displaying a place.
* This must be called by the entity that uses the table director when the using entity has
* left and is done displaying a place.
*/
public void didLeavePlace (PlaceObject place)
{
// remove our listenership
place.removeListener(this);
// clear out our lobby reference
_lobby = null;
// clear out our reference
_tlobj = 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.
* 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)
{
@@ -127,9 +114,8 @@ public class TableDirector extends BasicDirector
}
/**
* 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.
* 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)
{
@@ -137,8 +123,7 @@ public class TableDirector extends BasicDirector
}
/**
* Returns true if this client is currently seated at a table, false
* if they are not.
* Returns true if this client is currently seated at a table, false if they are not.
*/
public boolean isSeated ()
{
@@ -146,117 +131,100 @@ public class TableDirector extends BasicDirector
}
/**
* Sends a request to create a table with the specified game
* configuration. This user will become the owner of this table and
* will be added to the first position in the table. The response will
* be communicated via the {@link TableObserver} interface.
* Sends a request to create a table with the specified game configuration. This user will
* become the owner of this table and will be added to the first position in the table. The
* response will be communicated via the {@link TableObserver} interface.
*/
public void createTable (TableConfig tableConfig, GameConfig config)
{
// if we're already in a table, refuse the request
if (_ourTable != null) {
Log.warning("Ignoring request to create table as we're " +
"already in a table [table=" + _ourTable + "].");
Log.warning("Ignoring request to create table as we're already in a table " +
"[table=" + _ourTable + "].");
return;
}
// make sure we're currently in a place
if (_lobby == null) {
Log.warning("Requested to create a table but we're not " +
"currently in a place [config=" + config + "].");
if (_tlobj == null) {
Log.warning("Requested to create a table but we're not currently in a place " +
"[config=" + config + "].");
return;
}
// go ahead and issue the create request
_pservice.createTable(_ctx.getClient(), _lobby.getOid(), tableConfig,
config, this);
_tlobj.getTableService().createTable(_ctx.getClient(), tableConfig, config, this);
}
/**
* Sends a request to join the specified table at the specified
* position. The response will be communicated via the {@link
* TableObserver} interface.
* Sends a request to join the specified table at the specified position. The response will be
* communicated via the {@link TableObserver} interface.
*/
public void joinTable (int tableId, int position)
{
// if we're already in a table, refuse the request
if (_ourTable != null) {
Log.warning("Ignoring request to join table as we're " +
"already in a table [table=" + _ourTable + "].");
Log.warning("Ignoring request to join table as we're already in a table " +
"[table=" + _ourTable + "].");
return;
}
// make sure we're currently in a place
if (_lobby == null) {
Log.warning("Requested to join a table but we're not " +
"currently in a place [tableId=" + tableId + "].");
if (_tlobj == null) {
Log.warning("Requested to join a table but we're not currently in a place " +
"[tableId=" + tableId + "].");
return;
}
// issue the join request
_pservice.joinTable(_ctx.getClient(), _lobby.getOid(), tableId,
position, this);
_tlobj.getTableService().joinTable(_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.
* 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 + "].");
if (_tlobj == null) {
Log.warning("Requested to leave a table but we're not currently in a place " +
"[tableId=" + tableId + "].");
return;
}
// issue the leave request
_pservice.leaveTable(_ctx.getClient(), _lobby.getOid(), tableId, this);
_tlobj.getTableService().leaveTable(_ctx.getClient(), tableId, this);
}
/**
* Sends a request to have the specified table start now, even if
* all the seats have not yet been filled.
* Sends a request to have the specified table start now, even if all the seats have not yet
* been filled.
*/
public void startTableNow (int tableId)
{
if (_lobby == null) {
Log.warning("Requested to start a table but we're not " +
"currently in a place [tableId=" + tableId + "].");
if (_tlobj == null) {
Log.warning("Requested to start a table but we're not currently in a place " +
"[tableId=" + tableId + "].");
return;
}
_pservice.startTableNow(_ctx.getClient(), _lobby.getOid(),
tableId, this);
_tlobj.getTableService().startTableNow(_ctx.getClient(), tableId, this);
}
// documentation inherited
public void clientDidLogoff (Client client)
{
super.clientDidLogoff(client);
_pservice = null;
_lobby = null;
_ourTable = null;
}
// documentation inherited
protected void fetchServices (Client client)
{
// get a handle on our parlor services
_pservice = (ParlorService)client.requireService(ParlorService.class);
}
// documentation inherited
public void entryAdded (EntryAddedEvent event)
{
if (event.getName().equals(_tableField)) {
Table table = (Table)event.getEntry();
// check to see if we just joined a table
checkSeatedness(table);
// now let the observer know what's up
_observer.tableAdded(table);
}
@@ -267,10 +235,8 @@ public class TableDirector extends BasicDirector
{
if (event.getName().equals(_tableField)) {
Table table = (Table)event.getEntry();
// check to see if we just joined or left a table
checkSeatedness(table);
// now let the observer know what's up
_observer.tableUpdated(table);
}
@@ -281,43 +247,42 @@ public class TableDirector extends BasicDirector
{
if (event.getName().equals(_tableField)) {
int tableId = ((Integer) event.getKey()).intValue();
// check to see if our table just disappeared
if (_ourTable != null && tableId == _ourTable.tableId) {
_ourTable = null;
notifySeatedness(false);
}
// now let the observer know what's up
_observer.tableRemoved(tableId);
}
}
// documentation inherited from interface
public void tableCreated (int tableId)
// from interface TableService.ResultListener
public void requestProcessed (Object result)
{
if (_lobby == null) {
int tableId = (Integer)result;
if (_tlobj == null) {
// we've left, it's none of our concern anymore
Log.info("Table created, but no lobby. [tableId=" + tableId + "].");
return;
}
// All this to check to see if we created a party game (and should now enter).
Table table = (Table) _tlobj.getTables().get(tableId);
if (table == null) {
Log.warning("Table created, but where is it? [tableId=" + tableId + "]");
return;
}
// All this to check to see if we created a party game (and should now enter).
if (table.gameOid != -1 && table.occupants.length == 0) {
// let's boogie!
_ctx.getParlorDirector().gameIsReady(table.gameOid);
_ctx.getParlorDirector().gameIsReady(table.gameOid); // let's boogie!
}
}
// documentation inherited from interface
public void requestFailed (String reason)
{
Log.warning("Table creation failed [reason=" + reason + "].");
Log.warning("Table action failed [reason=" + reason + "].");
}
/**
@@ -328,9 +293,8 @@ public class TableDirector extends BasicDirector
{
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
// 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
if (table.equals(_ourTable)) {
_ourTable = null;
}
@@ -355,25 +319,18 @@ public class TableDirector extends BasicDirector
*/
protected void notifySeatedness (final boolean isSeated)
{
_seatedObservers.apply(
new ObserverList.ObserverOp<SeatednessObserver>() {
public boolean apply (SeatednessObserver so) {
so.seatednessDidChange(isSeated);
return true;
}
});
_seatedObservers.apply(new ObserverList.ObserverOp<SeatednessObserver>() {
public boolean apply (SeatednessObserver so) {
so.seatednessDidChange(isSeated);
return true;
}
});
}
/** A context by which we can access necessary client services. */
protected ParlorContext _ctx;
/** Parlor server-side services. */
protected ParlorService _pservice;
/** The place object in which we're currently managing tables. */
protected PlaceObject _lobby;
/** The place object, cast as a TableLobbyObject. */
/** Our TableLobbyObject. */
protected TableLobbyObject _tlobj;
/** The field name of the distributed set that contains our tables. */
@@ -385,8 +342,7 @@ public class TableDirector extends BasicDirector
/** 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. */
/** An array of entities that want to hear about when we stand up or sit down. */
protected ObserverList<SeatednessObserver> _seatedObservers =
new ObserverList<SeatednessObserver>(ObserverList.FAST_UNSAFE_NOTIFY);
}
@@ -0,0 +1,71 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/vilya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.parlor.client;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.InvocationService;
import com.threerings.parlor.data.TableConfig;
import com.threerings.parlor.game.data.GameConfig;
/**
* Provides table lobbying services.
*/
public interface TableService extends InvocationService
{
/**
* Requests that a new table be created.
*
* @param client a connected, operational client instance.
* @param tableConfig the table configuration parameters.
* @param config the game config for the game to be matchmade by the table.
* @param listener will receive and process the response.
*/
public void createTable (Client client, TableConfig tableConfig, GameConfig config,
ResultListener listener);
/**
* Requests that the current user be added to the specified table at the specified position.
*
* @param client a connected, operational client instance.
* @param tableId the unique id of the table to which this user wishes to be added.
* @param position the position at the table to which this user desires to be added.
* @param listener will receive and process the response.
*/
public void joinTable (Client client, int tableId, int position, InvocationListener listener);
/**
* 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 listener will receive and process the response.
*/
public void leaveTable (Client client, int tableId, InvocationListener listener);
/**
* Requests that the specified table be started now, even if all seats are not occupied. This
* will always fail if called by any other player than that seated in position 0 (usually the
* creator).
*/
public void startTableNow (Client client, int tableId, InvocationListener listener);
}