From e8071b5ad69ee5c89e82e23a05ca879b3a09030f Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Thu, 4 Oct 2001 23:41:44 +0000 Subject: [PATCH] More progress on MiCasa services. Have lobby selection working now. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@397 542714f4-19e9-0310-aa3c-eee0fc999fb1 --- rsrc/config/micasa/server.properties | 14 +- .../micasa/client/ClientController.java | 28 ++- .../micasa/client/OccupantList.java | 83 +++++++ .../com/threerings/micasa/lobby/Lobby.java | 69 ++++++ .../threerings/micasa/lobby/LobbyCodes.java | 22 +- .../threerings/micasa/lobby/LobbyManager.java | 50 +++- .../threerings/micasa/lobby/LobbyObject.dobj | 4 +- .../threerings/micasa/lobby/LobbyPanel.java | 62 ++--- .../micasa/lobby/LobbyProvider.java | 25 +- .../micasa/lobby/LobbyRegistry.java | 151 +++++++++--- .../micasa/lobby/LobbySelector.java | 215 ++++++++++++++++++ .../threerings/micasa/lobby/LobbyService.java | 62 +++++ .../micasa/server/MiCasaServer.java | 9 +- 13 files changed, 706 insertions(+), 88 deletions(-) create mode 100644 src/java/com/threerings/micasa/client/OccupantList.java create mode 100644 src/java/com/threerings/micasa/lobby/Lobby.java create mode 100644 src/java/com/threerings/micasa/lobby/LobbySelector.java create mode 100644 src/java/com/threerings/micasa/lobby/LobbyService.java diff --git a/rsrc/config/micasa/server.properties b/rsrc/config/micasa/server.properties index 43024a5b8..f9b0f95ab 100644 --- a/rsrc/config/micasa/server.properties +++ b/rsrc/config/micasa/server.properties @@ -1,4 +1,16 @@ # -# $Id: server.properties,v 1.1 2001/10/03 23:24:09 mdb Exp $ +# $Id: server.properties,v 1.2 2001/10/04 23:41:44 mdb Exp $ # # Configuration for the MiCasa server + +# +# The list of lobby identifiers to be loaded into this server. +# +lobby_ids = test + +# +# The cofiguration for the test lobby. +# +test.mgrclass = com.threerings.micasa.lobby.LobbyManager +test.ugi = test,board,partner +test.name = Test Lobby diff --git a/src/java/com/threerings/micasa/client/ClientController.java b/src/java/com/threerings/micasa/client/ClientController.java index 10ea7fda5..d1a912650 100644 --- a/src/java/com/threerings/micasa/client/ClientController.java +++ b/src/java/com/threerings/micasa/client/ClientController.java @@ -1,10 +1,11 @@ // -// $Id: ClientController.java,v 1.1 2001/10/03 23:24:09 mdb Exp $ +// $Id: ClientController.java,v 1.2 2001/10/04 23:41:44 mdb Exp $ package com.threerings.micasa.client; import java.awt.event.ActionEvent; import com.samskivert.swing.Controller; +import com.samskivert.util.StringUtil; import com.threerings.cocktail.cher.dobj.*; import com.threerings.cocktail.cher.client.*; @@ -13,10 +14,12 @@ import com.threerings.cocktail.cher.net.UsernamePasswordCreds; import com.threerings.cocktail.party.client.*; import com.threerings.cocktail.party.data.*; +import com.threerings.cocktail.party.util.PlaceViewUtil; import com.threerings.media.sprite.SpriteManager; import com.threerings.micasa.Log; +import com.threerings.micasa.lobby.LobbyService; import com.threerings.micasa.util.MiCasaContext; /** @@ -49,12 +52,8 @@ public class ClientController // we also want to know about occupant changes _ctx.getOccupantManager().addOccupantObserver(this); - // create our main panel which we'll use once we're logged on - _mainPanel = new MainPanel(); - - // create the chat ui - ChatPanel panel = new ChatPanel(_ctx); - _mainPanel.setNavigation(panel); + // create our lobby panel which we'll use once we're logged on + _lobbyPanel = new LobbyPanel(ctx); // create the logon panel and display it _logonPanel = new LogonPanel(_ctx); @@ -89,7 +88,7 @@ public class ClientController // we're logged on, so we lose the login panel and move to our // primary display - _frame.setPanel(_mainPanel); + _frame.setPanel(_lobbyPanel); } // documentation inherited @@ -132,12 +131,19 @@ public class ClientController { Log.info("Moved to new location [place=" + place + "]."); - // we wants to subscribe to the location + // clean up after the old place if (_place != null) { _place.removeSubscriber(this); + PlaceViewUtil.dispatchDidLeavePlace(_frame, _place); } - place.addSubscriber(this); + _place = place; + + // and enter the new place with bells on + if (_place != null) { + _place.addSubscriber(this); + PlaceViewUtil.dispatchWillEnterPlace(_frame, _place); + } } // documentation inherited @@ -185,5 +191,5 @@ public class ClientController // our panels protected LogonPanel _logonPanel; - protected MainPanel _mainPanel; + protected LobbyPanel _lobbyPanel; } diff --git a/src/java/com/threerings/micasa/client/OccupantList.java b/src/java/com/threerings/micasa/client/OccupantList.java new file mode 100644 index 000000000..3893cec21 --- /dev/null +++ b/src/java/com/threerings/micasa/client/OccupantList.java @@ -0,0 +1,83 @@ +// +// $Id: OccupantList.java,v 1.1 2001/10/04 23:41:44 mdb Exp $ + +package com.threerings.micasa.client; + +import java.util.Iterator; +import javax.swing.*; + +import com.threerings.cocktail.party.client.OccupantObserver; +import com.threerings.cocktail.party.data.OccupantInfo; +import com.threerings.cocktail.party.data.PlaceObject; +import com.threerings.cocktail.party.util.PlaceView; + +import com.threerings.micasa.util.MiCasaContext; + +/** + * The occupant list displays the list of users that are in a particular + * place. + */ +public class OccupantList + extends JList implements PlaceView, OccupantObserver +{ + /** + * Constructs an occupant list with the supplied context which it will + * use to register itself with the necessary managers. + */ + public OccupantList (MiCasaContext ctx) + { + // set up our list model + _model = new DefaultListModel(); + setModel(_model); + + // keep our context around for later + _ctx = ctx; + + // register ourselves as an occupant observer + _ctx.getOccupantManager().addOccupantObserver(this); + } + + // documentation inherited + public void willEnterPlace (PlaceObject plobj) + { + // add all of the occupants of the place to our list + Iterator users = plobj.occupantInfo.elements(); + while (users.hasNext()) { + OccupantInfo info = (OccupantInfo)users.next(); + _model.addElement(info.username); + } + } + + // documentation inherited + public void didLeavePlace (PlaceObject plobj) + { + // clear out our occupant entries + _model.clear(); + } + + // documentation inherited + public void occupantEntered (OccupantInfo info) + { + // simply add this user to our list + _model.addElement(info.username); + } + + // documentation inherited + public void occupantLeft (OccupantInfo info) + { + // remove this occupant from our list + _model.removeElement(info.username); + } + + // documentation inherited + public void occupantUpdated (OccupantInfo info) + { + // nothing doing + } + + /** Our client context. */ + protected MiCasaContext _ctx; + + /** A list model that provides a vector interface. */ + protected DefaultListModel _model; +} diff --git a/src/java/com/threerings/micasa/lobby/Lobby.java b/src/java/com/threerings/micasa/lobby/Lobby.java new file mode 100644 index 000000000..af8471c35 --- /dev/null +++ b/src/java/com/threerings/micasa/lobby/Lobby.java @@ -0,0 +1,69 @@ +// +// $Id: Lobby.java,v 1.1 2001/10/04 23:41:44 mdb Exp $ + +package com.threerings.micasa.lobby; + +import java.io.IOException; +import java.io.DataInputStream; +import java.io.DataOutputStream; + +import com.threerings.cocktail.cher.io.Streamable; + +/** + * A simple class for keeping track of information for each lobby in + * operation on the server. + */ +public class Lobby implements Streamable +{ + /** The object id of the lobby place object. */ + public int placeOid; + + /** The universal game identifier string for the game matchmade by + * this lobby. */ + public String gameIdent; + + /** The human readable name of the lobby. */ + public String name; + + /** + * Constructs a lobby record and initializes it with the specified + * values. + */ + public Lobby (int placeOid, String gameIdent, String name) + { + this.placeOid = placeOid; + this.gameIdent = gameIdent; + this.name = name; + } + + /** + * Constructs a blank lobby record suitable for unserialization. + */ + public Lobby () + { + } + + // documentation inherited + public void writeTo (DataOutputStream out) + throws IOException + { + out.writeInt(placeOid); + out.writeUTF(gameIdent); + out.writeUTF(name); + } + + // documentation inherited + public void readFrom (DataInputStream in) + throws IOException + { + placeOid = in.readInt(); + gameIdent = in.readUTF(); + name = in.readUTF(); + } + + public String toString () + { + return "[oid=" + placeOid + ", ident=" + gameIdent + + ", name=" + name + "]"; + } +} diff --git a/src/java/com/threerings/micasa/lobby/LobbyCodes.java b/src/java/com/threerings/micasa/lobby/LobbyCodes.java index 13f5b232a..ef6e188a8 100644 --- a/src/java/com/threerings/micasa/lobby/LobbyCodes.java +++ b/src/java/com/threerings/micasa/lobby/LobbyCodes.java @@ -1,7 +1,7 @@ // -// $Id: LobbyCodes.java,v 1.1 2001/10/04 00:29:07 mdb Exp $ +// $Id: LobbyCodes.java,v 1.2 2001/10/04 23:41:44 mdb Exp $ -package com.threerings.micasa.lobdy; +package com.threerings.micasa.lobby; import com.threerings.cocktail.cher.client.InvocationCodes; @@ -13,6 +13,24 @@ public interface LobbyCodes extends InvocationCodes /** The module name for the lobby services. */ public static final String MODULE_NAME = "lobby"; + /** The message identifier for a get categories request. */ + public static final String GET_CATEGORIES_REQUEST = "GetCategories"; + + /** The message identifier for a got categories response. This is + * mapped by the invocation services to a call to + * handleGotCategories. */ + public static final String GOT_CATEGORIES_RESPONSE = "GotCategories"; + /** The message identifier for a get lobbies request. */ public static final String GET_LOBBIES_REQUEST = "GetLobbies"; + + /** The message identifier for a got lobbies response. This is mapped + * by the invocation services to a call to + * handleGotLobbies. */ + public static final String GOT_LOBBIES_RESPONSE = "GotLobbies"; + + /** The message identifier for a failed request. This is + * mapped by the invocation services to a call to + * handleRequestFailed. */ + public static final String REQUEST_FAILED_RESPONSE = "RequestFailed"; } diff --git a/src/java/com/threerings/micasa/lobby/LobbyManager.java b/src/java/com/threerings/micasa/lobby/LobbyManager.java index 8aadd3ffd..7cd4ba4e9 100644 --- a/src/java/com/threerings/micasa/lobby/LobbyManager.java +++ b/src/java/com/threerings/micasa/lobby/LobbyManager.java @@ -1,9 +1,10 @@ // -// $Id: LobbyManager.java,v 1.1 2001/10/04 00:29:07 mdb Exp $ +// $Id: LobbyManager.java,v 1.2 2001/10/04 23:41:44 mdb Exp $ -package com.threerings.micasa.lobdy; +package com.threerings.micasa.lobby; import java.util.Properties; +import com.samskivert.util.StringUtil; import com.threerings.cocktail.party.server.PlaceManager; import com.threerings.micasa.Log; @@ -18,14 +19,55 @@ public class LobbyManager extends PlaceManager * * @exception Exception thrown if a configuration error is detected. */ - public void init (Properties config) + public void init (LobbyRegistry lobreg, Properties config) throws Exception { - Log.info("Lobby manager initialized."); + // look up some configuration parameters + _gameIdent = getConfigValue(config, "ugi"); + _name = getConfigValue(config, "name"); + + // keep this for later + _lobreg = lobreg; + + Log.info("Lobby manager initialized [ident=" + _gameIdent + + ", name=" + _name + "]."); } + /** Looks up a configuration property in the supplied properties + * object and throws an exception if it's not found. */ + protected String getConfigValue (Properties config, String key) + throws Exception + { + String value = config.getProperty(key); + if (StringUtil.blank(value)) { + throw new Exception("Missing '" + key + "' definition in " + + "lobby configuration."); + } + return value; + } + + // documentation inherited protected Class getPlaceObjectClass () { return LobbyObject.class; } + + // documentation inherited + protected void didStartup () + { + super.didStartup(); + + // let the lobby registry know that we're up and running + _lobreg.lobbyReady(_plobj.getOid(), _gameIdent, _name); + } + + /** The universal game identifier for the game matchmade by this + * lobby. */ + protected String _gameIdent; + + /** The human readable name of this lobby. */ + protected String _name; + + /** A reference to the lobby registry. */ + protected LobbyRegistry _lobreg; } diff --git a/src/java/com/threerings/micasa/lobby/LobbyObject.dobj b/src/java/com/threerings/micasa/lobby/LobbyObject.dobj index 01c00fb92..c36653e38 100644 --- a/src/java/com/threerings/micasa/lobby/LobbyObject.dobj +++ b/src/java/com/threerings/micasa/lobby/LobbyObject.dobj @@ -1,7 +1,7 @@ // -// $Id: LobbyObject.dobj,v 1.1 2001/10/04 00:29:07 mdb Exp $ +// $Id: LobbyObject.dobj,v 1.2 2001/10/04 23:41:44 mdb Exp $ -package com.threerings.micasa.lobdy; +package com.threerings.micasa.lobby; import com.threerings.cocktail.party.data.PlaceObject; diff --git a/src/java/com/threerings/micasa/lobby/LobbyPanel.java b/src/java/com/threerings/micasa/lobby/LobbyPanel.java index 18cd725c1..3e335fac6 100644 --- a/src/java/com/threerings/micasa/lobby/LobbyPanel.java +++ b/src/java/com/threerings/micasa/lobby/LobbyPanel.java @@ -1,36 +1,56 @@ // -// $Id: LobbyPanel.java,v 1.1 2001/10/03 23:24:09 mdb Exp $ +// $Id: LobbyPanel.java,v 1.2 2001/10/04 23:41:44 mdb Exp $ package com.threerings.micasa.client; import javax.swing.*; import com.samskivert.swing.*; +import com.threerings.micasa.util.MiCasaContext; /** - * The main panel is the top level component in the UI. It encloses the - * primary user interface display and the sidebar displays. The sidebar - * contains a navigation display, a management display (where various - * management panels can be added), and the chat interface. + * The lobby panel is used to display the interface for the lobbies. It + * contains a lobby selection mechanism, a chat interface and a user + * interface for whatever matchmaking mechanism is appropriate for a + * particular lobby. */ -public class MainPanel extends JPanel +public class LobbyPanel extends JPanel { - public MainPanel () + /** + * Constructs a new lobby panel and the associated user interface + * elements. + */ + public LobbyPanel (MiCasaContext ctx) { + // we want a five pixel border around everything + setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + + // create our primary layout which divides the display in two + // horizontally GroupLayout gl = new HGroupLayout(GroupLayout.STRETCH); gl.setOffAxisPolicy(GroupLayout.STRETCH); gl.setJustification(GroupLayout.RIGHT); - gl.setGap(0); setLayout(gl); // create our sidebar panel gl = new VGroupLayout(GroupLayout.STRETCH); gl.setOffAxisPolicy(GroupLayout.STRETCH); _sidePanel = new JPanel(gl); - _sidePanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); - // stick some stuff in our sidebar for now - JLabel label = new JLabel("Sidebar views"); + // the sidebar contains a lobby selector... + JLabel label = new JLabel("Select a lobby..."); _sidePanel.add(label, GroupLayout.FIXED); + LobbySelector selector = new LobbySelector(ctx); + _sidePanel.add(selector); + + // ...a lobby info display... + + // and an occupants list + label = new JLabel("Occupants"); + _sidePanel.add(label, GroupLayout.FIXED); + OccupantList occupants = new OccupantList(ctx); + _sidePanel.add(occupants); + + // add our sidebar panel into the mix add(_sidePanel, GroupLayout.FIXED); } @@ -58,29 +78,9 @@ public class MainPanel extends JPanel validate(); } - /** - * Sets the navigation view. This is the small map-like view in the - * upper right corner of the display. - */ - public void setNavigation (JPanel view) - { - // remove the old view - if (_navView != null) { - _sidePanel.remove(_navView); - } - - // add the new one - _navView = view; - _sidePanel.add(_navView); - _sidePanel.validate(); - } - /** The sidebar panel. */ protected JPanel _sidePanel; /** The primary view. */ protected JPanel _primView; - - /** The navigation view. */ - protected JPanel _navView; } diff --git a/src/java/com/threerings/micasa/lobby/LobbyProvider.java b/src/java/com/threerings/micasa/lobby/LobbyProvider.java index f7a5d72bf..940ec2f4c 100644 --- a/src/java/com/threerings/micasa/lobby/LobbyProvider.java +++ b/src/java/com/threerings/micasa/lobby/LobbyProvider.java @@ -1,9 +1,10 @@ // -// $Id: LobbyProvider.java,v 1.1 2001/10/04 00:29:07 mdb Exp $ +// $Id: LobbyProvider.java,v 1.2 2001/10/04 23:41:44 mdb Exp $ -package com.threerings.micasa.lobdy; +package com.threerings.micasa.lobby; import com.threerings.cocktail.cher.server.InvocationProvider; +import com.threerings.cocktail.cher.util.StreamableArrayList; import com.threerings.cocktail.party.data.BodyObject; /** @@ -26,13 +27,29 @@ public class LobbyProvider _lobreg = lobreg; } + /** + * Processes a request by the client to obtain a list of the lobby + * categories available on this server. + */ + public void handleGetCategoriesRequest ( + BodyObject source, int invid) + { + String[] cats = _lobreg.getCategories(source); + // we have to cast the array to an object to avoid having the + // compiler pick the version of sendResponse that takes Object[] + sendResponse(source, invid, GOT_CATEGORIES_RESPONSE, (Object)cats); + } + /** * Processes a request by the client to obtain a list of lobbies - * matching the supplied pattern string. + * matching the supplied category string. */ public void handleGetLobbiesRequest ( - BodyObject source, int invid, String pattern) + BodyObject source, int invid, String category) { + StreamableArrayList list = new StreamableArrayList(); + _lobreg.getLobbies(source, category, list); + sendResponse(source, invid, GOT_LOBBIES_RESPONSE, list); } /** A reference to the lobby registry. */ diff --git a/src/java/com/threerings/micasa/lobby/LobbyRegistry.java b/src/java/com/threerings/micasa/lobby/LobbyRegistry.java index 98638466c..d77534101 100644 --- a/src/java/com/threerings/micasa/lobby/LobbyRegistry.java +++ b/src/java/com/threerings/micasa/lobby/LobbyRegistry.java @@ -1,12 +1,13 @@ // -// $Id: LobbyRegistry.java,v 1.1 2001/10/04 00:29:07 mdb Exp $ +// $Id: LobbyRegistry.java,v 1.2 2001/10/04 23:41:44 mdb Exp $ -package com.threerings.micasa.lobdy; +package com.threerings.micasa.lobby; -import java.util.Properties; +import java.util.*; +import com.samskivert.util.*; -import com.samskivert.util.Config; -import com.samskivert.util.PropertiesUtil; +import com.threerings.cocktail.cher.server.InvocationManager; +import com.threerings.cocktail.party.data.BodyObject; import com.threerings.micasa.Log; import com.threerings.micasa.server.MiCasaServer; @@ -24,10 +25,14 @@ import com.threerings.micasa.server.MiCasaServer; * lobby_ids = foolobby, barlobby, bazlobby * * foolobby.mgrclass = com.threerings.micasa.lobby.LobbyManager + * foolobby.ugi = + * foolobby.name = * foolobby.config1 = some config value * foolobby.config2 = some other config value * * barlobby.mgrclass = com.threerings.micasa.lobby.LobbyManager + * barlobby.ugi = + * barlobby.name = * ... * * @@ -36,35 +41,50 @@ import com.threerings.micasa.server.MiCasaServer; * rsrc/config/micasa/server.properties somwhere in the * classpath where it will override the default MiCasa server properties * file. + * + *

The UGI or universal game identifier is a string that + * is used to uniquely identify every type of game and also to classify it + * according to meaningful keywords. It is best described with a few + * examples: + * + *

+ * backgammon,board,strategy
+ * spades,card,partner
+ * yahtzee,dice
+ * 
+ * + * As you can see, a UGI should start with an identifier uniquely + * identifying the type of game and can be followed by a list of keywords + * that classify it as a member of a particular category of games + * (eg. board, card, dice, partner game, strategy game). A game can belong + * to multiple categories. + * + *

As long as the UGIs in use by a particular server make some kind of + * sense, the client will be able to use them to search for lobbies + * containing games of similar types using the provided facilities. */ -public class LobbyRegistry +public class LobbyRegistry implements LobbyCodes { - /** - * A simple class for keeping track of information for each lobby in - * operation on the server. - */ - public static class Lobby - { - /** The object id of the lobby place object. */ - public int placeOid; - - /** The human readable name of the lobby. */ - public String name; - } - /** * Initializes the registry. It will use the supplied configuration * instance to determine which lobbies to load, etc. + * + * @param config the server configuration. + * @param invmgr a reference to the server's invocation manager. */ - public void init (Config config) + public void init (Config config, InvocationManager invmgr) { _config = config; + // register our invocation service handler + LobbyProvider provider = new LobbyProvider(this); + invmgr.registerProvider(MODULE_NAME, provider); + // create our lobby managers String[] lmgrs = config.getValue(LOBIDS_KEY, (String[])null); if (lmgrs == null || lmgrs.length == 0) { Log.warning("No lobbies specified in config file (via '" + - LOBIDS_KEY + "' parameter."); + LOBIDS_KEY + "' parameter)."); } else { for (int i = 0; i < lmgrs.length; i++) { @@ -85,9 +105,9 @@ public class LobbyRegistry _config.getProperties(MiCasaServer.CONFIG_KEY); props = PropertiesUtil.getSubProperties(props, lobbyId); - // get the lobby manager class + // get the lobby manager class and UGI String lmgrClass = props.getProperty("mgrclass"); - if (lmgrClass == null) { + if (StringUtil.blank(lmgrClass)) { throw new Exception("Missing 'mgrclass' definition in " + "lobby configuration."); } @@ -96,8 +116,10 @@ public class LobbyRegistry LobbyManager lobmgr = (LobbyManager) MiCasaServer.plreg.createPlace(Class.forName(lmgrClass)); - // initialize the manager - lobmgr.init(props); + // initialize the lobby manager. it will call lobbyReady() + // when it has obtained a reference to its lobby object and is + // ready to roll + lobmgr.init(this, props); } catch (Exception e) { Log.warning("Unable to create lobby manager " + @@ -106,20 +128,85 @@ public class LobbyRegistry } /** - * Returns information about all lobbies matching the supplied pattern - * string. Presently the pattern is interpreted as a simple prefix and - * all lobbies matching that prefix will be returned. + * Returns information about all lobbies hosting games in the + * specified category. * - * @param pattern the pattern to match or null if all lobbies should - * be returned. + * @param requester the body object of the client requesting the lobby + * list (which can be used to filter the list based on their + * capabilities). + * @param category the category of game for which the lobbies are + * desired. + * @param target the list into which the matching lobbies will be + * deposited. */ - public Lobby[] getLobbies (String pattern) + public void getLobbies (BodyObject requester, String category, + List target) { - return null; + ArrayList list = (ArrayList)_lobbies.get(category); + if (list != null) { + target.addAll(list); + } } + /** + * Returns an array containing the category identifiers of all the + * categories in which lobbies have been registered with the registry. + * + * @param requester the body object of the client requesting the + * cateogory list (which can be used to filter the list based on their + * capabilities). + */ + public String[] getCategories (BodyObject requester) + { + // might want to cache this some day so that we don't recreate it + // every time someone wants it. we can cache the array until the + // category count changes + String[] cats = new String[_lobbies.size()]; + Iterator iter = _lobbies.keySet().iterator(); + for (int i = 0; iter.hasNext(); i++) { + cats[i] = (String)iter.next(); + } + return cats; + } + + /** + * Called by our lobby managers once they have started up and are + * ready to do their lobby duties. + */ + protected void lobbyReady (int placeOid, String gameIdent, String name) + { + // create a lobby record + Lobby record = new Lobby(placeOid, gameIdent, name); + + // and register it in all the right lobby tables + StringTokenizer tok = new StringTokenizer(gameIdent, ","); + while (tok.hasMoreTokens()) { + String category = tok.nextToken(); + registerLobby(category, record); + } + } + + /** Registers the supplied lobby in the specified category table. */ + protected void registerLobby (String category, Lobby record) + { + ArrayList catlist = (ArrayList)_lobbies.get(category); + if (catlist == null) { + catlist = new ArrayList(); + _lobbies.put(category, catlist); + } + catlist.add(record); + Log.info("Registered lobby [cat=" + category + + ", record=" + record + "]."); + } + + /** A reference to the server config object from which we loaded all + * of our configuration information. */ protected Config _config; + /** A table containing references to all of our lobby records (in the + * form of category lists. */ + protected HashMap _lobbies = new HashMap(); + /** The configuration key for the lobby managers list. */ protected static final String LOBIDS_KEY = MiCasaServer.CONFIG_KEY + ".lobby_ids"; diff --git a/src/java/com/threerings/micasa/lobby/LobbySelector.java b/src/java/com/threerings/micasa/lobby/LobbySelector.java new file mode 100644 index 000000000..bc97af085 --- /dev/null +++ b/src/java/com/threerings/micasa/lobby/LobbySelector.java @@ -0,0 +1,215 @@ +// +// $Id: LobbySelector.java,v 1.1 2001/10/04 23:41:44 mdb Exp $ + +package com.threerings.micasa.client; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.event.*; + +import java.util.*; + +import javax.swing.*; +import javax.swing.event.AncestorEvent; +import javax.swing.event.AncestorListener; + +import com.samskivert.util.StringUtil; + +import com.threerings.cocktail.party.data.PlaceObject; + +import com.threerings.micasa.Log; +import com.threerings.micasa.lobby.Lobby; +import com.threerings.micasa.lobby.LobbyService; +import com.threerings.micasa.util.MiCasaContext; + +/** + * The lobby selector displays a drop-down box listing the categories of + * lobbies available on this server and when a category is selected, it + * displays a list of the lobbies that are available in that category. If + * a lobby is double-clicked, it moves the client into that lobby room. + */ +public class LobbySelector + extends JPanel implements ActionListener +{ + /** + * Constructs a new lobby selector component. It will wait until it is + * visible before issuing a get categories request to download a list + * of categories. + */ + public LobbySelector (MiCasaContext ctx) + { + setLayout(new BorderLayout()); + + // keep this around for later + _ctx = ctx; + + // create our drop-down box with a bogus entry at the moment + String[] options = new String[] { CAT_FIRST_ITEM }; + _combo = new JComboBox(options); + _combo.addActionListener(this); + add(_combo, BorderLayout.NORTH); + + // and create our empty lobby list + _loblist = new JList(); + _loblist.setCellRenderer(new LobbyCellRenderer()); + // add a mouse listener that tells us about double clicks + MouseListener ml = new MouseAdapter() { + public void mouseClicked (MouseEvent e) { + if (e.getClickCount() == 2) { + int index = _loblist.locationToIndex(e.getPoint()); + Object item = _loblist.getSelectedValue(); + enterLobby((Lobby)item); + } + } + }; + _loblist.addMouseListener(ml); + add(_loblist, BorderLayout.CENTER); + + // we'll need to know when we are made visible because we'll want + // to issue a request for our categories when that happens + addAncestorListener(new AncestorListener () { + public void ancestorAdded (AncestorEvent evt) { + // issue a get categories request to get the category list + LobbyService.getCategories( + _ctx.getClient(), LobbySelector.this); + } + public void ancestorRemoved (AncestorEvent evt) { + } + public void ancestorMoved (AncestorEvent evt) { + } + }); + } + + /** + * Called when the user selects a category or double-clicks on a + * lobby. + */ + public void actionPerformed (ActionEvent evt) + { + if (evt.getSource() == _combo) { + String selcat = (String)_combo.getSelectedItem(); + if (!selcat.equals(CAT_FIRST_ITEM)) { + selectCategory(selcat); + } + } + } + + /** + * Called in response to a {@link LobbyService#getCategories} + * invocation service request. + */ + public void handleGotCategories (int invid, String[] categories) + { + // append these to our "unselected" item + for (int i = 0; i < categories.length; i++) { + _combo.addItem(categories[i]); + } + } + + /** + * Called in response to a {@link LobbyService#getLobbies} invocation + * service request. + */ + public void handleGotLobbies (int invid, List lobbies) + { + // create a list model for this category + DefaultListModel model = new DefaultListModel(); + + // populate it with the lobby info + Iterator iter = lobbies.iterator(); + while (iter.hasNext()) { + model.addElement(iter.next()); + } + + // stick it in the table + _catlists.put(_pendingCategory, model); + + // finally tell the lobby list to update the display (which we do + // by setting the combo box to this category in case the luser + // decided to try to select some new category while we weren't + // looking) + _combo.setSelectedItem(_pendingCategory); + + // clear out our pending category indicator + _pendingCategory = null; + } + + /** + * Called in response to a lobby invocation request when the request + * failed. + */ + public void handleRequestFailed (int invid, String reason) + { + Log.info("Request failed [invid=" + invid + + ", reason=" + reason + "]."); + + // clear out our pending category indicator + _pendingCategory = null; + } + + /** + * Fetches the list of lobbies available in a particular category (if + * they haven't already been fetched) and displays them in the lobby + * list. + */ + protected void selectCategory (String category) + { + DefaultListModel model = (DefaultListModel)_catlists.get(category); + if (model != null) { + _loblist.setModel(model); + + } else if (_pendingCategory == null) { + // make a note that we're loading up this category + _pendingCategory = category; + // issue a request to load up the lobbies in this category + LobbyService.getLobbies(_ctx.getClient(), category, this); + + } else { + Log.info("Ignoring category select request because " + + "one is outstanding [pcat=" + _pendingCategory + + ", newcat=" + category + "]."); + } + } + + /** Called when the user selects a lobby from the lobby list. */ + protected void enterLobby (Lobby lobby) + { + // make sure we're not already in this lobby + PlaceObject plobj = _ctx.getLocationDirector().getPlaceObject(); + if (plobj != null && plobj.getOid() == lobby.placeOid) { + return; + } + + // otherwise request that we go there + _ctx.getLocationDirector().moveTo(lobby.placeOid); + + Log.info("Entering lobby " + lobby + "."); + + } + + /** Used to render Lobby instances in our lobby list. */ + protected static class LobbyCellRenderer + extends DefaultListCellRenderer + { + public Component getListCellRendererComponent( + JList list, + Object value, + int index, + boolean isSelected, + boolean cellHasFocus) { + // use the lobby's name rather than the value of toString() + value = ((Lobby)value).name; + return super.getListCellRendererComponent( + list, value, index, isSelected, cellHasFocus); + } + } + + protected MiCasaContext _ctx; + protected JComboBox _combo; + protected JList _loblist; + + protected HashMap _catlists = new HashMap(); + protected String _pendingCategory; + + protected static final String CAT_FIRST_ITEM = ""; +} diff --git a/src/java/com/threerings/micasa/lobby/LobbyService.java b/src/java/com/threerings/micasa/lobby/LobbyService.java new file mode 100644 index 000000000..6c3beeb94 --- /dev/null +++ b/src/java/com/threerings/micasa/lobby/LobbyService.java @@ -0,0 +1,62 @@ +// +// $Id: LobbyService.java,v 1.1 2001/10/04 23:41:44 mdb Exp $ + +package com.threerings.micasa.lobby; + +import com.threerings.cocktail.cher.client.Client; +import com.threerings.cocktail.cher.client.InvocationDirector; + +import com.threerings.micasa.Log; + +/** + * This class provides an interface to the various parlor services that + * are directly invokable by the client (by means of the invocation + * services). + */ +public class LobbyService + implements LobbyCodes +{ + /** + * Requests the list of lobby cateogories that are available on this + * server. + * + * @param client a connected, operational client instance. + * @param rsptarget the object reference that will receive and process + * the response. The response will come in the form of a method call + * to handleGotCategories or + * handleRequestFailed. + * + * @return the invocation request id of the generated request. + */ + public static int getCategories (Client client, Object rsptarget) + { + InvocationDirector invdir = client.getInvocationDirector(); + Log.info("Sending get categories."); + return invdir.invoke( + MODULE_NAME, GET_CATEGORIES_REQUEST, null, rsptarget); + } + + /** + * Requests information on all active lobbies that match the specified + * category. + * + * @param client a connected, operational client instance. + * @param category the category of game for which a list of lobbies is + * desired. + * @param rsptarget the object reference that will receive and process + * the response. The response will come in the form of a method call + * to handleLobbyList or + * handleRequestFailed. + * + * @return the invocation request id of the generated request. + */ + public static int getLobbies ( + Client client, String category, Object rsptarget) + { + InvocationDirector invdir = client.getInvocationDirector(); + Object[] args = new Object[] { category }; + Log.info("Sending get lobbies [category=" + category + "]."); + return invdir.invoke( + MODULE_NAME, GET_LOBBIES_REQUEST, args, rsptarget); + } +} diff --git a/src/java/com/threerings/micasa/server/MiCasaServer.java b/src/java/com/threerings/micasa/server/MiCasaServer.java index 7e4a0e225..2c3416661 100644 --- a/src/java/com/threerings/micasa/server/MiCasaServer.java +++ b/src/java/com/threerings/micasa/server/MiCasaServer.java @@ -1,5 +1,5 @@ // -// $Id: MiCasaServer.java,v 1.1 2001/10/03 23:24:09 mdb Exp $ +// $Id: MiCasaServer.java,v 1.2 2001/10/04 23:41:44 mdb Exp $ package com.threerings.micasa.server; @@ -11,6 +11,7 @@ import com.threerings.cocktail.party.server.PartyServer; import com.threerings.parlor.server.ParlorManager; import com.threerings.micasa.Log; +import com.threerings.micasa.lobby.LobbyRegistry; /** * This class is the main entry point and general organizer of everything @@ -27,6 +28,9 @@ public class MiCasaServer extends PartyServer /** The parlor manager in operation on this server. */ public static ParlorManager parmgr = new ParlorManager(); + /** The lobby registry operating on this server. */ + public static LobbyRegistry lobreg = new LobbyRegistry(); + /** * Initializes all of the server services and prepares for operation. */ @@ -42,6 +46,9 @@ public class MiCasaServer extends PartyServer // initialize our parlor manager parmgr.init(config, invmgr); + // initialize the lobby registry + lobreg.init(config, invmgr); + // create our connection provider String dbmap = config.getValue(DBMAP_KEY, DEF_DBMAP); conprov = new StaticConnectionProvider(dbmap);