Split Vilya into core and aslib submodules.

This ensures that our ActionScript artifact is properly published to Maven
Central for all to use.

I'm still putting off creating vilya-tools. Later, later...
This commit is contained in:
Michael Bayne
2013-05-08 11:09:31 -07:00
parent 6565145f53
commit ff80efdfe4
490 changed files with 223 additions and 88 deletions
+105
View File
@@ -0,0 +1,105 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.threerings</groupId>
<artifactId>vilya-parent</artifactId>
<version>1.6-SNAPSHOT</version>
</parent>
<artifactId>vilya</artifactId>
<packaging>jar</packaging>
<name>Vilya Core</name>
<dependencies>
<!-- exported dependencies -->
<dependency>
<groupId>com.threerings</groupId>
<artifactId>ooo-util</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>com.threerings</groupId>
<artifactId>narya</artifactId>
<version>1.14</version>
</dependency>
<dependency>
<groupId>com.threerings</groupId>
<artifactId>nenya</artifactId>
<version>1.5</version>
</dependency>
<!-- optional dependencies -->
<dependency>
<groupId>com.threerings</groupId>
<artifactId>nenya-tools</artifactId>
<version>1.5</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>commons-digester</groupId>
<artifactId>commons-digester</artifactId>
<version>2.0</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.samskivert</groupId>
<artifactId>depot</artifactId>
<version>1.6.1</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.8.0.GA</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.megginson.sax</groupId>
<artifactId>xml-writer</artifactId>
<version>0.2</version>
<optional>true</optional>
</dependency>
<!-- test dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.7</version>
<configuration>
<quiet>true</quiet>
<show>public</show>
<links>
<link>http://samskivert.github.com/samskivert/apidocs/</link>
<link>http://depot.googlecode.com/svn/apidocs/</link>
<link>http://docs.guava-libraries.googlecode.com/git/javadoc/</link>
<link>http://google-web-toolkit.googlecode.com/svn/javadoc/2.1/</link>
</links>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.6</version>
<configuration>
<includes>
<include>com/threerings/**/*Test.java</include>
</includes>
<systemPropertyVariables>
<test_dir>target/test-classes</test_dir>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,33 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa;
import com.samskivert.util.Logger;
/**
* Contains a reference to the log object used by the MiCasa package.
*/
public class Log
{
/** The static log instance configured for use by this package. */
public static Logger log = Logger.getLogger("com.threerings.micasa");
}
@@ -0,0 +1,338 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.client;
import java.util.StringTokenizer;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import com.samskivert.swing.GroupLayout;
import com.samskivert.swing.HGroupLayout;
import com.samskivert.swing.VGroupLayout;
import com.samskivert.swing.event.AncestorAdapter;
import com.threerings.util.Name;
import com.threerings.crowd.chat.client.ChatDisplay;
import com.threerings.crowd.chat.data.ChatCodes;
import com.threerings.crowd.chat.data.ChatMessage;
import com.threerings.crowd.chat.data.SystemMessage;
import com.threerings.crowd.chat.data.UserMessage;
import com.threerings.crowd.client.OccupantObserver;
import com.threerings.crowd.client.PlaceView;
import com.threerings.crowd.data.OccupantInfo;
import com.threerings.crowd.data.PlaceObject;
import com.threerings.crowd.util.CrowdContext;
import static com.threerings.micasa.Log.log;
public class ChatPanel extends JPanel
implements ActionListener, ChatDisplay, OccupantObserver, PlaceView
{
public ChatPanel (CrowdContext ctx)
{
this(ctx, true);
}
public ChatPanel (CrowdContext ctx, boolean showHintLabel)
{
// keep this around for later
_ctx = ctx;
// register ourselves with the chat director
_ctx.getChatDirector().addChatDisplay(this);
// register as an occupant observer
_ctx.getOccupantDirector().addOccupantObserver(this);
GroupLayout gl = new VGroupLayout(GroupLayout.STRETCH);
gl.setOffAxisPolicy(GroupLayout.STRETCH);
setLayout(gl);
// create our scrolling chat text display
_text = new JTextPane();
_text.setEditable(false);
add(new JScrollPane(_text));
// create our styles and add those to the text pane
createStyles(_text);
// add a label for the text entry stuff
if (showHintLabel) {
add(new JLabel("Type here to chat:"), GroupLayout.FIXED);
}
// create a horizontal group for the text entry bar
gl = new HGroupLayout(GroupLayout.STRETCH, GroupLayout.STRETCH, 5, GroupLayout.LEFT);
JPanel epanel = new JPanel(gl);
epanel.add(_entry = new JTextField());
_entry.setActionCommand("send");
_entry.addActionListener(this);
_entry.setEnabled(false);
_send = new JButton("Chat!");
_send.setEnabled(false);
_send.addActionListener(this);
_send.setActionCommand("send");
epanel.add(_send, GroupLayout.FIXED);
add(epanel, GroupLayout.FIXED);
// listen to ancestor events to request focus when added
addAncestorListener(new AncestorAdapter() {
@Override
public void ancestorAdded (AncestorEvent e) {
if (_focus) {
_entry.requestFocusInWindow();
}
}
});
}
/**
* For applications where the chat box has extremely limited space,
* the send button can be removed to leave more space for the text
* input box.
*/
public void removeSendButton ()
{
if (_send.isVisible()) {
// _send.getParent().remove(_send);
_send.setVisible(false);
}
}
/**
* Sets whether the chat box text entry field requests the keyboard
* focus when the panel receives {@link
* AncestorListener#ancestorAdded} or {@link PlaceView#willEnterPlace}
* events.
*/
public void setRequestFocus (boolean focus)
{
_focus = focus;
}
protected void createStyles (JTextPane text)
{
StyleContext sctx = StyleContext.getDefaultStyleContext();
Style defstyle = sctx.getStyle(StyleContext.DEFAULT_STYLE);
_nameStyle = text.addStyle("name", defstyle);
StyleConstants.setForeground(_nameStyle, Color.blue);
_msgStyle = text.addStyle("msg", defstyle);
StyleConstants.setForeground(_msgStyle, Color.black);
_errStyle = text.addStyle("err", defstyle);
StyleConstants.setForeground(_errStyle, Color.red);
_noticeStyle = text.addStyle("notice", defstyle);
StyleConstants.setForeground(_noticeStyle, Color.magenta);
}
// documentation inherited
public void actionPerformed (ActionEvent e)
{
String cmd = e.getActionCommand();
if (cmd.equals("send")) {
sendText();
} else {
System.out.println("Unknown action event: " + cmd);
}
}
// documentation inherited
public void occupantEntered (OccupantInfo info)
{
displayOccupantMessage("*** " + info.username + " entered.");
}
// documentation inherited
public void occupantLeft (OccupantInfo info)
{
displayOccupantMessage("*** " + info.username + " left.");
}
// documentation inherited
public void occupantUpdated (OccupantInfo oinfo, OccupantInfo info)
{
}
protected void displayOccupantMessage (String message)
{
append(message + "\n", _noticeStyle);
}
protected void sendText ()
{
String text = _entry.getText();
// if the message to send begins with /tell then parse it and
// generate a tell request rather than a speak request
if (text.startsWith("/tell")) {
StringTokenizer tok = new StringTokenizer(text);
// there should be at least three tokens: '/tell target word'
if (tok.countTokens() < 3) {
displayError("Usage: /tell username message");
return;
}
// skip the /tell and grab the username
tok.nextToken();
String username = tok.nextToken();
// now strip off everything up to the username to get the
// message
int uidx = text.indexOf(username);
String message = text.substring(uidx + username.length()).trim();
// request to send this text as a tell message
_ctx.getChatDirector().requestTell(new Name(username), message, null);
} else if (text.startsWith("/clear")) {
// clear the chat box
_ctx.getChatDirector().clearDisplays();
} else {
// request to send this text as a chat message
_ctx.getChatDirector().requestSpeak(null, text, ChatCodes.DEFAULT_MODE);
}
// clear out the input because we sent a request
_entry.setText("");
}
// documentation inherited from interface ChatDisplay
public void clear ()
{
_text.setText("");
}
// documentation inherited from interface ChatDisplay
public boolean displayMessage (ChatMessage message, boolean displayed)
{
if (message instanceof UserMessage) {
UserMessage msg = (UserMessage) message;
if (msg.localtype == ChatCodes.USER_CHAT_TYPE) {
append("[" + msg.speaker + " whispers] ", _nameStyle);
append(msg.message + "\n", _msgStyle);
} else {
append("<" + msg.speaker + "> ", _nameStyle);
append(msg.message + "\n", _msgStyle);
}
return true;
} else if (message instanceof SystemMessage) {
append(message.message + "\n", _noticeStyle);
return true;
} else {
log.warning("Received unknown message type [message=" +
message + "].");
return false;
}
}
protected void displayError (String message)
{
append(message + "\n", _errStyle);
}
/**
* Append the specified text in the specified style.
*/
protected void append (String text, Style style)
{
Document doc = _text.getDocument();
try {
doc.insertString(doc.getLength(), text, style);
} catch (BadLocationException ble) {
log.warning("Unable to insert text!? [error=" + ble + "].");
}
}
public void willEnterPlace (PlaceObject place)
{
// enable our chat input elements since we're now somewhere that
// we can chat
_entry.setEnabled(true);
_send.setEnabled(true);
if (_focus) {
_entry.requestFocusInWindow();
}
}
// documentation inherited
public void didLeavePlace (PlaceObject place)
{
// nothing doing
}
@Override
public Dimension getPreferredSize ()
{
Dimension size = super.getPreferredSize();
// always prefer a sensible but not overly large width. this also
// prevents us from inheriting a foolishly large preferred width
// from the JTextPane which sometimes decides it wants to be as
// wide as its widest line of text rather than wrap that line of
// text.
size.width = PREFERRED_WIDTH;
return size;
}
protected CrowdContext _ctx;
protected boolean _focus = true;
protected JComboBox _roombox;
protected JTextPane _text;
protected JButton _send;
protected JTextField _entry;
protected Style _nameStyle;
protected Style _msgStyle;
protected Style _errStyle;
protected Style _noticeStyle;
/** A width that isn't so skinny that the text is teeny. */
protected static final int PREFERRED_WIDTH = 200;
}
@@ -0,0 +1,148 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.client;
import java.awt.event.ActionEvent;
import com.samskivert.swing.Controller;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.SessionObserver;
import com.threerings.crowd.data.BodyObject;
import com.threerings.micasa.data.MiCasaBootstrapData;
import com.threerings.micasa.util.MiCasaContext;
import static com.threerings.micasa.Log.log;
/**
* Responsible for top-level control of the client user interface.
*/
public class ClientController extends Controller
implements SessionObserver
{
/**
* Creates a new client controller. The controller will set everything
* up in preparation for logging on.
*/
public ClientController (MiCasaContext ctx, MiCasaFrame frame)
{
// we'll want to keep these around
_ctx = ctx;
_frame = frame;
// we want to know about logon/logoff
_ctx.getClient().addClientObserver(this);
// create the logon panel and display it
_logonPanel = new LogonPanel(_ctx);
_frame.setPanel(_logonPanel);
}
@Override
public boolean handleAction (ActionEvent action)
{
String cmd = action.getActionCommand();
if (cmd.equals("logoff")) {
// request that we logoff
_ctx.getClient().logoff(true);
return true;
}
log.info("Unhandled action: " + action);
return false;
}
// documentation inherited
public void clientWillLogon (Client client)
{
// nada
}
// documentation inherited
public void clientDidLogon (Client client)
{
log.info("Client did logon [client=" + client + "].");
// keep the body object around for stuff
_body = (BodyObject)client.getClientObject();
// figure out where to go
int moveOid = -1;
// hacky hack
String jumpOidStr = null;
try {
jumpOidStr = System.getProperty("jumpoid");
} catch (SecurityException se) {
log.info("Not checking for jumpOid as we're in an applet.");
}
if (jumpOidStr != null) {
try {
moveOid = Integer.parseInt(jumpOidStr);
} catch (NumberFormatException nfe) {
log.warning("Invalid jump oid [oid=" + jumpOidStr +
", err=" + nfe + "].");
}
} else if (_body.location != null) {
// if we were already in a location, go there
moveOid = _body.location.placeOid;
} else {
// otherwise head to the default lobby to start things off
MiCasaBootstrapData data = (MiCasaBootstrapData)
client.getBootstrapData();
moveOid = data.defLobbyOid;
}
if (moveOid > 0) {
_ctx.getLocationDirector().moveTo(moveOid);
}
}
// documentation inherited
public void clientObjectDidChange (Client client)
{
// regrab our body object
_body = (BodyObject)client.getClientObject();
}
// documentation inherited
public void clientDidLogoff (Client client)
{
log.info("Client did logoff [client=" + client + "].");
// reinstate the logon panel
_frame.setPanel(_logonPanel);
}
protected MiCasaContext _ctx;
protected MiCasaFrame _frame;
protected BodyObject _body;
// our panels
protected LogonPanel _logonPanel;
}
@@ -0,0 +1,248 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.client;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import com.samskivert.swing.GroupLayout;
import com.samskivert.swing.HGroupLayout;
import com.samskivert.swing.VGroupLayout;
import com.threerings.util.MessageBundle;
import com.threerings.util.Name;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.ClientObserver;
import com.threerings.presents.client.LogonException;
import com.threerings.presents.net.Credentials;
import com.threerings.presents.net.UsernamePasswordCreds;
import com.threerings.micasa.util.MiCasaContext;
public class LogonPanel extends JPanel
implements ActionListener, ClientObserver
{
public LogonPanel (MiCasaContext ctx)
{
// keep these around for later
_ctx = ctx;
_msgs = _ctx.getMessageManager().getBundle("micasa.logon");
setLayout(new VGroupLayout());
// stick the logon components into a panel that will stretch them
// to a sensible width
JPanel box = new JPanel(
new VGroupLayout(VGroupLayout.NONE, VGroupLayout.STRETCH,
5, VGroupLayout.CENTER)) {
@Override
public Dimension getPreferredSize () {
Dimension psize = super.getPreferredSize();
psize.width = Math.max(psize.width, 300);
return psize;
}
};
add(box);
// try obtaining our title text from a system property
String tstr = null;
try {
tstr = System.getProperty("logon.title");
} catch (Throwable t) {
}
if (tstr == null) {
tstr = "Mi Casa!";
}
// create a big fat label
JLabel title = new JLabel(tstr, JLabel.CENTER);
title.setFont(new Font("Helvetica", Font.BOLD, 24));
box.add(title);
// create the username bar
JPanel bar = new JPanel(new HGroupLayout(GroupLayout.STRETCH));
bar.add(new JLabel(_msgs.get("m.username")), GroupLayout.FIXED);
_username = new JTextField();
_username.setActionCommand("skipToPassword");
_username.addActionListener(this);
bar.add(_username);
box.add(bar);
// create the password bar
bar = new JPanel(new HGroupLayout(GroupLayout.STRETCH));
bar.add(new JLabel(_msgs.get("m.password")), GroupLayout.FIXED);
_password = new JPasswordField();
_password.setActionCommand("logon");
_password.addActionListener(this);
bar.add(_password);
box.add(bar);
// create the logon button bar
HGroupLayout gl = new HGroupLayout(GroupLayout.NONE);
gl.setJustification(GroupLayout.RIGHT);
bar = new JPanel(gl);
_logon = new JButton(_msgs.get("m.logon"));
_logon.setActionCommand("logon");
_logon.addActionListener(this);
bar.add(_logon);
box.add(bar);
box.add(new JLabel(_msgs.get("m.status")));
_status = new JTextArea() {
@Override
public Dimension getPreferredScrollableViewportSize ()
{
return new Dimension(10, 100);
}
};
_status.setEditable(false);
JScrollPane scroller = new JScrollPane(_status);
box.add(scroller);
// we'll want to listen for logon failure
_ctx.getClient().addClientObserver(this);
// start with focus in the username field
_username.requestFocusInWindow();
}
public void actionPerformed (ActionEvent e)
{
String cmd = e.getActionCommand();
if (cmd.equals("skipToPassword")) {
_password.requestFocusInWindow();
} else if (cmd.equals("logon")) {
logon();
} else {
System.out.println("Unknown action event: " + cmd);
}
}
// documentation inherited from interface
public void clientWillLogon (Client client)
{
// nada
}
// documentation inherited from interface
public void clientDidLogon (Client client)
{
_status.append(_msgs.get("m.logon_success") + "\n");
}
// documentation inherited from interface
public void clientDidLogoff (Client client)
{
_status.append(_msgs.get("m.logged_off") + "\n");
setLogonEnabled(true);
}
// documentation inherited from interface
public void clientDidClear (Client client)
{
// nada
}
// documentation inherited from interface
public void clientFailedToLogon (Client client, Exception cause)
{
String msg;
if (cause instanceof LogonException) {
msg = MessageBundle.compose("m.logon_failed", cause.getMessage());
} else {
msg = MessageBundle.tcompose("m.logon_failed", cause.getMessage());
}
_status.append(_msgs.xlate(msg) + "\n");
setLogonEnabled(true);
}
// documentation inherited from interface
public void clientObjectDidChange (Client client)
{
// nothing we can do here...
}
// documentation inherited from interface
public void clientConnectionFailed (Client client, Exception cause)
{
String msg = MessageBundle.tcompose("m.connection_failed",
cause.getMessage());
_status.append(_msgs.xlate(msg) + "\n");
setLogonEnabled(true);
}
// documentation inherited from interface
public boolean clientWillLogoff (Client client)
{
// no vetoing here
return true;
}
protected void logon ()
{
// disable further logon attempts until we hear back
setLogonEnabled(false);
Name username = new Name(_username.getText().trim());
String password = new String(_password.getPassword()).trim();
String server = _ctx.getClient().getHostname();
int port = _ctx.getClient().getPorts()[0];
String msg = MessageBundle.tcompose("m.logging_on",
server, String.valueOf(port));
_status.append(_msgs.xlate(msg) + "\n");
// configure the client with some credentials and logon
Credentials creds = new UsernamePasswordCreds(username, password);
Client client = _ctx.getClient();
client.setCredentials(creds);
client.logon();
}
protected void setLogonEnabled (boolean enabled)
{
_username.setEnabled(enabled);
_password.setEnabled(enabled);
_logon.setEnabled(enabled);
}
protected MiCasaContext _ctx;
protected MessageBundle _msgs;
protected JTextField _username;
protected JPasswordField _password;
protected JButton _logon;
protected JTextArea _status;
}
@@ -0,0 +1,111 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.client;
import java.io.IOException;
import com.samskivert.swing.util.SwingUtil;
import com.threerings.util.Name;
import com.threerings.presents.client.Client;
import com.threerings.presents.net.UsernamePasswordCreds;
import static com.threerings.micasa.Log.log;
/**
* The micasa app is the main point of entry for the MiCasa client application. It creates and
* initializes the myriad components of the client and sets all the proper wheels in motion.
*/
public class MiCasaApp
{
public void init ()
throws IOException
{
// create a frame
_frame = new MiCasaFrame();
// create our client instance
String cclass = null;
try {
cclass = System.getProperty("client");
} catch (Throwable t) {
// security manager in effect, no problem
}
if (cclass == null) {
cclass = MiCasaClient.class.getName();
}
try {
_client = (MiCasaClient)Class.forName(cclass).newInstance();
} catch (Exception e) {
log.warning("Unable to instantiate client class", "cclass", cclass, e);
}
// initialize our client instance
_client.init(_frame);
}
public void run (String server, String username, String password)
{
// position everything and show the frame
_frame.setSize(800, 600);
SwingUtil.centerWindow(_frame);
_frame.setVisible(true);
Client client = _client.getContext().getClient();
log.info("Using", "server", server);
client.setServer(server, Client.DEFAULT_SERVER_PORTS);
// configure the client with some credentials and logon
if (username != null && password != null) {
// create and set our credentials
client.setCredentials(new UsernamePasswordCreds(new Name(username), password));
client.logon();
}
}
public static void main (String[] args)
{
String server = "localhost";
if (args.length > 0) {
server = args[0];
}
String username = (args.length > 1) ? args[1] : null;
String password = (args.length > 2) ? args[2] : null;
MiCasaApp app = new MiCasaApp();
try {
// initialize the app
app.init();
} catch (IOException ioe) {
log.warning("Error initializing application.", ioe);
}
// and run it
app.run(server, username, password);
}
protected MiCasaClient _client;
protected MiCasaFrame _frame;
}
@@ -0,0 +1,129 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.client;
import java.applet.Applet;
import java.io.IOException;
import com.samskivert.swing.util.SwingUtil;
import com.threerings.util.Name;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.ClientAdapter;
import com.threerings.presents.net.UsernamePasswordCreds;
import static com.threerings.micasa.Log.log;
/**
* The MiCasa applet is used to make MiCasa games available via the web
* browser.
*/
public class MiCasaApplet extends Applet
{
/**
* Create the client instance and set things up.
*/
@Override
public void init ()
{
try {
// create a frame
_frame = new MiCasaFrame();
// create our client instance
_client = new MiCasaClient();
_client.init(_frame);
Name username = new Name(requireParameter("username"));
String authkey = requireParameter("authkey");
String server = getCodeBase().getHost();
Client client = _client.getContext().getClient();
// indicate which server to which we should connect
client.setServer(server, Client.DEFAULT_SERVER_PORTS);
// create and set our credentials
client.setCredentials(
new UsernamePasswordCreds(username, authkey));
// we want to hide the client frame when we logoff
client.addClientObserver(new ClientAdapter() {
@Override
public void clientDidLogoff (Client c)
{
_frame.setVisible(false);
}
});
} catch (IOException ioe) {
log.warning("Unable to create client.", ioe);
}
}
protected String requireParameter (String name)
throws IOException
{
String value = getParameter(name);
if (value == null) {
throw new IOException("Applet missing '" + name + "' parameter.");
}
return value;
}
/**
* Display the client frame and really get things going.
*/
@Override
public void start ()
{
if (_client != null) {
// show the frame
_frame.setSize(800, 600);
SwingUtil.centerWindow(_frame);
_frame.setVisible(true);
// and log on
_client.getContext().getClient().logon();
}
}
/**
* Log off and shut on down.
*/
@Override
public void stop ()
{
if (_client != null) {
// hide the frame and log off
_frame.setVisible(false);
Client client = _client.getContext().getClient();
if (client.isLoggedOn()) {
client.logoff(false);
}
}
}
protected MiCasaClient _client;
protected MiCasaFrame _frame;
}
@@ -0,0 +1,220 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.client;
import java.io.IOException;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JPanel;
import com.samskivert.util.Config;
import com.samskivert.util.RunQueue;
import com.threerings.util.MessageManager;
import com.threerings.presents.client.Client;
import com.threerings.presents.dobj.DObjectManager;
import com.threerings.crowd.chat.client.ChatDirector;
import com.threerings.crowd.client.LocationDirector;
import com.threerings.crowd.client.OccupantDirector;
import com.threerings.crowd.client.PlaceView;
import com.threerings.parlor.client.ParlorDirector;
import com.threerings.micasa.util.MiCasaContext;
/**
* The MiCasa client takes care of instantiating all of the proper
* managers and loading up all of the necessary configuration and getting
* the client bootstrapped. It can be extended by games that require an
* extended context implementation.
*/
public class MiCasaClient
{
/**
* Initializes a new client and provides it with a frame in which to
* display everything.
*/
public void init (MiCasaFrame frame)
throws IOException
{
// create our context
_ctx = createContextImpl();
// create the directors/managers/etc. provided by the context
createContextServices();
// for test purposes, hardcode the server info
_client.setServer("localhost", Client.DEFAULT_SERVER_PORTS);
// keep this for later
_frame = frame;
// log off when they close the window
_frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing (WindowEvent evt) {
// if we're logged on, log off
if (_client.isLoggedOn()) {
_client.logoff(true);
} else {
// otherwise get the heck out
System.exit(0);
}
}
});
// create our client controller and stick it in the frame
_frame.setController(new ClientController(_ctx, _frame));
}
/**
* Returns a reference to the context in effect for this client. This
* reference is valid for the lifetime of the application.
*/
public MiCasaContext getContext ()
{
return _ctx;
}
/**
* Creates the {@link MiCasaContext} implementation that will be
* passed around to all of the client code. Derived classes may wish
* to override this and create some extended context implementation.
*/
protected MiCasaContext createContextImpl ()
{
return new MiCasaContextImpl();
}
/**
* Creates and initializes the various services that are provided by
* the context. Derived classes that provide an extended context
* should override this method and create their own extended
* services. They should be sure to call
* <code>super.createContextServices</code>.
*/
protected void createContextServices ()
throws IOException
{
// create the handles on our various services
_client = new Client(null, RunQueue.AWT);
// create our managers and directors
_locdir = new LocationDirector(_ctx);
_occdir = new OccupantDirector(_ctx);
_pardtr = new ParlorDirector(_ctx);
_msgmgr = new MessageManager(MESSAGE_MANAGER_PREFIX);
_chatdir = new ChatDirector(_ctx, null);
}
/**
* The context implementation. This provides access to all of the
* objects and services that are needed by the operating client.
*/
protected class MiCasaContextImpl implements MiCasaContext
{
/**
* Apparently the default constructor has default access, rather
* than protected access, even though this class is declared to be
* protected. Why, I don't know, but we need to be able to extend
* this class elsewhere, so we need this.
*/
protected MiCasaContextImpl ()
{
}
public Client getClient ()
{
return _client;
}
public DObjectManager getDObjectManager ()
{
return _client.getDObjectManager();
}
public Config getConfig ()
{
return _config;
}
public LocationDirector getLocationDirector ()
{
return _locdir;
}
public OccupantDirector getOccupantDirector ()
{
return _occdir;
}
public ChatDirector getChatDirector ()
{
return _chatdir;
}
public MessageManager getMessageManager ()
{
return _msgmgr;
}
public ParlorDirector getParlorDirector ()
{
return _pardtr;
}
public void setPlaceView (PlaceView view)
{
// stick the place view into our frame
_frame.setPanel((JPanel)view);
}
public void clearPlaceView (PlaceView view)
{
// we'll just let the next place view replace our old one
}
public MiCasaFrame getFrame ()
{
return _frame;
}
}
protected MiCasaContext _ctx;
protected MiCasaFrame _frame;
protected Config _config = new Config("micasa");
protected Client _client;
protected LocationDirector _locdir;
protected OccupantDirector _occdir;
protected ChatDirector _chatdir;
protected ParlorDirector _pardtr;
protected MessageManager _msgmgr;
/** The prefix prepended to localization bundle names before looking
* them up in the classpath. */
protected static final String MESSAGE_MANAGER_PREFIX = "rsrc.i18n";
}
@@ -0,0 +1,89 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.client;
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import com.samskivert.swing.Controller;
import com.samskivert.swing.ControllerProvider;
/**
* Contains the user interface for the MiCasa client application.
*/
public class MiCasaFrame
extends JFrame implements ControllerProvider
{
/**
* Constructs the top-level MiCasa client frame.
*/
public MiCasaFrame ()
{
this("MiCasa Client");
}
/**
* Constructs the top-level MiCasa client frame with the specified
* window title.
*/
public MiCasaFrame (String title)
{
super(title);
// we'll handle shutting things down ourselves
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
}
/**
* Sets the panel that makes up the entire client display.
*/
public void setPanel (JPanel panel)
{
// remove the old panel
getContentPane().removeAll();
// add the new one
getContentPane().add(panel, BorderLayout.CENTER);
// swing doesn't properly repaint after adding/removing children
panel.revalidate();
repaint();
}
/**
* Sets the controller for the outermost scope. This controller will
* handle all actions that aren't handled by controllers of tigher
* scope.
*/
public void setController (Controller controller)
{
_controller = controller;
}
// documentation inherited
public Controller getController ()
{
return _controller;
}
protected Controller _controller;
}
@@ -0,0 +1,98 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.client;
import javax.swing.DefaultListModel;
import javax.swing.JList;
import com.threerings.crowd.client.OccupantObserver;
import com.threerings.crowd.client.PlaceView;
import com.threerings.crowd.data.OccupantInfo;
import com.threerings.crowd.data.PlaceObject;
import com.threerings.crowd.util.CrowdContext;
/**
* 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 (CrowdContext 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.getOccupantDirector().addOccupantObserver(this);
}
// documentation inherited
public void willEnterPlace (PlaceObject plobj)
{
// add all of the occupants of the place to our list
for (OccupantInfo info : plobj.occupantInfo) {
_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 oinfo, OccupantInfo info)
{
// nothing doing
}
/** Our client context. */
protected CrowdContext _ctx;
/** A list model that provides a vector interface. */
protected DefaultListModel _model;
}
@@ -0,0 +1,34 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.data;
import com.threerings.presents.net.BootstrapData;
/**
* Extends the basic Presents bootstrap data and provides some bootstrap
* information specific to the MiCasa services.
*/
public class MiCasaBootstrapData extends BootstrapData
{
/** The oid of the default lobby. */
public int defLobbyOid;
}
@@ -0,0 +1,59 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.lobby;
import com.threerings.io.SimpleStreamableObject;
/**
* A simple class for keeping track of information for each lobby in
* operation on the server.
*/
public class Lobby extends SimpleStreamableObject
{
/** 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 ()
{
}
}
@@ -0,0 +1,111 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.lobby;
import java.util.Properties;
import javax.swing.JComponent;
import javax.swing.JLabel;
import com.samskivert.util.StringUtil;
import com.threerings.crowd.client.PlaceController;
import com.threerings.crowd.data.PlaceConfig;
import com.threerings.parlor.game.data.GameConfig;
import com.threerings.micasa.util.MiCasaContext;
public class LobbyConfig extends PlaceConfig
{
@Override
public PlaceController createController ()
{
return new LobbyController();
}
@Override
public String getManagerClassName ()
{
return "com.threerings.micasa.lobby.LobbyManager";
}
/**
* Derived classes override this function and create the appropriate
* matchmaking user interface component.
*/
public JComponent createMatchMakingView (MiCasaContext ctx)
{
return new JLabel("Match-making view goes here.");
}
/**
* Instantiates and returns a game config instance using the game
* config classname provided by the lobby configuration parameters.
*
* @exception Exception thrown if a problem occurs loading or
* instantiating the class.
*/
public GameConfig getGameConfig ()
throws Exception
{
return (GameConfig)Class.forName(_gameConfigClass).newInstance();
}
/**
* Initializes this lobby config object with the properties that are
* used to configure the lobby. This is called on the server when the
* lobby is loaded.
*/
public void init (Properties config)
throws Exception
{
_gameConfigClass = getConfigValue(config, "game_config");
}
@Override
protected void toString (StringBuilder buf)
{
super.toString(buf);
if (buf.length() > 1) {
buf.append(", ");
}
buf.append("game_config=").append(_gameConfigClass);
}
/** 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.isBlank(value)) {
throw new Exception("Missing '" + key + "' definition in " +
"lobby configuration.");
}
return value;
}
/** The name of the game config class that represents the type of game
* we are matchmaking for in this lobby. */
protected String _gameConfigClass;
}
@@ -0,0 +1,125 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.lobby;
import com.threerings.util.Name;
import com.threerings.crowd.client.PlaceController;
import com.threerings.crowd.client.PlaceView;
import com.threerings.crowd.data.PlaceConfig;
import com.threerings.crowd.data.PlaceObject;
import com.threerings.crowd.util.CrowdContext;
import com.threerings.parlor.client.Invitation;
import com.threerings.parlor.client.InvitationHandler;
import com.threerings.parlor.client.InvitationResponseObserver;
import com.threerings.parlor.game.data.GameConfig;
import com.threerings.micasa.util.MiCasaContext;
import static com.threerings.micasa.Log.log;
public class LobbyController extends PlaceController
implements InvitationHandler, InvitationResponseObserver
{
@Override
public void init (CrowdContext ctx, PlaceConfig config)
{
// cast our context reference
_ctx = (MiCasaContext)ctx;
_config = (LobbyConfig)config;
super.init(ctx, config);
// register ourselves as the invitation handler
_ctx.getParlorDirector().setInvitationHandler(this);
}
@Override
protected PlaceView createPlaceView (CrowdContext ctx)
{
return new LobbyPanel(_ctx, _config);
}
@Override
public void willEnterPlace (PlaceObject plobj)
{
super.willEnterPlace(plobj);
try {
// if a system property has been specified requesting that we
// invite another player, do so now
String invitee = System.getProperty("invitee");
if (invitee != null) {
// create a game config object
try {
GameConfig config = _config.getGameConfig();
_ctx.getParlorDirector().invite(
new Name(invitee), config, this);
} catch (Exception e) {
log.warning("Error instantiating game config.", e);
}
}
} catch (SecurityException se) {
// nothing to see here, move it along...
}
}
// documentation inherited from interface
public void invitationReceived (Invitation invite)
{
log.info("Invitation received [invite=" + invite + "].");
// accept the invitation. we're game...
invite.accept();
}
// documentation inherited from interface
public void invitationCancelled (Invitation invite)
{
log.info("Invitation cancelled " + invite + ".");
}
// documentation inherited from interface
public void invitationAccepted (Invitation invite)
{
log.info("Invitation accepted " + invite + ".");
}
// documentation inherited from interface
public void invitationRefused (Invitation invite, String message)
{
log.info("Invitation refused [invite=" + invite +
", message=" + message + "].");
}
// documentation inherited from interface
public void invitationCountered (Invitation invite, GameConfig config)
{
log.info("Invitation countered [invite=" + invite +
", config=" + config + "].");
}
protected MiCasaContext _ctx;
protected LobbyConfig _config;
}
@@ -0,0 +1,82 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.lobby;
import java.util.Properties;
import com.samskivert.util.StringUtil;
import com.threerings.crowd.data.PlaceObject;
import com.threerings.crowd.server.PlaceManager;
import static com.threerings.micasa.Log.log;
/**
* Takes care of the server side of a particular lobby.
*/
public class LobbyManager extends PlaceManager
{
/**
* Initializes this lobby manager with its configuration properties.
*
* @exception Exception thrown if a configuration error is detected.
*/
public void init (LobbyRegistry lobreg, Properties config)
throws Exception
{
// look up some configuration parameters
_gameIdent = getConfigValue(config, "ugi");
_name = getConfigValue(config, "name");
// let the lobby registry know that we're up and running
lobreg.lobbyReady(_plobj.getOid(), _gameIdent, _name);
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.isBlank(value)) {
throw new Exception("Missing '" + key + "' definition in " +
"lobby configuration.");
}
return value;
}
@Override // from PlaceManager
protected PlaceObject createPlaceObject ()
{
return new LobbyObject();
}
/** The universal game identifier for the game matchmade by this
* lobby. */
protected String _gameIdent;
/** The human readable name of this lobby. */
protected String _name;
}
@@ -0,0 +1,130 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.lobby;
import javax.annotation.Generated;
import com.threerings.presents.data.InvocationMarshaller;
import java.util.List;
/**
* Provides the implementation of the {@link LobbyService} interface
* that marshalls the arguments and delivers the request to the provider
* on the server. Also provides an implementation of the response listener
* interfaces that marshall the response arguments and deliver them back
* to the requesting client.
*/
@Generated(value={"com.threerings.presents.tools.GenServiceTask"},
comments="Derived from LobbyService.java.")
public class LobbyMarshaller extends InvocationMarshaller
implements LobbyService
{
/**
* Marshalls results to implementations of {@code LobbyService.CategoriesListener}.
*/
public static class CategoriesMarshaller extends ListenerMarshaller
implements CategoriesListener
{
/** The method id used to dispatch {@link #gotCategories}
* responses. */
public static final int GOT_CATEGORIES = 1;
// from interface CategoriesMarshaller
public void gotCategories (String[] arg1)
{
sendResponse(GOT_CATEGORIES, new Object[] { arg1 });
}
@Override // from InvocationMarshaller
public void dispatchResponse (int methodId, Object[] args)
{
switch (methodId) {
case GOT_CATEGORIES:
((CategoriesListener)listener).gotCategories(
(String[])args[0]);
return;
default:
super.dispatchResponse(methodId, args);
return;
}
}
}
/**
* Marshalls results to implementations of {@code LobbyService.LobbiesListener}.
*/
public static class LobbiesMarshaller extends ListenerMarshaller
implements LobbiesListener
{
/** The method id used to dispatch {@link #gotLobbies}
* responses. */
public static final int GOT_LOBBIES = 1;
// from interface LobbiesMarshaller
public void gotLobbies (List<Lobby> arg1)
{
sendResponse(GOT_LOBBIES, new Object[] { arg1 });
}
@Override // from InvocationMarshaller
public void dispatchResponse (int methodId, Object[] args)
{
switch (methodId) {
case GOT_LOBBIES:
((LobbiesListener)listener).gotLobbies(
this.<List<Lobby>>cast(args[0]));
return;
default:
super.dispatchResponse(methodId, args);
return;
}
}
}
/** The method id used to dispatch {@link #getCategories} requests. */
public static final int GET_CATEGORIES = 1;
// from interface LobbyService
public void getCategories (LobbyService.CategoriesListener arg1)
{
LobbyMarshaller.CategoriesMarshaller listener1 = new LobbyMarshaller.CategoriesMarshaller();
listener1.listener = arg1;
sendRequest(GET_CATEGORIES, new Object[] {
listener1
});
}
/** The method id used to dispatch {@link #getLobbies} requests. */
public static final int GET_LOBBIES = 2;
// from interface LobbyService
public void getLobbies (String arg1, LobbyService.LobbiesListener arg2)
{
LobbyMarshaller.LobbiesMarshaller listener2 = new LobbyMarshaller.LobbiesMarshaller();
listener2.listener = arg2;
sendRequest(GET_LOBBIES, new Object[] {
arg1, listener2
});
}
}
@@ -0,0 +1,33 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.lobby;
import com.threerings.crowd.data.PlaceObject;
/**
* Presently the lobby object contains nothing specific, but the class
* acts as a placeholder in case lobby-wide fields are needed in the
* future.
*/
public class LobbyObject extends PlaceObject
{
}
@@ -0,0 +1,121 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.lobby;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import com.samskivert.swing.Controller;
import com.samskivert.swing.HGroupLayout;
import com.samskivert.swing.VGroupLayout;
import com.threerings.crowd.client.PlaceView;
import com.threerings.crowd.data.PlaceObject;
import com.threerings.micasa.client.ChatPanel;
import com.threerings.micasa.client.OccupantList;
import com.threerings.micasa.util.MiCasaContext;
/**
* Used to display the interface for the lobbies. It contains a lobby
* selection mechanism, a chat interface and a user interface for whatever
* match-making mechanism is appropriate for this particular lobby.
*/
public class LobbyPanel
extends JPanel implements PlaceView
{
/**
* Constructs a new lobby panel and the associated user interface
* elements.
*/
public LobbyPanel (MiCasaContext ctx, LobbyConfig config)
{
// 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
HGroupLayout hgl = new HGroupLayout(HGroupLayout.STRETCH);
hgl.setOffAxisPolicy(HGroupLayout.STRETCH);
hgl.setJustification(HGroupLayout.RIGHT);
setLayout(hgl);
// create our main panel
VGroupLayout vgl = new VGroupLayout(VGroupLayout.STRETCH);
vgl.setOffAxisPolicy(VGroupLayout.STRETCH);
_main = new JPanel(vgl);
// create our match-making view
_main.add(config.createMatchMakingView(ctx));
// create a chat box and stick that in as well
_main.add(new ChatPanel(ctx));
// now add the main panel into the mix
add(_main);
// create our sidebar panel
vgl = new VGroupLayout(VGroupLayout.STRETCH);
vgl.setOffAxisPolicy(VGroupLayout.STRETCH);
JPanel sidePanel = new JPanel(vgl);
// the sidebar contains a lobby info display...
// ...a lobby selector...
JLabel label = new JLabel("Select a lobby...");
sidePanel.add(label, VGroupLayout.FIXED);
LobbySelector selector = new LobbySelector(ctx);
sidePanel.add(selector);
// and an occupants list
label = new JLabel("People in lobby");
sidePanel.add(label, VGroupLayout.FIXED);
_occupants = new OccupantList(ctx);
sidePanel.add(_occupants);
JButton logoff = new JButton("Logoff");
logoff.addActionListener(Controller.DISPATCHER);
logoff.setActionCommand("logoff");
sidePanel.add(logoff, VGroupLayout.FIXED);
// add our sidebar panel into the mix
add(sidePanel, VGroupLayout.FIXED);
}
// documentation inherited
public void willEnterPlace (PlaceObject plobj)
{
}
// documentation inherited
public void didLeavePlace (PlaceObject plobj)
{
}
/** Contains the match-making view and the chatbox. */
protected JPanel _main;
/** Our occupant list display. */
protected OccupantList _occupants;
}
@@ -0,0 +1,48 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.lobby;
import javax.annotation.Generated;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.server.InvocationException;
import com.threerings.presents.server.InvocationProvider;
/**
* Defines the server-side of the {@link LobbyService}.
*/
@Generated(value={"com.threerings.presents.tools.GenServiceTask"},
comments="Derived from LobbyService.java.")
public interface LobbyProvider extends InvocationProvider
{
/**
* Handles a {@link LobbyService#getCategories} request.
*/
void getCategories (ClientObject caller, LobbyService.CategoriesListener arg1)
throws InvocationException;
/**
* Handles a {@link LobbyService#getLobbies} request.
*/
void getLobbies (ClientObject caller, String arg1, LobbyService.LobbiesListener arg2)
throws InvocationException;
}
@@ -0,0 +1,263 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.lobby;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.inject.Inject;
import com.samskivert.util.StringUtil;
import com.threerings.util.StreamableArrayList;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.data.InvocationCodes;
import com.threerings.presents.server.InvocationManager;
import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.server.PlaceRegistry;
import com.threerings.micasa.lobby.LobbyMarshaller;
import com.threerings.micasa.lobby.LobbyService.CategoriesListener;
import com.threerings.micasa.lobby.LobbyService.LobbiesListener;
import com.threerings.micasa.server.MiCasaConfig;
import static com.threerings.micasa.Log.log;
/**
* The lobby registry is the primary class that coordinates the lobby services on the client. It
* sets up the necessary invocation services and keeps track of the lobbies in operation on the
* server. Only one lobby registry should be created on a server.
*
* <p> Presently, the lobby registry is configured with lobbies via the server configuration. An
* example configuration follows:
*
* <pre>
* lobby_ids = foolobby, barlobby, bazlobby
*
* foolobby.mgrclass = com.threerings.micasa.lobby.LobbyManager
* foolobby.ugi = <universal game identifier>
* foolobby.name = <human readable lobby name>
* foolobby.config1 = some config value
* foolobby.config2 = some other config value
*
* barlobby.mgrclass = com.threerings.micasa.lobby.LobbyManager
* barlobby.ugi = <universal game identifier>
* barlobby.name = <human readable lobby name>
* ...
* </pre>
*
* This information will be loaded from the MiCasa server configuration which means that it should
* live in <code>rsrc/config/micasa/server.properties</code> somwhere in the classpath where it
* will override the default MiCasa server properties file.
*
* <p> The <code>UGI</code> 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:
*
* <pre>
* backgammon,board,strategy
* spades,card,partner
* yahtzee,dice
* </pre>
*
* 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.
*
* <p> 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
implements LobbyProvider
{
@Inject public LobbyRegistry (InvocationManager invmgr)
{
// register ourselves as an invocation service handler
invmgr.registerProvider(this, LobbyMarshaller.class, InvocationCodes.GLOBAL_GROUP);
}
/**
* Initializes the registry, creating our default lobbies.
*/
public void init ()
{
// create our lobby managers
String[] lmgrs = null;
lmgrs = MiCasaConfig.config.getValue(LOBIDS_KEY, lmgrs);
if (lmgrs == null || lmgrs.length == 0) {
log.warning("No lobbies specified in config file (via '" + LOBIDS_KEY + "' parameter).");
} else {
for (String lmgr : lmgrs) {
loadLobby(lmgr);
}
}
}
/**
* Returns the oid of the default lobby.
*/
public int getDefaultLobbyOid ()
{
return _defLobbyOid;
}
/**
* Extracts the properties for a lobby from the server config and creates and initializes the
* lobby manager.
*/
protected void loadLobby (String lobbyId)
{
try {
// extract the properties for this lobby
Properties props = MiCasaConfig.config.getSubProperties(lobbyId);
// get the lobby manager class and UGI
String cfgClass = props.getProperty("config");
if (StringUtil.isBlank(cfgClass)) {
throw new Exception("Missing 'config' definition in lobby configuration.");
}
// create and initialize the lobby config object
LobbyConfig config = (LobbyConfig)Class.forName(cfgClass).newInstance();
config.init(props);
// create and initialize the lobby manager. it will call lobbyReady() when it has
// obtained a reference to its lobby object and is ready to roll
LobbyManager lobmgr = (LobbyManager)_plreg.createPlace(config);
lobmgr.init(this, props);
} catch (Exception e) {
log.warning("Unable to create lobby manager [lobbyId=" + lobbyId +
", error=" + e + "].");
}
}
/**
* Returns information about all lobbies hosting games in the specified category.
*
* @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 void getLobbies (BodyObject requester, String category, List<Lobby> target)
{
List<Lobby> list = _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 category list (which can be
* used to filter the list based on their capabilities).
*/
public String[] getCategories (BodyObject requester)
{
String[] cats = new String[_lobbies.size()];
Iterator<String> iter = _lobbies.keySet().iterator();
for (int ii = 0; iter.hasNext(); ii++) {
cats[ii] = iter.next();
}
return cats;
}
/**
* Processes a request by the client to obtain a list of the lobby categories available on this
* server.
*/
public void getCategories (ClientObject caller, CategoriesListener listener)
{
listener.gotCategories(getCategories((BodyObject)caller));
}
/**
* Processes a request by the client to obtain a list of lobbies matching the supplied category
* string.
*/
public void getLobbies (ClientObject caller, String category, LobbiesListener listener)
{
List<Lobby> target = StreamableArrayList.newList();
List<Lobby> list = _lobbies.get(category);
if (list != null) {
target.addAll(list);
}
listener.gotLobbies(target);
}
/**
* 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);
// if we don't already have a default lobby, this one is the big winner
if (_defLobbyOid == -1) {
_defLobbyOid = placeOid;
}
// 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)
{
List<Lobby> catlist = _lobbies.get(category);
if (catlist == null) {
catlist = Lists.newArrayList();
_lobbies.put(category, catlist);
}
catlist.add(record);
log.info("Registered lobby", "cat", category, "record", record);
}
@Inject protected PlaceRegistry _plreg;
/** A table containing references to our lobby records (in the form of category lists. */
protected Map<String,List<Lobby>> _lobbies = Maps.newHashMap();
/** The oid of the default lobby. */
protected int _defLobbyOid = -1;
/** The configuration key for the lobby managers list. */
protected static final String LOBIDS_KEY = "lobby_ids";
}
@@ -0,0 +1,230 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.lobby;
import java.util.List;
import java.util.Map;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultListModel;
import javax.swing.JComboBox;
import javax.swing.JList;
import javax.swing.JPanel;
import com.google.common.collect.Maps;
import com.threerings.crowd.data.PlaceObject;
import com.threerings.micasa.util.MiCasaContext;
import static com.threerings.micasa.Log.log;
/**
* 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, LobbyService.CategoriesListener,
LobbyService.LobbiesListener
{
/**
* 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() {
@Override
public void mouseClicked (MouseEvent e) {
if (e.getClickCount() == 2) {
Object item = _loblist.getSelectedValue();
enterLobby((Lobby)item);
}
}
};
_loblist.addMouseListener(ml);
add(_loblist, BorderLayout.CENTER);
}
@Override
public void addNotify ()
{
super.addNotify();
// get a handle on our lobby service instance
_lservice = _ctx.getClient().requireService(LobbyService.class);
// and use them to look up the lobby categories
_lservice.getCategories(this);
}
/**
* 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);
}
}
}
// documentation inherited from interface
public void gotCategories (String[] categories)
{
// append these to our "unselected" item
for (String categorie : categories) {
_combo.addItem(categorie);
}
}
// documentation inherited from interface
public void gotLobbies (List<Lobby> lobbies)
{
// create a list model for this category
DefaultListModel model = new DefaultListModel();
// populate it with the lobby info
for (Lobby lobby : lobbies) {
model.addElement(lobby);
}
// 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;
}
// documentation inherited from interface
public void requestFailed (String reason)
{
log.info("Request failed [reason=" + reason + "].");
// clear out our pending category indicator in case this was a
// failed getLobbies() request
_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 = _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
_lservice.getLobbies(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
{
@Override
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 LobbyService _lservice;
protected JComboBox _combo;
protected JList _loblist;
protected Map<String, DefaultListModel> _catlists = Maps.newHashMap();
protected String _pendingCategory;
protected static final String CAT_FIRST_ITEM = "<categories...>";
}
@@ -0,0 +1,71 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.lobby;
import java.util.List;
import com.threerings.presents.client.InvocationService;
/**
* Provides an interface to the various parlor services that are directly
* invokable by the client (by means of the invocation services).
*/
public interface LobbyService extends InvocationService
{
/**
* Used to communicate the results of a {@link LobbyService#getCategories} request.
*/
public static interface CategoriesListener extends InvocationListener
{
/**
* Supplies the listener with the results of a {@link LobbyService#getCategories} request.
*/
public void gotCategories (String[] categories);
}
/**
* Used to communicate the results of a {@link LobbyService#getLobbies} request.
*/
public static interface LobbiesListener extends InvocationListener
{
/**
* Supplies the listener with the results of a {@link LobbyService#getLobbies} request.
*/
public void gotLobbies (List<Lobby> lobbies);
}
/**
* Requests the list of lobby categories that are available on this
* server.
*
* @param listener the listener that will receive and process the response.
*/
public void getCategories (CategoriesListener listener);
/**
* Requests information on all active lobbies that match the specified category.
*
* @param category the category of game for which a list of lobbies is desired.
* @param listener the listener that will receive and process the response.
*/
public void getLobbies (String category, LobbiesListener listener);
}
@@ -0,0 +1,252 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.lobby.table;
import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import com.threerings.util.Name;
import com.threerings.crowd.data.BodyObject;
import com.threerings.parlor.client.SeatednessObserver;
import com.threerings.parlor.client.TableDirector;
import com.threerings.parlor.data.Table;
import com.threerings.micasa.util.MiCasaContext;
import static com.threerings.micasa.Log.log;
/**
* A table item displays the user interface for a single table (whether it
* be in-play or still being matchmade).
*/
public class TableItem
extends JPanel
implements ActionListener, SeatednessObserver
{
/** A reference to the table we are displaying. */
public Table table;
/**
* Creates a new table item to display and interact with the supplied
* table.
*/
public TableItem (MiCasaContext ctx, TableDirector tdtr, Table table)
{
// keep track of these
_tdtr = tdtr;
_ctx = ctx;
// add ourselves as a seatedness observer
_tdtr.addSeatednessObserver(this);
// figure out who we are
_self = ((BodyObject)ctx.getClient().getClientObject()).getVisibleName();
// now create our user interface
setBorder(BorderFactory.createLineBorder(Color.black));
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
// create a label for the table
JLabel tlabel = new JLabel("Table " + table.tableId);
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(2, 0, 0, 0);
add(tlabel, gbc);
// we have one button for every "seat" at the table
int bcount = table.players.length;
// create blank buttons for now and then we'll update everything
// with the current state when we're done
gbc.weightx = 1.0;
gbc.insets = new Insets(2, 0, 2, 0);
_seats = new JButton[bcount];
for (int ii = 0; ii < bcount; ii++) {
// create the button
_seats[ii] = new JButton(JOIN_LABEL);
_seats[ii].addActionListener(this);
// if we're on the left
if (ii % 2 == 0) {
// if we're the last seat, then we've got an odd number
// and need to center this final seat
if (ii == bcount-1) {
gbc.gridwidth = GridBagConstraints.REMAINDER;
} else {
gbc.gridwidth = 1;
}
} else {
gbc.gridwidth = GridBagConstraints.REMAINDER;
}
// adjust the insets of our last element
if (ii == bcount-1) {
gbc.insets = new Insets(2, 0, 4, 0);
}
// and add the button with the configured constraints
add(_seats[ii], gbc);
// if we just added the first button, add the "go" button
// right after it
if (ii == 0) {
_goButton = new JButton("Go");
_goButton.setActionCommand("go");
_goButton.addActionListener(this);
add(_goButton, gbc);
}
}
// and update ourselves based on the contents of the occupants
// list
tableUpdated(table);
}
/**
* Called when our table has been updated and we need to update the UI
* to reflect the new information.
*/
public void tableUpdated (Table table)
{
// grab this new table reference
this.table = table;
// first look to see if we're already sitting at a table
boolean isSeated = _tdtr.isSeated();
// now enable and label the buttons accordingly
int slength = _seats.length;
for (int ii = 0; ii < slength; ii++) {
if (table.players[ii] == null) {
_seats[ii].setText(JOIN_LABEL);
_seats[ii].setEnabled(!isSeated);
_seats[ii].setActionCommand("join");
} else if (table.players[ii].equals(_self) &&
!table.inPlay()) {
_seats[ii].setText(LEAVE_LABEL);
_seats[ii].setEnabled(true);
_seats[ii].setActionCommand("leave");
} else {
_seats[ii].setText(table.players[ii].toString());
_seats[ii].setEnabled(false);
}
}
// show or hide our "go" button appropriately
_goButton.setVisible(table.gameOid != -1);
}
/**
* Called by the table list view prior to removing us. Here we clean
* up.
*/
public void tableRemoved ()
{
// no more observy
_tdtr.removeSeatednessObserver(this);
}
// documentation inherited
public void actionPerformed (ActionEvent event)
{
String cmd = event.getActionCommand();
if (cmd.equals("join")) {
// figure out what position this button is in
int position = -1;
for (int ii = 0; ii < _seats.length; ii++) {
if (_seats[ii] == event.getSource()) {
position = ii;
break;
}
}
// sanity check
if (position == -1) {
log.warning("Unable to figure out what position a <join> " +
"click came from [event=" + event + "].");
} else {
// otherwise, request to join the table at this position
_tdtr.joinTable(table.tableId, position);
}
} else if (cmd.equals("leave")) {
// if we're not joining, we're leaving
_tdtr.leaveTable(table.tableId);
} else if (cmd.equals("go")) {
// they want to see the game... so go there
_ctx.getLocationDirector().moveTo(table.gameOid);
} else {
log.warning("Received unknown action [event=" + event + "].");
}
}
// documentation inherited
public void seatednessDidChange (boolean isSeated)
{
// just update ourselves
tableUpdated(table);
// enable or disable the go button based on our seatedness
if (_goButton.isVisible()) {
_goButton.setEnabled(!isSeated);
}
}
/** A reference to our context. */
protected MiCasaContext _ctx;
/** Our username. */
protected Name _self;
/** A reference to our table director. */
protected TableDirector _tdtr;
/** We have a button for each "seat" at the table. */
protected JButton[] _seats;
/** We have a button for going to games that are already in
* progress. */
protected JButton _goButton;
/** The text shown for seats at which the user can join. */
protected static final String JOIN_LABEL = "<join>";
/** The text shown for the seat in which this user occupies and which
* lets her/him know that they can leave that seat by clicking. */
protected static final String LEAVE_LABEL = "<leave>";
}
@@ -0,0 +1,302 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.lobby.table;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import com.samskivert.swing.HGroupLayout;
import com.samskivert.swing.VGroupLayout;
import com.samskivert.swing.util.SwingUtil;
import com.threerings.crowd.client.PlaceView;
import com.threerings.crowd.data.PlaceObject;
import com.threerings.parlor.client.SeatednessObserver;
import com.threerings.parlor.client.TableConfigurator;
import com.threerings.parlor.client.TableDirector;
import com.threerings.parlor.client.TableObserver;
import com.threerings.parlor.data.Table;
import com.threerings.parlor.game.client.GameConfigurator;
import com.threerings.parlor.game.client.SwingGameConfigurator;
import com.threerings.parlor.game.data.GameConfig;
import com.threerings.micasa.lobby.LobbyConfig;
import com.threerings.micasa.util.MiCasaContext;
import static com.threerings.micasa.Log.log;
/**
* A view that displays the tables in a table lobby. It displays two
* separate lists, one of tables being matchmade and another of games in
* progress. These tables are updated dynamically as they proceed through
* the matchmaking process. UI mechanisms for creating and joining tables
* are also provided.
*/
public class TableListView extends JPanel
implements PlaceView, TableObserver, ActionListener, SeatednessObserver
{
/**
* Creates a new table list view, suitable for providing the user
* interface for table-style matchmaking in a table lobby.
*/
public TableListView (MiCasaContext ctx, LobbyConfig config)
{
// keep track of these
_config = config;
_ctx = ctx;
// create our table director
_tdtr = new TableDirector(ctx, TableLobbyObject.TABLE_SET, this);
// add ourselves as a seatedness observer
_tdtr.addSeatednessObserver(this);
// set up a layout manager
HGroupLayout gl = new HGroupLayout(HGroupLayout.STRETCH);
gl.setOffAxisPolicy(HGroupLayout.STRETCH);
setLayout(gl);
// we have two lists of tables, one of tables being matchmade...
VGroupLayout pgl = new VGroupLayout(VGroupLayout.STRETCH);
pgl.setOffAxisPolicy(VGroupLayout.STRETCH);
JPanel panel = new JPanel(pgl);
panel.add(new JLabel("Pending tables"), VGroupLayout.FIXED);
VGroupLayout mgl = new VGroupLayout(VGroupLayout.NONE);
mgl.setOffAxisPolicy(VGroupLayout.STRETCH);
mgl.setJustification(VGroupLayout.TOP);
_matchList = new JPanel(mgl);
_matchList.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
panel.add(new JScrollPane(_matchList));
// create and initialize the configurator interface
GameConfig gconfig = null;
try {
gconfig = config.getGameConfig();
_tableFigger = gconfig.createTableConfigurator();
if (_tableFigger == null) {
log.warning("Game config has not been set up to work with " +
"tables: it needs to return non-null from " +
"createTableConfigurator().");
// let's just wait until we throw an NPE below
}
_figger = gconfig.createConfigurator();
_tableFigger.init(_ctx, _figger);
if (_figger != null) {
_figger.init(_ctx);
_figger.setGameConfig(gconfig);
panel.add(((SwingGameConfigurator) _figger).getPanel(),
VGroupLayout.FIXED);
}
_create = new JButton("Create table");
_create.addActionListener(this);
panel.add(_create, VGroupLayout.FIXED);
} catch (Exception e) {
log.warning("Unable to create configurator interface " +
"[config=" + gconfig + "].", e);
// stick something in the UI to let them know we're hosed
panel.add(new JLabel("Aiya! Can't create tables. " +
"Configuration borked."), VGroupLayout.FIXED);
}
add(panel);
// ...and one of games in progress
panel = new JPanel(pgl);
panel.add(new JLabel("Games in progress"), VGroupLayout.FIXED);
_playList = new JPanel(mgl);
_playList.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
panel.add(new JScrollPane(_playList));
add(panel);
}
// documentation inherited
public void willEnterPlace (PlaceObject place)
{
// pass the good word on to our table director
_tdtr.setTableObject(place);
// iterate over the tables already active in this lobby and put
// them in their respective lists
TableLobbyObject tlobj = (TableLobbyObject)place;
for (Table table : tlobj.tableSet) {
tableAdded(table);
}
}
// documentation inherited
public void didLeavePlace (PlaceObject place)
{
// pass the good word on to our table director
_tdtr.clearTableObject();
// clear out our table lists
_matchList.removeAll();
_playList.removeAll();
}
// documentation inherited
public void tableAdded (Table table)
{
log.info("Table added [table=" + table + "].");
// create a table item for this table and insert it into the
// appropriate list
JPanel panel = table.inPlay() ? _playList : _matchList;
panel.add(new TableItem(_ctx, _tdtr, table));
SwingUtil.refresh(panel);
}
// documentation inherited
public void tableUpdated (Table table)
{
log.info("Table updated [table=" + table + "].");
// locate the table item associated with this table
TableItem item = getTableItem(table.tableId);
if (item == null) {
log.warning("Received table updated notification for " +
"unknown table [table=" + table + "].");
return;
}
// let the item perform any updates it finds necessary
item.tableUpdated(table);
// and we may need to move the item from the match to the in-play
// list if it just transitioned
if (table.gameOid != -1 && item.getParent() == _matchList) {
_matchList.remove(item);
SwingUtil.refresh(_matchList);
_playList.add(item);
SwingUtil.refresh(_playList);
}
}
// documentation inherited
public void tableRemoved (int tableId)
{
log.info("Table removed [tableId=" + tableId + "].");
// locate the table item associated with this table
TableItem item = getTableItem(tableId);
if (item == null) {
log.warning("Received table removed notification for " +
"unknown table [tableId=" + tableId + "].");
return;
}
// remove this item from the user interface
JPanel panel = (JPanel)item.getParent();
panel.remove(item);
SwingUtil.refresh(panel);
// let the little fellow know that we gave him the boot
item.tableRemoved();
}
// documentation inherited
public void actionPerformed (ActionEvent event)
{
// the create table button was clicked. use the game config as
// configured by the configurator to create a table
_tdtr.createTable(_tableFigger.getTableConfig(),
_figger.getGameConfig());
}
// documentation inherited
public void seatednessDidChange (boolean isSeated)
{
// update the create table button
_create.setEnabled(!isSeated);
}
/**
* Fetches the table item component associated with the specified
* table id.
*/
protected TableItem getTableItem (int tableId)
{
// first check the match list
int ccount = _matchList.getComponentCount();
for (int ii = 0; ii < ccount; ii++) {
TableItem child = (TableItem)_matchList.getComponent(ii);
if (child.table.tableId == tableId) {
return child;
}
}
// then the inplay list
ccount = _playList.getComponentCount();
for (int ii = 0; ii < ccount; ii++) {
TableItem child = (TableItem)_playList.getComponent(ii);
if (child.table.tableId == tableId) {
return child;
}
}
// sorry charlie
return null;
}
/** A reference to the client context. */
protected MiCasaContext _ctx;
/** A reference to the lobby config for the lobby in which we are
* doing table-style matchmaking. */
protected LobbyConfig _config;
/** A reference to our table director. */
protected TableDirector _tdtr;
/** The list of tables currently being matchmade. */
protected JPanel _matchList;
/** The list of tables that are in play. */
protected JPanel _playList;
/** The interface used to configure the table for a game. */
protected TableConfigurator _tableFigger;
/** The interface used to configure a game before creating it. */
protected GameConfigurator _figger;
/** Our create table button. */
protected JButton _create;
/** Our number of players indicator. */
protected JLabel _pcount;
}
@@ -0,0 +1,46 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.lobby.table;
import javax.swing.JComponent;
import com.threerings.micasa.lobby.LobbyConfig;
import com.threerings.micasa.util.MiCasaContext;
/**
* Instructs the lobby services to use a {@link TableListView} as the
* matchmaking component.
*/
public class TableLobbyConfig extends LobbyConfig
{
@Override
public String getManagerClassName ()
{
return "com.threerings.micasa.lobby.table.TableLobbyManager";
}
@Override
public JComponent createMatchMakingView (MiCasaContext ctx)
{
return new TableListView(ctx, this);
}
}
@@ -0,0 +1,59 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.lobby.table;
import com.threerings.crowd.data.PlaceObject;
import com.threerings.parlor.server.TableManager;
import com.threerings.micasa.lobby.LobbyManager;
/**
* Extends lobby manager only to ensure that a table lobby object is used for table lobbies.
*/
public class TableLobbyManager extends LobbyManager
{
@Override
protected void didStartup ()
{
super.didStartup();
// now that we have our place object, we can create our table manager
_tmgr = new TableManager(_omgr, _invmgr, _registry, getPlaceObject());
}
@Override
protected void didShutdown ()
{
super.didShutdown();
// clean up our table manager
_tmgr.shutdown();
}
@Override
protected PlaceObject createPlaceObject ()
{
return new TableLobbyObject();
}
/** A reference to our table manager. */
protected TableManager _tmgr;
}
@@ -0,0 +1,150 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.lobby.table;
import javax.annotation.Generated;
import com.threerings.presents.dobj.DSet;
import com.threerings.parlor.data.Table;
import com.threerings.parlor.data.TableMarshaller;
import com.threerings.micasa.lobby.LobbyObject;
public class TableLobbyObject extends LobbyObject
implements com.threerings.parlor.data.TableLobbyObject
{
// AUTO-GENERATED: FIELDS START
/** The field name of the <code>tableSet</code> field. */
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
public static final String TABLE_SET = "tableSet";
/** The field name of the <code>tableService</code> field. */
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
public static final String TABLE_SERVICE = "tableService";
// AUTO-GENERATED: FIELDS END
/** A set containing all of the tables being managed by this lobby. */
public DSet<Table> tableSet = new DSet<Table>();
/** Handles our table services. */
public TableMarshaller tableService;
// from interface TableLobbyObject
public DSet<Table> getTables ()
{
return tableSet;
}
// from interface TableLobbyObject
public void addToTables (Table table)
{
addToTableSet(table);
}
// from interface TableLobbyObject
public void updateTables (Table table)
{
updateTableSet(table);
}
// from interface TableLobbyObject
public void removeFromTables (Comparable<?> key)
{
removeFromTableSet(key);
}
// from interface TableLobbyObject
public TableMarshaller getTableService ()
{
return tableService;
}
// AUTO-GENERATED: METHODS START
/**
* Requests that the specified entry be added to the
* <code>tableSet</code> set. The set will not change until the event is
* actually propagated through the system.
*/
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
public void addToTableSet (Table elem)
{
requestEntryAdd(TABLE_SET, tableSet, elem);
}
/**
* Requests that the entry matching the supplied key be removed from
* the <code>tableSet</code> set. The set will not change until the
* event is actually propagated through the system.
*/
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
public void removeFromTableSet (Comparable<?> key)
{
requestEntryRemove(TABLE_SET, tableSet, key);
}
/**
* Requests that the specified entry be updated in the
* <code>tableSet</code> set. The set will not change until the event is
* actually propagated through the system.
*/
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
public void updateTableSet (Table elem)
{
requestEntryUpdate(TABLE_SET, tableSet, elem);
}
/**
* Requests that the <code>tableSet</code> field be set to the
* specified value. Generally one only adds, updates and removes
* entries of a distributed set, but certain situations call for a
* complete replacement of the set value. The local value will be
* updated immediately and an event will be propagated through the
* system to notify all listeners that the attribute did
* change. Proxied copies of this object (on clients) will apply the
* value change when they received the attribute changed notification.
*/
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
public void setTableSet (DSet<Table> value)
{
requestAttributeChange(TABLE_SET, value, this.tableSet);
DSet<Table> clone = (value == null) ? null : value.clone();
this.tableSet = clone;
}
/**
* Requests that the <code>tableService</code> field be set to the
* specified value. The local value will be updated immediately and an
* event will be propagated through the system to notify all listeners
* that the attribute did change. Proxied copies of this object (on
* clients) will apply the value change when they received the
* attribute changed notification.
*/
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
public void setTableService (TableMarshaller value)
{
TableMarshaller ovalue = this.tableService;
requestAttributeChange(
TABLE_SERVICE, value, ovalue);
this.tableService = value;
}
// AUTO-GENERATED: METHODS END
}
@@ -0,0 +1,33 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.server;
import com.samskivert.util.Config;
/**
* Provides access to the MiCasa server configuration.
*/
public class MiCasaConfig
{
/** Provides access to configuration data for this package. */
public static Config config = new Config("rsrc/config/micasa/server");
}
@@ -0,0 +1,76 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.server;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.threerings.util.Name;
import com.threerings.presents.net.AuthRequest;
import com.threerings.presents.server.SessionFactory;
import com.threerings.presents.server.ClientResolver;
import com.threerings.presents.server.PresentsSession;
import com.threerings.crowd.server.CrowdServer;
import com.threerings.parlor.server.ParlorManager;
import com.threerings.micasa.lobby.LobbyRegistry;
import static com.threerings.micasa.Log.log;
/**
* The main general organizer of everything that goes on in the MiCasa game server process.
*/
public class MiCasaServer extends CrowdServer
{
public static void main (String[] args)
{
runServer(new CrowdModule(), new PresentsServerModule(MiCasaServer.class));
}
@Override // from CrowdServer
public void init (Injector injector)
throws Exception
{
super.init(injector);
// configure the client manager to use our client class
_clmgr.setDefaultSessionFactory(new SessionFactory() {
@Override public Class<? extends PresentsSession> getSessionClass (AuthRequest areq) {
return MiCasaSession.class;
}
@Override public Class<? extends ClientResolver> getClientResolverClass (Name username) {
return ClientResolver.class;
}
});
// initialize the lobby registry
_lobreg.init();
log.info("MiCasa server initialized.");
}
@Inject protected LobbyRegistry _lobreg;
@Inject protected ParlorManager _parmgr;
}
@@ -0,0 +1,54 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.server;
import com.google.inject.Inject;
import com.threerings.presents.net.BootstrapData;
import com.threerings.crowd.server.CrowdSession;
import com.threerings.micasa.data.MiCasaBootstrapData;
import com.threerings.micasa.lobby.LobbyRegistry;
/**
* Extends the Crowd session and provides bootstrap data specific to the MiCasa services.
*/
public class MiCasaSession extends CrowdSession
{
@Override // from PresentsSession
protected BootstrapData createBootstrapData ()
{
return new MiCasaBootstrapData();
}
@Override // from PresentsSession
protected void populateBootstrapData (BootstrapData data)
{
super.populateBootstrapData(data);
// let the client know their default lobby oid
((MiCasaBootstrapData)data).defLobbyOid = _lobreg.getDefaultLobbyOid();
}
@Inject protected LobbyRegistry _lobreg;
}
@@ -0,0 +1,183 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.simulator.client;
import java.io.IOException;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JPanel;
import com.samskivert.util.Config;
import com.samskivert.util.RunQueue;
import com.threerings.util.MessageManager;
import com.threerings.presents.client.Client;
import com.threerings.presents.dobj.DObjectManager;
import com.threerings.crowd.chat.client.ChatDirector;
import com.threerings.crowd.client.LocationDirector;
import com.threerings.crowd.client.OccupantDirector;
import com.threerings.crowd.client.PlaceView;
import com.threerings.parlor.client.ParlorDirector;
import com.threerings.parlor.util.ParlorContext;
import com.threerings.micasa.client.MiCasaFrame;
import com.threerings.micasa.util.MiCasaContext;
public class SimpleClient
implements SimulatorClient
{
public SimpleClient (SimulatorFrame frame)
throws IOException
{
// create our context
_ctx = createContext();
// create the handles on our various services
_client = new Client(null, RunQueue.AWT);
// create our managers and directors
_msgmgr = new MessageManager(getMessageManagerPrefix());
_locdir = new LocationDirector(_ctx);
_occdir = new OccupantDirector(_ctx);
_pardtr = new ParlorDirector(_ctx);
_chatdir = new ChatDirector(_ctx, null);
// keep this for later
_frame = frame;
// log off when they close the window
_frame.getFrame().addWindowListener(new WindowAdapter() {
@Override
public void windowClosing (WindowEvent evt) {
// if we're logged on, log off
if (_client.isLoggedOn()) {
_client.logoff(true);
}
}
});
}
/**
* Creates our context reference.
*/
protected MiCasaContext createContext ()
{
return new MiCasaContextImpl();
}
/**
* Returns the prefix used by the message manager when looking for
* translation properties files.
*/
protected String getMessageManagerPrefix ()
{
return "rsrc";
}
/**
* Returns a reference to the context in effect for this client. This
* reference is valid for the lifetime of the application.
*/
public ParlorContext getParlorContext ()
{
return _ctx;
}
/**
* The context implementation. This provides access to all of the
* objects and services that are needed by the operating client.
*/
protected class MiCasaContextImpl implements MiCasaContext
{
public Config getConfig ()
{
return _config;
}
public Client getClient ()
{
return _client;
}
public DObjectManager getDObjectManager ()
{
return _client.getDObjectManager();
}
public LocationDirector getLocationDirector ()
{
return _locdir;
}
public OccupantDirector getOccupantDirector ()
{
return _occdir;
}
public ParlorDirector getParlorDirector ()
{
return _pardtr;
}
public ChatDirector getChatDirector ()
{
return _chatdir;
}
public MessageManager getMessageManager ()
{
return _msgmgr;
}
public void setPlaceView (PlaceView view)
{
// stick the place view into our frame
_frame.setPanel((JPanel)view);
}
public void clearPlaceView (PlaceView view)
{
// we'll just let the next view replace the old one
}
public MiCasaFrame getFrame ()
{
return (MiCasaFrame)_frame;
}
}
protected MiCasaContext _ctx;
protected SimulatorFrame _frame;
protected MessageManager _msgmgr;
protected Config _config = new Config("micasa");
protected Client _client;
protected LocationDirector _locdir;
protected OccupantDirector _occdir;
protected ParlorDirector _pardtr;
protected ChatDirector _chatdir;
}
@@ -0,0 +1,51 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.simulator.client;
import javax.swing.JFrame;
import com.samskivert.swing.Controller;
import com.threerings.micasa.client.MiCasaFrame;
/**
* Contains the user interface for the Simulator client application.
*/
public class SimpleFrame extends MiCasaFrame
implements SimulatorFrame
{
/**
* Constructs the top-level Simulator client frame.
*/
public SimpleFrame ()
{
super("Simulator");
}
// documentation inherited
public JFrame getFrame ()
{
return this;
}
protected Controller _controller;
}
@@ -0,0 +1,232 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.simulator.client;
import javax.swing.JFrame;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.samskivert.util.Interval;
import com.samskivert.util.ResultListener;
import com.samskivert.swing.Controller;
import com.samskivert.swing.util.SwingUtil;
import com.threerings.util.Name;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.ClientAdapter;
import com.threerings.presents.net.UsernamePasswordCreds;
import com.threerings.micasa.simulator.data.SimulatorInfo;
import com.threerings.micasa.simulator.server.SimpleServer;
import com.threerings.micasa.simulator.server.SimulatorServer;
import static com.threerings.micasa.Log.log;
/**
* The simulator application is a test harness to facilitate development and debugging of games.
*/
public class SimulatorApp
{
public void start (final String[] args) throws Exception
{
// create a frame
_frame = createSimulatorFrame();
// create the simulator info object
SimulatorInfo siminfo = new SimulatorInfo();
siminfo.gameConfigClass = args[0];
siminfo.simClass = args[1];
siminfo.playerCount = getInt(System.getProperty("playercount"), DEFAULT_PLAYER_COUNT);
// create our client instance
_client = createSimulatorClient(_frame);
// set up the top-level client controller
Controller ctrl = createController(siminfo);
_frame.setController(ctrl);
// create the server
Injector injector = Guice.createInjector(new SimpleServer.CrowdModule());
SimulatorServer server = createSimulatorServer(injector);
server.init(injector, new ResultListener<SimulatorServer>() {
public void requestCompleted (SimulatorServer result) {
try {
run();
} catch (Exception e) {
log.warning("Simulator initialization failed [e=" + e + "].");
}
}
public void requestFailed (Exception e) {
log.warning("Simulator initialization failed [e=" + e + "].");
}
});
// run the server on a separate thread
_serverThread = new ServerThread(server);
// start up the server so that we can be notified when initialization is complete
_serverThread.start();
}
protected SimulatorServer createSimulatorServer (Injector injector)
{
return injector.getInstance(SimpleServer.class);
}
protected SimulatorFrame createSimulatorFrame ()
{
return new SimpleFrame();
}
protected SimulatorClient createSimulatorClient (SimulatorFrame frame)
throws Exception
{
return new SimpleClient(_frame);
}
protected SimulatorController createController (SimulatorInfo siminfo)
{
return new SimulatorController(
_client.getParlorContext(), _frame, siminfo);
}
public void run ()
{
// configure and display the main frame
JFrame frame = _frame.getFrame();
frame.setSize(800, 600);
SwingUtil.centerWindow(frame);
frame.setVisible(true);
// start up the client
Client client = _client.getParlorContext().getClient();
log.info("Connecting to localhost.");
client.setServer("localhost", Client.DEFAULT_SERVER_PORTS);
// we want to exit when we logged off or failed to log on
client.addClientObserver(new ClientAdapter() {
@Override
public void clientFailedToLogon (Client c, Exception cause) {
log.info("Client failed to logon: " + cause);
System.exit(0);
}
@Override
public void clientDidLogoff (Client c) {
System.exit(0);
}
});
// configure the client with some credentials and logon
String username = System.getProperty("username");
if (username == null) {
username =
"bob" + ((int)(Math.random() * Integer.MAX_VALUE) % 500);
}
String password = System.getProperty("password");
if (password == null) {
password = "test";
}
// create and set our credentials
client.setCredentials(
new UsernamePasswordCreds(new Name(username), password));
// this is a bit of a hack, but we need to give the server long
// enough to fully initialize and start listening on its socket
// before we try to logon; there's no good way for this otherwise
// wholly independent thread to wait for the server to be ready as
// in normal circumstances they are entirely different processes;
// so we just wait half a second which does the job
new Interval(Interval.RUN_DIRECT) {
@Override
public void expired () {
_client.getParlorContext().getClient().logon();
}
}.schedule(500L);
}
public static void main (String[] args)
{
if (!checkArgs(args)) {
return;
}
SimulatorApp app = new SimulatorApp();
try {
app.start(args);
} catch (Exception e) {
log.warning("Error starting up application.", e);
}
}
protected static boolean checkArgs (String[] args)
{
if (args.length < 2) {
String msg = "Usage:\n" +
" java com.threerings.simulator.SimulatorApp " +
"<game config class name> <simulant class name>\n" +
"Optional properties:\n" +
" -Dusername=<user>\n" +
" -Dplayercount=<number>\n" +
" -Dwidth=<width>\n" +
" -Dheight=<height>";
System.out.println(msg);
return false;
}
return true;
}
protected int getInt (String value, int defval)
{
try {
return Integer.parseInt(value);
} catch (NumberFormatException nfe) {
return defval;
}
}
protected static class ServerThread extends Thread
{
public ServerThread (SimulatorServer server)
{
_server = server;
}
@Override
public void run ()
{
_server.run();
}
protected SimulatorServer _server;
}
protected SimulatorClient _client;
protected SimulatorFrame _frame;
protected ServerThread _serverThread;
/** The default number of players in the game. */
protected static final int DEFAULT_PLAYER_COUNT = 2;
}
@@ -0,0 +1,29 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.simulator.client;
import com.threerings.parlor.util.ParlorContext;
public interface SimulatorClient
{
public ParlorContext getParlorContext ();
}
@@ -0,0 +1,137 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.simulator.client;
import java.awt.event.ActionEvent;
import com.samskivert.swing.Controller;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.SessionObserver;
import com.threerings.crowd.data.BodyObject;
import com.threerings.parlor.game.data.GameConfig;
import com.threerings.parlor.util.ParlorContext;
import com.threerings.micasa.simulator.data.SimulatorInfo;
import static com.threerings.micasa.Log.log;
/**
* Responsible for top-level control of the simulator client user interface.
*/
public class SimulatorController extends Controller
implements SessionObserver
{
/** Command constant used to logoff the client. */
public static final String LOGOFF = "logoff";
// 577-2028
/**
* Creates a new simulator controller. The controller will set
* everything up in preparation for logging on.
*/
public SimulatorController (ParlorContext ctx, SimulatorFrame frame,
SimulatorInfo info)
{
// we'll want to keep these around
_ctx = ctx;
_frame = frame;
_info = info;
// we want to know about logon/logoff
_ctx.getClient().addClientObserver(this);
}
@Override
public boolean handleAction (ActionEvent action)
{
String cmd = action.getActionCommand();
if (cmd.equals(LOGOFF)) {
// request that we logoff
_ctx.getClient().logoff(true);
return true;
}
log.info("Unhandled action: " + action);
return false;
}
// documentation inherited
public void clientWillLogon (Client client)
{
// nada
}
// documentation inherited
public void clientDidLogon (Client client)
{
log.info("Client did logon [client=" + client + "].");
// keep the body object around for stuff
_body = (BodyObject)client.getClientObject();
// have at it
createGame(client);
}
public void createGame (Client client)
{
GameConfig config = null;
try {
// create the game config object
config = (GameConfig)Class.forName(_info.gameConfigClass).newInstance();
// get the simulator service and use it to request that our game be created
SimulatorService sservice = client.requireService(SimulatorService.class);
sservice.createGame(config, _info.simClass, _info.playerCount);
// our work here is done, as the location manager will move us into the game room
// straightaway
} catch (Exception e) {
log.warning("Failed to instantiate game config [class=" + _info.gameConfigClass +
", error=" + e + "].");
}
}
// documentation inherited
public void clientObjectDidChange (Client client)
{
// regrab our body object
_body = (BodyObject)client.getClientObject();
}
// documentation inherited
public void clientDidLogoff (Client client)
{
log.info("Client did logoff [client=" + client + "].");
}
protected ParlorContext _ctx;
protected SimulatorFrame _frame;
protected SimulatorInfo _info;
protected BodyObject _body;
}
@@ -0,0 +1,52 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.simulator.client;
import javax.swing.JFrame;
import javax.swing.JPanel;
import com.samskivert.swing.Controller;
import com.samskivert.swing.ControllerProvider;
/**
* Contains the user interface for the Simulator client application.
*/
public interface SimulatorFrame extends ControllerProvider
{
/**
* Returns a reference to the top-level frame that the simulator will
* use to display everything.
*/
public JFrame getFrame ();
/**
* Sets the panel that makes up the entire client display.
*/
public void setPanel (JPanel panel);
/**
* Sets the controller for the outermost scope. This controller will
* handle all actions that aren't handled by controllers of higher
* scope.
*/
public void setController (Controller controller);
}
@@ -0,0 +1,41 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.simulator.client;
import com.threerings.presents.client.InvocationService;
import com.threerings.parlor.game.data.GameConfig;
/**
* Provides access to simulator invocation services.
*/
public interface SimulatorService extends InvocationService
{
/**
* Requests that a new game be created.
*
* @param config the game config for the game to be created.
* @param simClass the class name of the simulant to create.
* @param playerCount the number of players in the game.
*/
public void createGame (GameConfig config, String simClass, int playerCount);
}
@@ -0,0 +1,41 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.simulator.data;
public class SimulatorInfo
{
/** The game config classname. */
public String gameConfigClass;
/** The simulant classname. */
public String simClass;
/** The number of players in the game. */
public int playerCount;
@Override
public String toString ()
{
return "[gameConfigClass=" + gameConfigClass +
", simClass=" + simClass + ", playerCount=" + playerCount + "]";
}
}
@@ -0,0 +1,52 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.simulator.data;
import javax.annotation.Generated;
import com.threerings.micasa.simulator.client.SimulatorService;
import com.threerings.parlor.game.data.GameConfig;
import com.threerings.presents.data.InvocationMarshaller;
/**
* Provides the implementation of the {@link SimulatorService} interface
* that marshalls the arguments and delivers the request to the provider
* on the server. Also provides an implementation of the response listener
* interfaces that marshall the response arguments and deliver them back
* to the requesting client.
*/
@Generated(value={"com.threerings.presents.tools.GenServiceTask"},
comments="Derived from SimulatorService.java.")
public class SimulatorMarshaller extends InvocationMarshaller
implements SimulatorService
{
/** The method id used to dispatch {@link #createGame} requests. */
public static final int CREATE_GAME = 1;
// from interface SimulatorService
public void createGame (GameConfig arg1, String arg2, int arg3)
{
sendRequest(CREATE_GAME, new Object[] {
arg1, arg2, Integer.valueOf(arg3)
});
}
}
@@ -0,0 +1,52 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.simulator.server;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Singleton;
import com.samskivert.util.ResultListener;
import com.threerings.micasa.server.MiCasaServer;
/**
* A simple simulator server implementation that extends the MiCasa server.
*/
@Singleton
public class SimpleServer extends MiCasaServer
implements SimulatorServer
{
public void init (Injector injector, ResultListener<SimulatorServer> obs)
throws Exception
{
init(injector); // do our standard initialization
if (obs != null) {
// let the initialization observer know that we've started up
obs.requestCompleted(this);
}
}
// cause the simulator manager to be created
@Inject protected SimulatorManager _simmgr;
}
@@ -0,0 +1,88 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.simulator.server;
import com.threerings.presents.dobj.DObjectManager;
import com.threerings.presents.dobj.MessageEvent;
import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.data.PlaceObject;
import com.threerings.parlor.game.data.GameConfig;
import com.threerings.parlor.game.server.GameManager;
public abstract class Simulant
{
/**
* Initializes the simulant with a body object and the game config for
* the game they'll be engaged in.
*/
public void init (BodyObject self, GameConfig config,
GameManager gmgr, DObjectManager omgr)
{
_self = self;
_config = config;
_gmgr = gmgr;
_omgr = omgr;
}
/**
* Called when the simulant is about to enter the room in which it
* will be doing all of its business. Default implementation
* immediately notifies the game manager that the simulant is ready to
* play. Sub-classes may wish to override this to do things like
* subscribe to the game object, but should be sure to call this
* method when they're finished to give the game manager the go-ahead
* to proceed.
*/
public void willEnterPlace (PlaceObject plobj)
{
// let the game manager know that the simulant's ready
_gmgr.playerReady(_self);
}
/**
* Posts the given message event to the server. Since the simulant
* resides within the server itself, it has no available client
* distributed object manager and so we must set up the source oid
* ourselves before sending it on its merry way. Sub-classes should
* accordingly be sure to make use of this method to send any
* messages.
*/
protected void postEvent (MessageEvent mevt)
{
mevt.setSourceOid(_self.getOid());
_omgr.postEvent(mevt);
}
/** The game config object. */
protected GameConfig _config;
/** The game manager for the game we're playing. */
protected GameManager _gmgr;
/** Our body object. */
protected BodyObject _self;
/** The object manager with which we're interacting. */
protected DObjectManager _omgr;
}
@@ -0,0 +1,202 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.simulator.server;
import java.util.List;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.threerings.micasa.simulator.data.SimulatorMarshaller;
import com.threerings.util.Name;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.data.InvocationCodes;
import com.threerings.presents.dobj.RootDObjectManager;
import com.threerings.presents.server.ClientManager;
import com.threerings.presents.server.ClientResolutionListener;
import com.threerings.presents.server.InvocationManager;
import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.server.LocationManager;
import com.threerings.crowd.server.PlaceRegistry;
import com.threerings.parlor.game.data.GameAI;
import com.threerings.parlor.game.data.GameConfig;
import com.threerings.parlor.game.data.GameObject;
import com.threerings.parlor.game.server.GameManager;
import static com.threerings.micasa.Log.log;
/**
* The simulator manager is responsible for handling the simulator services on the server side.
*/
@Singleton
public class SimulatorManager
{
@Inject public SimulatorManager (InvocationManager invmgr)
{
// register our simulator provider
SimulatorProvider sprov = new SimulatorProvider(this);
invmgr.registerProvider(sprov, SimulatorMarshaller.class, InvocationCodes.GLOBAL_GROUP);
}
/**
* Creates a game along with the specified number of simulant players and forcibly moves all
* players into the game room.
*/
public void createGame (BodyObject source, GameConfig config, String simClass, int playerCount)
{
new CreateGameTask(source, config, simClass, playerCount);
}
public class CreateGameTask
{
public CreateGameTask (BodyObject source, GameConfig config, String simClass,
int playerCount) {
// save off game request info
_source = source;
_config = config;
_simClass = simClass;
_playerCount = playerCount;
try {
// create the game manager and begin its initialization process. the game manager
// will take care of notifying the players that the game has been created
// configure the game config with the player names
config.players = new Name[_playerCount];
config.players[0] = _source.getVisibleName();
for (int ii = 1; ii < _playerCount; ii++) {
config.players[ii] = new Name("simulant" + ii);
}
_gmgr = (GameManager)_plreg.createPlace(config);
} catch (Exception e) {
log.warning("Unable to create game manager", "e", e, e);
return;
}
// cast the place to the game object for the game we're creating
_gobj = (GameObject)_gmgr.getPlaceObject();
// determine the AI player skill level
byte skill;
try {
skill = Byte.parseByte(System.getProperty("skill"));
} catch (NumberFormatException nfe) {
skill = DEFAULT_SKILL;
}
for (int ii = 1; ii < _playerCount; ii++) {
// mark all simulants as AI players
_gmgr.setAI(ii, new GameAI(0, skill));
}
// resolve the simulant body objects
ClientResolutionListener listener = new ClientResolutionListener() {
public void clientResolved (Name username, ClientObject clobj) {
// hold onto the body object for later game creation
_sims.add(clobj);
// create the game if we've received all body objects
if (_sims.size() == (_playerCount - 1)) {
createSimulants();
}
}
public void resolutionFailed (Name username, Exception cause) {
log.warning("Unable to create simulant body object", "error", cause);
}
};
// resolve client objects for all of our simulants
for (int ii = 1; ii < _playerCount; ii++) {
Name username = new Name("simulant" + ii);
_clmgr.resolveClientObject(username, listener);
}
}
/**
* Called when all simulant body objects are present and the simulants are ready to be
* created.
*/
protected void createSimulants () {
// finish setting up the simulants
for (int ii = 1; ii < _playerCount; ii++) {
// create the simulant object
Simulant sim;
try {
sim = (Simulant)Class.forName(_simClass).newInstance();
} catch (Exception e) {
log.warning("Unable to create simulant", "class", _simClass);
return;
}
// give the simulant its body
BodyObject bobj = (BodyObject)_sims.get(ii - 1);
sim.init(bobj, _config, _gmgr, _omgr);
// give the simulant a chance to engage in place antics
sim.willEnterPlace(_gobj);
// move the simulant into the game room since they have no location director to
// move them automagically
try {
_locman.moveTo(bobj, _gobj.getOid());
} catch (Exception e) {
log.warning("Failed to move simulant into room", "e", e);
return;
}
}
}
/** The simulant body objects. */
protected List<ClientObject> _sims = Lists.newArrayList();
/** The game object for the game being created. */
protected GameObject _gobj;
/** The game manager for the game being created. */
protected GameManager _gmgr;
/** The number of players in the game. */
protected int _playerCount;
/** The simulant class instantiated on game creation. */
protected String _simClass;
/** The game config object. */
protected GameConfig _config;
/** The body object of the player requesting the game creation. */
protected BodyObject _source;
}
// needed for general operation
@Inject protected PlaceRegistry _plreg;
@Inject protected ClientManager _clmgr;
@Inject protected RootDObjectManager _omgr;
@Inject protected LocationManager _locman;
/** The default skill level for AI players. */
protected static final byte DEFAULT_SKILL = 50;
}
@@ -0,0 +1,63 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.simulator.server;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.server.InvocationProvider;
import com.threerings.crowd.data.BodyObject;
import com.threerings.parlor.game.data.GameConfig;
import static com.threerings.micasa.Log.log;
/**
* The simulator provider handles game creation requests on the server
* side, passing them off to the {@link SimulatorManager}.
*/
public class SimulatorProvider
implements InvocationProvider
{
/**
* Constructs a simulator provider.
*/
public SimulatorProvider (SimulatorManager simmgr)
{
_simmgr = simmgr;
}
/**
* Processes a request from the client to create a new game.
*/
public void createGame (ClientObject caller, GameConfig config,
String simClass, int playerCount)
{
log.info("handleCreateGameRequest [caller=" + caller.who() +
", config=" + config + ", simClass=" + simClass +
", playerCount=" + playerCount + "].");
_simmgr.createGame((BodyObject)caller, config, simClass, playerCount);
}
/** The simulator manager. */
protected SimulatorManager _simmgr;
}
@@ -0,0 +1,49 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.simulator.server;
import com.google.inject.Injector;
import com.samskivert.util.ResultListener;
/**
* The simulator manager needs a mechanism for faking body object registrations, which is provided
* by implementations of this interface.
*/
public interface SimulatorServer
{
/**
* Called to initialize this server instance.
*
* @param obs the observer to notify when the server has finished starting up, or
* <code>null</code> if no notification is desired.
*
* @exception Exception thrown if anything goes wrong initializing the server.
*/
public void init (Injector injector, ResultListener<SimulatorServer> obs) throws Exception;
/**
* Called to perform the main body of server processing. This is called from the server thread
* and should do the simulator server's primary business.
*/
public void run ();
}
@@ -0,0 +1,44 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.simulator.util;
import com.threerings.parlor.util.ParlorContext;
import com.threerings.micasa.simulator.client.SimulatorFrame;
import com.threerings.micasa.simulator.data.SimulatorInfo;
/**
* The simulator context encapsulates the contexts of all of the services
* that are used by the simulator client so that we can pass around one
* single context implementation that provides all of the necessary
* components to all of the services in use.
*/
public interface SimulatorContext
extends ParlorContext
{
/** Returns a reference to the primary user interface frame. */
public SimulatorFrame getFrame ();
/** Returns a reference to the simulator info describing the game and
* other details of the simulation. */
public SimulatorInfo getSimulatorInfo ();
}
@@ -0,0 +1,48 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.micasa.util;
import com.threerings.util.MessageManager;
import com.threerings.parlor.util.ParlorContext;
import com.threerings.micasa.client.MiCasaFrame;
/**
* The micasa context encapsulates the contexts of all of the services
* that are used by the micasa client so that we can pass around one
* single context implementation that provides all of the necessary
* components to all of the services in use.
*/
public interface MiCasaContext
extends ParlorContext
{
/** Returns a reference to the primary user interface frame. This can
* be used to set the top-level panel when we enter a game, etc. */
public MiCasaFrame getFrame ();
/**
* Returns a reference to the message manager used by the client to
* generate localized messages.
*/
public MessageManager getMessageManager ();
}
@@ -0,0 +1,33 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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;
import com.samskivert.util.Logger;
/**
* Contains a reference to the log object used by the Parlor services.
*/
public class Log
{
/** We dispatch our log messages through this logger. */
public static Logger log = Logger.getLogger("com.threerings.parlor");
}
@@ -0,0 +1,32 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.card;
import com.samskivert.util.Logger;
/**
* Contains a reference to the log object used by the Card services.
*/
public class Log
{
public static Logger log = Logger.getLogger("com.threerings.parlor.card");
}
@@ -0,0 +1,118 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.card.client;
import com.threerings.util.Name;
import com.threerings.crowd.data.PlaceObject;
import com.threerings.parlor.card.data.Card;
import com.threerings.parlor.card.data.CardCodes;
import com.threerings.parlor.card.data.Hand;
import com.threerings.parlor.game.client.GameController;
import com.threerings.parlor.turn.client.TurnGameController;
import static com.threerings.parlor.card.Log.log;
/**
* A controller class for card games. Handles common functions like accepting dealt hands.
*/
public abstract class CardGameController extends GameController
implements TurnGameController, CardCodes, CardGameReceiver
{
@Override
public void willEnterPlace (PlaceObject plobj)
{
if (_ctx.getClient().getClientObject().receivers.containsKey(
CardGameDecoder.RECEIVER_CODE)) {
log.warning("Yuh oh, we already have a card game receiver registered and are trying " +
"for another...!", new Exception());
}
_ctx.getClient().getInvocationDirector().registerReceiver(new CardGameDecoder(this));
super.willEnterPlace(plobj);
}
@Override
public void didLeavePlace (PlaceObject plobj)
{
super.didLeavePlace(plobj);
_ctx.getClient().getInvocationDirector().unregisterReceiver(CardGameDecoder.RECEIVER_CODE);
}
// Documentation inherited.
public void turnDidChange (Name turnHolder)
{}
/**
* Called by our sender to notify us of a received hand.
*/
public final void receivedHand (int oid, Hand hand)
{
if (oid == _gobj.getOid()) {
receivedHand(hand);
}
}
/**
* Called when the server deals the client a new hand of cards. Default implementation does
* nothing.
*
* @param hand the hand dealt to the user
*/
public void receivedHand (Hand hand)
{}
/**
* Dispatched to the client when it has received a set of cards from another player. Default
* implementation does nothing.
*
* @param plidx the index of the player providing the cards
* @param cards the cards received
*/
public void receivedCardsFromPlayer (int plidx, Card[] cards)
{}
/**
* Dispatched to the client when the server has forced it to send a set of cards to another
* player. Default implementation does nothing.
*
* @param plidx the index of the player to which the cards were sent
* @param cards the cards sent
*/
public void sentCardsToPlayer (int plidx, Card[] cards)
{}
/**
* Dispatched to the client when a set of cards is transferred between two other players in
* the game. Default implementation does nothing.
*
* @param fromidx the index of the player sending the cards
* @param toidx the index of the player receiving the cards
* @param cards the number of cards transferred
*/
public void cardsTransferredBetweenPlayers (int fromidx, int toidx,
int cards)
{}
}
@@ -0,0 +1,100 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.card.client;
import com.threerings.parlor.card.data.Card;
import com.threerings.parlor.card.data.Hand;
import com.threerings.presents.client.InvocationDecoder;
/**
* Dispatches calls to a {@link CardGameReceiver} instance.
*/
public class CardGameDecoder extends InvocationDecoder
{
/** The generated hash code used to identify this receiver class. */
public static final String RECEIVER_CODE = "0718199d459e31d8d673744c71b0e788";
/** The method id used to dispatch {@link CardGameReceiver#cardsTransferredBetweenPlayers}
* notifications. */
public static final int CARDS_TRANSFERRED_BETWEEN_PLAYERS = 1;
/** The method id used to dispatch {@link CardGameReceiver#receivedCardsFromPlayer}
* notifications. */
public static final int RECEIVED_CARDS_FROM_PLAYER = 2;
/** The method id used to dispatch {@link CardGameReceiver#receivedHand}
* notifications. */
public static final int RECEIVED_HAND = 3;
/** The method id used to dispatch {@link CardGameReceiver#sentCardsToPlayer}
* notifications. */
public static final int SENT_CARDS_TO_PLAYER = 4;
/**
* Creates a decoder that may be registered to dispatch invocation
* service notifications to the specified receiver.
*/
public CardGameDecoder (CardGameReceiver receiver)
{
this.receiver = receiver;
}
@Override
public String getReceiverCode ()
{
return RECEIVER_CODE;
}
@Override
public void dispatchNotification (int methodId, Object[] args)
{
switch (methodId) {
case CARDS_TRANSFERRED_BETWEEN_PLAYERS:
((CardGameReceiver)receiver).cardsTransferredBetweenPlayers(
((Integer)args[0]).intValue(), ((Integer)args[1]).intValue(), ((Integer)args[2]).intValue()
);
return;
case RECEIVED_CARDS_FROM_PLAYER:
((CardGameReceiver)receiver).receivedCardsFromPlayer(
((Integer)args[0]).intValue(), (Card[])args[1]
);
return;
case RECEIVED_HAND:
((CardGameReceiver)receiver).receivedHand(
((Integer)args[0]).intValue(), (Hand)args[1]
);
return;
case SENT_CARDS_TO_PLAYER:
((CardGameReceiver)receiver).sentCardsToPlayer(
((Integer)args[0]).intValue(), (Card[])args[1]
);
return;
default:
super.dispatchNotification(methodId, args);
return;
}
}
}
@@ -0,0 +1,71 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.card.client;
import com.threerings.presents.client.InvocationReceiver;
import com.threerings.parlor.card.data.Card;
import com.threerings.parlor.card.data.Hand;
/**
* Defines, for the card game services, a set of notifications delivered
* asynchronously by the server to the client.
*/
public interface CardGameReceiver extends InvocationReceiver
{
/**
* Dispatched to the client when it has received a hand of cards.
*
* @param oid the oid of the game for which this hand applies
* @param hand the received hand
*/
public void receivedHand (int oid, Hand hand);
/**
* Dispatched to the client when it has received a set of cards
* from another player.
*
* @param plidx the index of the player providing the cards
* @param cards the cards received
*/
public void receivedCardsFromPlayer (int plidx, Card[] cards);
/**
* Dispatched to the client when the server has forced it to send
* a set of cards to another player.
*
* @param plidx the index of the player to which the cards were sent
* @param cards the cards sent
*/
public void sentCardsToPlayer (int plidx, Card[] cards);
/**
* Dispatched to the client when a set of cards is transferred between
* two other players in the game.
*
* @param fromidx the index of the player sending the cards
* @param toidx the index of the player receiving the cards
* @param cards the number of cards transferred
*/
public void cardsTransferredBetweenPlayers (int fromidx, int toidx,
int cards);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,235 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.card.client;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import com.threerings.media.sprite.FadableImageSprite;
import com.threerings.parlor.card.data.Card;
/**
* A sprite representing a playing card.
*/
public class CardSprite extends FadableImageSprite
{
/**
* Creates a new upward-facing card sprite.
*
* @param panel the panel responsible for the sprite
* @param card the card to depict (can be null, in which case the
* card back will be shown)
*/
public CardSprite (CardPanel panel, Card card)
{
_panel = panel;
_card = card;
_facingUp = true;
updateMirage();
}
/**
* Creates a new card sprite.
*
* @param panel the panel responsible for the sprite
* @param card the card to depict
* @param facingUp whether or not the card should be facing up
*/
public CardSprite (CardPanel panel, Card card, boolean facingUp)
{
_panel = panel;
_card = card;
_facingUp = facingUp;
updateMirage();
}
/**
* Sets the card to depict.
*
* @param card the new card
*/
public void setCard (Card card)
{
_card = card;
updateMirage();
}
/**
* Returns the card being depicted.
*
* @return the current card
*/
public Card getCard ()
{
return _card;
}
/**
* Turns this card up or down.
*
* @param facingUp whether or not the card should be facing up
*/
public void setFacingUp (boolean facingUp)
{
_facingUp = facingUp;
updateMirage();
}
/**
* Checks whether this card is facing up or down.
*
* @return true if the card is facing up, false if facing down
*/
public boolean isFacingUp ()
{
return _facingUp;
}
/**
* Sets whether or not the user can drag this card around the board.
*
* @param draggable whether or not the user can drag the card
*/
public void setDraggable (boolean draggable)
{
_draggable = draggable;
}
/**
* Checks whether or not the user can drag this card.
*
* @return true if the user can drag the card, false if not
*/
public boolean isDraggable ()
{
return _draggable;
}
/**
* Flip the card from its current displayed card to the specified card.
*/
public void flip (Card newCard, long duration)
{
_flipStamp = 0;
_flipDuration = duration;
_flipCard = newCard;
_scaleFactor = 1.0;
}
@Override
public void tick (long tickStamp)
{
super.tick(tickStamp);
// Take care of any flipping we might be doing.
if (_flipDuration != -1) {
if (_flipStamp == 0) {
_flipStamp = tickStamp;
}
long diff = tickStamp - _flipStamp;
// Set the new scale while we're flipping
if (diff < _flipDuration/2) {
_scaleFactor = 1.0 - ((float)diff*2)/_flipDuration;
} else {
// Switch the image to the card we're flipping to.
if (_flipCard != null) {
setCard(_flipCard);
_flipCard = null;
}
_scaleFactor = ((float)diff*2)/_flipDuration - 1.0;
}
// If we're done, stop flipping.
if (_scaleFactor > 1.0) {
_scaleFactor = 1.0;
_flipDuration = -1;
}
// Make sure we flag our location as needing redrawing
if (_mgr != null) {
_mgr.getRegionManager().invalidateRegion(_bounds);
}
}
}
@Override
public void paint (Graphics2D gfx)
{
if (_scaleFactor <= 0) {
return;
}
// If we are flipping the card, scale it horizontally.
AffineTransform otrans = gfx.getTransform();
if (_scaleFactor < 1.0) {
int xtrans = getX() + getWidth()/2;
gfx.translate(xtrans, 0);
gfx.scale(_scaleFactor, 1.0);
gfx.translate(-xtrans, 0);
}
super.paint(gfx);
gfx.setTransform(otrans);
}
/**
* Updates the mirage according to the current state.
*/
protected void updateMirage ()
{
setMirage((_card != null && _facingUp ) ?
_panel.getCardImage(_card) : _panel.getCardBackImage());
}
/** The panel responsible for the sprite. */
protected CardPanel _panel;
/** The depicted card. */
protected Card _card;
/** Whether or not the card is facing up. */
protected boolean _facingUp;
/** Whether or not the user can drag the card around the board. */
protected boolean _draggable;
/** The horizontal scale factor used while flipping the card. */
protected double _scaleFactor = 1.0;
/** If flipping, how long the current flip should take (otherwise -1). */
protected long _flipDuration;
/** The timestamp for when we started flipping the card. */
protected long _flipStamp;
/** The card which will be revealed when we're done flipping. */
protected Card _flipCard;
}
@@ -0,0 +1,65 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.card.client;
import java.awt.event.MouseEvent;
/**
* Observer interface for (draggable) card sprites.
*/
public interface CardSpriteObserver
{
/**
* Notifies the observer that the user clicked a card sprite.
*
* @param sprite the dragged sprite
* @param me the mouse event associated with the drag
*/
public void cardSpriteClicked (CardSprite sprite, MouseEvent me);
/**
* Notifies the observer that the user moved the mouse pointer onto
* a card sprite.
*
* @param sprite the entered sprite
* @param me the mouse event associated with the entrance
*/
public void cardSpriteEntered (CardSprite sprite, MouseEvent me);
/**
* Notifies the observer that the user moved the mouse pointer off of
* a card sprite.
*
* @param sprite the exited the sprite
* @param me the mouse event associated with the exit
*/
public void cardSpriteExited (CardSprite sprite, MouseEvent me);
/**
* Notifies the observer that the user dragged a card sprite to a new
* location.
*
* @param sprite the dragged sprite
* @param me the mouse event associated with the drag
*/
public void cardSpriteDragged (CardSprite sprite, MouseEvent me);
}
@@ -0,0 +1,59 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.card.client;
import com.threerings.parlor.card.data.Card;
public class MicroCardSprite extends CardSprite
{
/**
* Creates a new upward-facing micro-card sprite.
*
* @param panel the panel responsible for the sprite
* @param card the card to depict (can be null, in which case the
* card back will be shown)
*/
public MicroCardSprite (CardPanel panel, Card card)
{
super(panel, card);
}
/**
* Creates a new micro-card sprite.
*
* @param panel the panel responsible for the sprite
* @param card the card to depict
* @param facingUp whether or not the card should be facing up
*/
public MicroCardSprite (CardPanel panel, Card card, boolean facingUp)
{
super(panel, card, facingUp);
}
@Override
protected void updateMirage ()
{
setMirage((_card != null && _facingUp ) ?
_panel.getMicroCardImage(_card) : _panel.getMicroCardBackImage());
}
}
@@ -0,0 +1,228 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.card.data;
import com.threerings.util.ActionScript;
import com.threerings.presents.dobj.DSet;
/**
* Instances of this class represent individual playing cards.
*/
public class Card implements DSet.Entry, Comparable<Card>, CardCodes
{
/**
* No-arg constructor for deserialization.
*/
public Card ()
{}
/**
* Creates a new card.
*
* @param number the number of the card
* @param suit the suit of the card
*/
public Card (int number, int suit)
{
_value = (byte)((suit << 5) | number);
}
/**
* Returns the value of the card, either from 2 to 11 or KING, QUEEN, JACK, ACE, RED_JOKER, or
* BLACK_JOKER.
*
* @return the value of the card
*/
public int getNumber ()
{
return (_value & 0x1F);
}
/**
* Returns the suit of the card: SPADES, HEARTS, DIAMONDS, or CLUBS. If the card is the joker,
* the suit is undefined.
*
* @return the suit of the card
*/
public int getSuit ()
{
return (_value >> 5);
}
/**
* Checks whether the card is a number card (2 to 10).
*
* @return true if the card is a number card, false otherwise
*/
public boolean isNumber ()
{
int number = getNumber();
return number >= 2 && number <= 10;
}
/**
* Checks whether the card is a face card (KING, QUEEN, or JACK).
*
* @return true if the card is a face card, false otherwise
*/
public boolean isFace ()
{
int number = getNumber();
return number == KING || number == QUEEN || number == JACK;
}
/**
* Checks whether the card is an ace.
*
* @return true if the card is an ace, false otherwise
*/
public boolean isAce ()
{
return getNumber() == ACE;
}
/**
* Checks whether the card is a joker.
*
* @return true if the card is a joker, false otherwise
*/
public boolean isJoker ()
{
int number = getNumber();
return number == RED_JOKER || number == BLACK_JOKER;
}
/**
* Checks whether or not this card is valid. The no-arg public constructor for deserialization
* creates an invalid card.
*
* @return true if this card is valid, false if not
*/
public boolean isValid ()
{
int number = getNumber(), suit = getSuit();
return number == RED_JOKER || number == BLACK_JOKER ||
(number >= 2 && number <= ACE &&
suit >= SPADES && suit <= DIAMONDS);
}
// Documentation inherited.
public Comparable<?> getKey ()
{
if (_key == null) {
_key = Byte.valueOf(_value);
}
return _key;
}
@Override
public int hashCode ()
{
return _value;
}
@Override
public boolean equals (Object other)
{
if (other instanceof Card) {
return _value == ((Card)other)._value;
}
else {
return false;
}
}
/**
* Compares this card to another. The card order is the same as the initial deck ordering: two
* through ten, jack, queen, king, ace for spades, hearts, clubs, and diamonds, then the red
* joker and the black joker.
*
* @param other the other card to compare this to
* @return -1, 0, or +1, depending on whether this card is less than, equal to, or greater
* than the other card
*/
public int compareTo (Card other)
{
int otherValue = other._value;
if (_value > otherValue) {
return +1;
} else if(_value < otherValue) {
return -1;
} else {
return 0;
}
}
@Override
public String toString ()
{
int number = getNumber();
if (number == RED_JOKER) {
return "RJ";
}
else if (number == BLACK_JOKER) {
return "BJ";
}
else {
StringBuilder sb = new StringBuilder();
if (number >= 2 && number <= 9) {
sb.append(Integer.toString(number));
}
else {
switch (number) {
case 10: sb.append('T'); break;
case JACK: sb.append('J'); break;
case QUEEN: sb.append('Q'); break;
case KING: sb.append('K'); break;
case ACE: sb.append('A'); break;
default: sb.append('?'); break;
}
}
switch (getSuit()) {
case SPADES: sb.append('s'); break;
case HEARTS: sb.append('h'); break;
case CLUBS: sb.append('c'); break;
case DIAMONDS: sb.append('d'); break;
default: sb.append('?'); break;
}
return sb.toString();
}
}
/** The number of the card. */
protected byte _value;
/** The comparison key. */
@ActionScript(omit=true)
protected transient Byte _key;
}
@@ -0,0 +1,60 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.card.data;
import com.threerings.presents.data.InvocationCodes;
/**
* Constants relating to the card services.
*/
public interface CardCodes extends InvocationCodes
{
/** The suit of spades. */
public static final int SPADES = 0;
/** The suit of hearts. */
public static final int HEARTS = 1;
/** The suit of clubs. */
public static final int CLUBS = 2;
/** The suit of diamonds. */
public static final int DIAMONDS = 3;
/** The number of the jack. */
public static final int JACK = 11;
/** The number of the queen. */
public static final int QUEEN = 12;
/** The number of the king. */
public static final int KING = 13;
/** The number of the ace. */
public static final int ACE = 14;
/** The number of the red joker. */
public static final int RED_JOKER = 15;
/** The number of the black joker. */
public static final int BLACK_JOKER = 16;
}
@@ -0,0 +1,31 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.card.data;
import com.threerings.parlor.game.data.GameObject;
/**
* Game object class for card games.
*/
public class CardGameObject extends GameObject
{
}
@@ -0,0 +1,130 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.card.data;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import com.threerings.util.StreamableArrayList;
/**
* Instances of this class represent decks of cards.
*/
public class Deck extends StreamableArrayList<Card>
implements CardCodes
{
/**
* Default constructor creates an unshuffled deck of cards without
* jokers.
*/
public Deck ()
{
reset(false);
}
/**
* Constructor.
*
* @param includeJokers whether or not to include the two jokers
* in the deck
*/
public Deck (boolean includeJokers)
{
reset(includeJokers);
}
/**
* Resets the deck to its initial state: an unshuffled deck of
* 52 or 54 cards, depending on whether the jokers are included.
*
* @param includeJokers whether or not to include the two jokers
* in the deck
*/
public void reset (boolean includeJokers)
{
clear();
for (int ii = SPADES; ii <= DIAMONDS; ii++) {
for (int j = 2; j <= ACE; j++) {
add(new Card(j, ii));
}
}
if (includeJokers) {
add(new Card(RED_JOKER, 3));
add(new Card(BLACK_JOKER, 3));
}
}
/**
* Shuffles the deck.
*/
public void shuffle ()
{
Collections.shuffle(this);
}
/**
* Shuffles the deck.
*/
public void shuffle (Random r)
{
Collections.shuffle(this, r);
}
/**
* Deals a hand of cards from the deck.
*
* @param size the size of the hand to deal
* @return the newly created and populated hand, or null
* if there are not enough cards in the deck to deal the hand
*/
public Hand dealHand (int size)
{
int dsize = size();
if (dsize < size) {
return null;
} else {
Hand hand = new Hand();
// use a sublist view to manipulate the top of the deck
List<Card> sublist = subList(dsize - size, dsize);
hand.addAll(sublist);
sublist.clear();
return hand;
}
}
/**
* Returns a hand of cards to the deck.
*
* @param hand the hand of cards to return
*/
public void returnHand (Hand hand)
{
addAll(hand);
hand.clear();
}
}
@@ -0,0 +1,98 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.card.data;
import java.util.ArrayList;
import com.threerings.util.ActionScript;
import com.threerings.util.StreamableArrayList;
/**
* Instances of this class represent hands of cards.
*/
public class Hand extends StreamableArrayList<Card>
{
/**
* Adds all of the specified cards to this hand.
*/
@ActionScript(name="addAllCards")
public void addAll (Card[] cards)
{
for (Card card : cards) {
add(card);
}
}
/**
* Removes all of the specified cards from this hand.
*/
@ActionScript(name="removeAllCards")
public void removeAll (Card[] cards)
{
for (Card card : cards) {
remove(card);
}
}
/**
* Checks whether this hand contains all of the specified cards.
*/
@ActionScript(name="containsAllCards")
public boolean containsAll (Card[] cards)
{
for (int ii = 0; ii < cards.length; ii++) {
if (!contains(cards[ii])) {
return false;
}
}
return true;
}
/**
* Counts the members of a particular suit within this hand.
*
* @param suit the suit of interest
* @return the number of cards in the specified suit
*/
public int getSuitMemberCount (int suit)
{
int len = size(), members = 0;
for (int ii = 0; ii < len; ii++) {
if (get(ii).getSuit() == suit) {
members++;
}
}
return members;
}
/**
* Converts this list of cards into an array. This only necessary if you
* need the cards in array form, use {@link ArrayList#get} if you simply need to get
* a card from the hand.
*/
public Card[] getCards ()
{
Card[] cards = new Card[size()];
toArray(cards);
return cards;
}
}
@@ -0,0 +1,54 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.card.data;
import com.threerings.io.Streamable;
/**
* Pairs a player index with the card that the player played in the trick.
*/
public class PlayerCard implements Streamable
{
/** The index of the player. */
public int pidx;
/** The card that the player played. */
public Card card;
/**
* No-argument constructor for deserialization.
*/
public PlayerCard ()
{}
/**
* Creates a new player card.
*
* @param pidx the index of the player
* @param card the card played
*/
public PlayerCard (int pidx, Card card)
{
this.pidx = pidx;
this.card = card;
}
}
@@ -0,0 +1,267 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.card.server;
import com.threerings.presents.data.ClientObject;
import com.threerings.crowd.data.OccupantInfo;
import com.threerings.crowd.server.OccupantOp;
import com.threerings.parlor.card.data.Card;
import com.threerings.parlor.card.data.CardCodes;
import com.threerings.parlor.card.data.CardGameObject;
import com.threerings.parlor.card.data.Deck;
import com.threerings.parlor.card.data.Hand;
import com.threerings.parlor.game.data.GameObject;
import com.threerings.parlor.game.server.GameManager;
import com.threerings.parlor.turn.server.TurnGameManager;
/**
* A manager class for card games. Handles common functions like dealing hands of cards to all
* players.
*/
public class CardGameManager extends GameManager
implements TurnGameManager, CardCodes
{
@Override
protected void didStartup ()
{
super.didStartup();
_cardgameobj = (CardGameObject)_gameobj;
}
// Documentation inherited.
public void turnWillStart ()
{}
// Documentation inherited.
public void turnDidStart ()
{}
// Documentation inherited.
public void turnDidEnd ()
{}
@Override
protected void gameDidEnd ()
{
// Copy off our player oids so we have them handy to see if somebody was a player for
// rematch purposes.
_oldPlayerOids = _playerOids.clone();
super.gameDidEnd();
}
/**
* This should be called to start a rematched game. It just starts the current game anew, but
* provides a mechanism for derived classes to do special things when there is a rematch.
*/
public void rematchGame ()
{
if (gameWillRematch()) {
// Stuff our old oids back in since they're ready.
_playerOids = _oldPlayerOids.clone();
startGame();
}
}
/**
* Derived classes can override this method and take any action needed prior to a game
* rematch. If the rematch needs to be vetoed for any reason, they can return false from this
* method and the rematch will be aborted.
*/
protected boolean gameWillRematch ()
{
return true;
}
/**
* Deals a hand of cards to the player at the specified index from the given Deck.
*
* @param deck the deck from which to deal
* @param size the size of the hand to deal
* @param playerIndex the index of the target player
* @return the hand dealt to the player, or null if the deal was canceled because the deck did
* not contain enough cards
*/
public Hand dealHand (Deck deck, int size, int playerIndex)
{
if (deck.size() < size) {
return null;
} else {
Hand hand = deck.dealHand(size);
if (!isAI(playerIndex)) {
ClientObject clobj = (ClientObject)_omgr.getObject(_playerOids[playerIndex]);
if (clobj != null) {
CardGameSender.sendHand(clobj, _cardgameobj.getOid(), hand);
}
}
return hand;
}
}
/**
* Deals a hand of cards to each player from the specified Deck.
*
* @param deck the deck from which to deal
* @param size the size of the hands to deal
* @return the array of hands dealt to each player, or null if the deal was canceled because
* the deck did not contain enough cards
*/
public Hand[] dealHands (Deck deck, int size)
{
if (deck.size() < size * _playerCount) {
return null;
} else {
Hand[] hands = new Hand[_playerCount];
for (int ii = 0; ii < _playerCount; ii++) {
hands[ii] = dealHand(deck, size, ii);
}
return hands;
}
}
/**
* Gets the player index of the specified client object, or -1 if the client object does not
* represent a player. If the game has ended, looks through the players of the now-ended game.
*/
public int getPlayerIndex (ClientObject client)
{
int[] oids = (_gameobj.state == GameObject.GAME_OVER) ? _oldPlayerOids : _playerOids;
int oid = client.getOid();
for (int ii = 0; ii < oids.length; ii++) {
if (oids[ii] == oid) {
return ii;
}
}
return -1;
}
/**
* Returns the client object corresponding to the specified player index, or null if the
* position is not occupied by a player.
*/
public ClientObject getClientObject (int pidx)
{
if (_playerOids[pidx] != 0) {
return (ClientObject)_omgr.getObject(_playerOids[pidx]);
} else {
return null;
}
}
/**
* Sends a set of cards from one player to another.
*
* @param fromPlayerIdx the index of the player sending the cards
* @param toPlayerIdx the index of the player receiving the cards
* @param cards the cards to be exchanged
*/
public void transferCardsBetweenPlayers (int fromPlayerIdx,
int toPlayerIdx, Card[] cards)
{
// Notify the sender that the cards have been taken
ClientObject fromClient = getClientObject(fromPlayerIdx);
if (fromClient != null) {
CardGameSender.sentCardsToPlayer(fromClient, toPlayerIdx, cards);
}
// Notify the receiver with the cards
ClientObject toClient = getClientObject(toPlayerIdx);
if (toClient != null) {
CardGameSender.sendCardsFromPlayer(toClient, fromPlayerIdx,
cards);
}
// and everybody else in the room other than the sender and the
// receiver with the number of cards sent
notifyCardsTransferred(fromPlayerIdx, toPlayerIdx, cards.length);
}
/**
* Sends sets of cards between players simultaneously. Each player is guaranteed to receive
* the notification of cards received after the notification of cards sent. The length of the
* arrays passed must be equal to the player count.
*
* @param toPlayerIndices for each player, the index of the player to transfer cards to
* @param cards for each player, the cards to transfer
*/
public void transferCardsBetweenPlayers (int[] toPlayerIndices, Card[][] cards)
{
// Send all removal notices
for (int ii = 0; ii < _playerCount; ii++) {
ClientObject fromClient = getClientObject(ii);
if (fromClient != null) {
CardGameSender.sentCardsToPlayer(fromClient, toPlayerIndices[ii], cards[ii]);
}
}
// Send all addition notices and notify everyone else
for (int ii = 0; ii < _playerCount; ii++) {
ClientObject toClient = getClientObject(toPlayerIndices[ii]);
if (toClient != null) {
CardGameSender.sendCardsFromPlayer(toClient, ii, cards[ii]);
}
notifyCardsTransferred(ii, toPlayerIndices[ii], cards[ii].length);
}
}
/**
* Notifies everyone in the room (other than the sender and the receiver) that a set of cards
* have been transferred.
*
* @param fromPlayerIdx the index of the player sending the cards
* @param toPlayerIdx the index of the player receiving the cards
* @param cards the number of cards sent
*/
protected void notifyCardsTransferred (
final int fromPlayerIdx, final int toPlayerIdx, final int cards)
{
final int senderOid = _playerOids[fromPlayerIdx],
receiverOid = _playerOids[toPlayerIdx];
OccupantOp op = new OccupantOp() {
public void apply (OccupantInfo info) {
int oid = info.getBodyOid();
if (oid != senderOid && oid != receiverOid) {
ClientObject client = (ClientObject)_omgr.getObject(oid);
if (client != null) {
CardGameSender.cardsTransferredBetweenPlayers(client,
fromPlayerIdx, toPlayerIdx, cards);
}
}
}
};
applyToOccupants(op);
}
/** The card game object. */
protected CardGameObject _cardgameobj;
/** The oids of the players from our now-ended game. */
protected int[] _oldPlayerOids;
}
@@ -0,0 +1,85 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.card.server;
import com.threerings.parlor.card.client.CardGameDecoder;
import com.threerings.parlor.card.client.CardGameReceiver;
import com.threerings.parlor.card.data.Card;
import com.threerings.parlor.card.data.Hand;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.server.InvocationSender;
/**
* Used to issue notifications to a {@link CardGameReceiver} instance on a
* client.
*/
public class CardGameSender extends InvocationSender
{
/**
* Issues a notification that will result in a call to {@link
* CardGameReceiver#cardsTransferredBetweenPlayers} on a client.
*/
public static void cardsTransferredBetweenPlayers (
ClientObject target, int arg1, int arg2, int arg3)
{
sendNotification(
target, CardGameDecoder.RECEIVER_CODE, CardGameDecoder.CARDS_TRANSFERRED_BETWEEN_PLAYERS,
new Object[] { Integer.valueOf(arg1), Integer.valueOf(arg2), Integer.valueOf(arg3) });
}
/**
* Issues a notification that will result in a call to {@link
* CardGameReceiver#receivedCardsFromPlayer} on a client.
*/
public static void sendCardsFromPlayer (
ClientObject target, int arg1, Card[] arg2)
{
sendNotification(
target, CardGameDecoder.RECEIVER_CODE, CardGameDecoder.RECEIVED_CARDS_FROM_PLAYER,
new Object[] { Integer.valueOf(arg1), arg2 });
}
/**
* Issues a notification that will result in a call to {@link
* CardGameReceiver#receivedHand} on a client.
*/
public static void sendHand (
ClientObject target, int arg1, Hand arg2)
{
sendNotification(
target, CardGameDecoder.RECEIVER_CODE, CardGameDecoder.RECEIVED_HAND,
new Object[] { Integer.valueOf(arg1), arg2 });
}
/**
* Issues a notification that will result in a call to {@link
* CardGameReceiver#sentCardsToPlayer} on a client.
*/
public static void sentCardsToPlayer (
ClientObject target, int arg1, Card[] arg2)
{
sendNotification(
target, CardGameDecoder.RECEIVER_CODE, CardGameDecoder.SENT_CARDS_TO_PLAYER,
new Object[] { Integer.valueOf(arg1), arg2 });
}
}
@@ -0,0 +1,46 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.card.trick.client;
import com.threerings.parlor.card.client.CardGameController;
import com.threerings.parlor.turn.client.TurnGameControllerDelegate;
/**
* A card game controller delegate for trick-based card games, such as Spades and Hearts.
*/
public class TrickCardGameControllerDelegate
extends TurnGameControllerDelegate
{
/**
* Constructor.
*
* @param controller the game controller
*/
public TrickCardGameControllerDelegate (CardGameController controller)
{
super(controller);
_cgctrl = controller;
}
/** The card game controller. */
protected CardGameController _cgctrl;
}
@@ -0,0 +1,54 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.card.trick.client;
import com.threerings.presents.client.InvocationService;
import com.threerings.parlor.card.data.Card;
/**
* Service calls related to trick card games.
*/
public interface TrickCardGameService extends InvocationService
{
/**
* Sends a group of cards to the player at the specified index.
*
* @param toidx the index of the player to send the cards to
* @param cards the cards to send
*/
public void sendCardsToPlayer (int toidx, Card[] cards);
/**
* Plays a card in the trick.
*
* @param card the card to play
* @param handSize the size of the player's hand, which is used to verify
* that the request is for the current trick
*/
public void playCard (Card card, int handSize);
/**
* A request for a rematch.
*/
public void requestRematch ();
}
@@ -0,0 +1,42 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.card.trick.data;
import com.threerings.parlor.card.data.CardCodes;
/**
* Constants relating to trick-based card games.
*/
public interface TrickCardCodes extends CardCodes
{
/** For four-player games, the bottom (own) player. */
public static final int BOTTOM = 0;
/** For four-player games, the player on the left. */
public static final int LEFT = 1;
/** For four-player games, the top (opposite) player. */
public static final int TOP = 2;
/** For four-player games, the player on the right. */
public static final int RIGHT = 3;
}
@@ -0,0 +1,73 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.card.trick.data;
import javax.annotation.Generated;
import com.threerings.parlor.card.data.Card;
import com.threerings.parlor.card.trick.client.TrickCardGameService;
import com.threerings.presents.data.InvocationMarshaller;
/**
* Provides the implementation of the {@link TrickCardGameService} interface
* that marshalls the arguments and delivers the request to the provider
* on the server. Also provides an implementation of the response listener
* interfaces that marshall the response arguments and deliver them back
* to the requesting client.
*/
@Generated(value={"com.threerings.presents.tools.GenServiceTask"},
comments="Derived from TrickCardGameService.java.")
public class TrickCardGameMarshaller extends InvocationMarshaller
implements TrickCardGameService
{
/** The method id used to dispatch {@link #playCard} requests. */
public static final int PLAY_CARD = 1;
// from interface TrickCardGameService
public void playCard (Card arg1, int arg2)
{
sendRequest(PLAY_CARD, new Object[] {
arg1, Integer.valueOf(arg2)
});
}
/** The method id used to dispatch {@link #requestRematch} requests. */
public static final int REQUEST_REMATCH = 2;
// from interface TrickCardGameService
public void requestRematch ()
{
sendRequest(REQUEST_REMATCH, new Object[] {
});
}
/** The method id used to dispatch {@link #sendCardsToPlayer} requests. */
public static final int SEND_CARDS_TO_PLAYER = 3;
// from interface TrickCardGameService
public void sendCardsToPlayer (int arg1, Card[] arg2)
{
sendRequest(SEND_CARDS_TO_PLAYER, new Object[] {
Integer.valueOf(arg1), arg2
});
}
}
@@ -0,0 +1,210 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.card.trick.data;
import com.threerings.parlor.card.data.Card;
import com.threerings.parlor.card.data.Hand;
import com.threerings.parlor.card.data.PlayerCard;
import com.threerings.parlor.turn.data.TurnGameObject;
/**
* Game objects for trick-based card games must implement this interface.
*/
public interface TrickCardGameObject extends TurnGameObject
{
/** The state that indicates the game is currently between hands. */
public static final int BETWEEN_HANDS = 0;
/** The state that indicates the game is currently playing a hand. */
public static final int PLAYING_HAND = 1;
/** The state that indicates the game is currently playing a trick. */
public static final int PLAYING_TRICK = 2;
/** The number of states defined for the base trick card game object. */
public static final int TRICK_STATE_COUNT = 3;
/** Indicates that the player has not requested or accepted a rematch. */
public static final int NO_REQUEST = 0;
/** Indicates that the player has requested a rematch. */
public static final int REQUESTS_REMATCH = 1;
/** Indicates that the player has accepted the rematch request. */
public static final int ACCEPTS_REMATCH = 2;
/**
* Returns a reference to the trick card game service used to make
* requests to the server.
*
* @return a reference to the trick card game service
*/
public TrickCardGameMarshaller getTrickCardGameService ();
/**
* Sets the reference to the trick card game service.
*
* @param trickCardGameService the trick card game service
*/
public void setTrickCardGameService (TrickCardGameMarshaller
trickCardGameService);
/**
* Returns the name of the field that contains the trick state: between
* hands, playing a hand, or playing a trick.
*
* @return the name of the trickState field
*/
public String getTrickStateFieldName ();
/**
* Returns the trick state: between hands, playing a hand, or playing a
* trick.
*
* @return the trick state
*/
public int getTrickState ();
/**
* Sets the trick state.
*
* @param trickState the trick state
*/
public void setTrickState (int trickState);
/**
* Returns an array containing the turn duration scales for each player.
* Turn duration scales decrease each time players time out.
*
* @return the array of turn duration scales
*/
public float[] getTurnDurationScales ();
/**
* Sets the array of turn duration scales.
*
* @param turnDurationScales the array of turn duration scales
*/
public void setTurnDurationScales (float[] turnDurationScales);
/**
* Sets an element of the array of turn duration scales.
*
* @param turnDurationScale the turn duration scale
* @param index the index of the turn duration scale
*/
public void setTurnDurationScalesAt (float turnDurationScale, int index);
/**
* Returns the duration of the current turn, which may depend on the state
* of the game as well as the duration scale of the active player.
*/
public long getTurnDuration ();
/**
* Returns the name of the field that contains the history of the trick
* in terms of the cards played by each player.
*
* @return the name of the cardsPlayed field
*/
public String getCardsPlayedFieldName ();
/**
* Returns an array containing the history of the trick in terms of the
* cards played by each player.
*
* @return the cards played so far in the trick
*/
public PlayerCard[] getCardsPlayed ();
/**
* Sets the array of cards played by each player.
*
* @param cardsPlayed the array of cards played
*/
public void setCardsPlayed (PlayerCard[] cardsPlayed);
/**
* Returns the name of the field that contains the history of the last
* trick in terms of the cards played by each player.
*
* @return the name of the lastCardsPlayed field
*/
public String getLastCardsPlayedFieldName ();
/**
* Returns an array containing the history of the last trick in terms of
* the cards played by each player.
*
* @return the cards played in the last trick
*/
public PlayerCard[] getLastCardsPlayed ();
/**
* Sets the last array of cards played by each player.
*
* @param lastCardsPlayed the last array of cards played
*/
public void setLastCardsPlayed (PlayerCard[] lastCardsPlayed);
/**
* Returns the name of the field that contains the rematch requests.
*
* @return the name of the rematchRequests field
*/
public String getRematchRequestsFieldName ();
/**
* Returns the array of rematch requests.
*
* @return the array of rematch requests
*/
public int[] getRematchRequests ();
/**
* Sets the array of rematch requests.
*
* @param rematchRequests the array of rematch requests
*/
public void setRematchRequests (int[] rematchRequests);
/**
* Sets an element of the rematch request array.
*
* @param rematchRequest the rematch request value
* @param index the index at which to set the value
*/
public void setRematchRequestsAt (int rematchRequest, int index);
/**
* Checks whether a user can play the specified card at this time.
*
* @param hand the player's hand
* @param card the card that the user would like to play
*/
public boolean isCardPlayable (Hand hand, Card card);
/**
* Returns the card of the player who took the current trick.
*/
public PlayerCard getTrickTaker ();
}
@@ -0,0 +1,619 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.card.trick.server;
import java.util.Arrays;
import java.util.List;
import com.google.common.collect.Lists;
import com.samskivert.util.ArrayUtil;
import com.samskivert.util.Interval;
import com.samskivert.util.RandomUtil;
import com.threerings.util.Name;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.dobj.DObject;
import com.threerings.presents.dobj.RootDObjectManager;
import com.threerings.presents.server.InvocationManager;
import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.data.PlaceConfig;
import com.threerings.crowd.data.PlaceObject;
import com.threerings.crowd.server.PlaceManager;
import com.threerings.parlor.card.data.Card;
import com.threerings.parlor.card.data.CardGameObject;
import com.threerings.parlor.card.data.Deck;
import com.threerings.parlor.card.data.Hand;
import com.threerings.parlor.card.data.PlayerCard;
import com.threerings.parlor.card.server.CardGameManager;
import com.threerings.parlor.card.trick.data.TrickCardGameMarshaller;
import com.threerings.parlor.card.trick.data.TrickCardGameObject;
import com.threerings.parlor.turn.server.TurnGameManagerDelegate;
import static com.threerings.parlor.card.Log.log;
/**
* A card game manager delegate for trick-based card games, such as Spades and Hearts.
*/
public class TrickCardGameManagerDelegate extends TurnGameManagerDelegate
implements TrickCardGameProvider
{
public TrickCardGameManagerDelegate ()
{
}
/**
* @deprecated use the zero-argument constructor.
*/
@Deprecated public TrickCardGameManagerDelegate (CardGameManager manager)
{
}
@Override
public void init (PlaceManager plmgr, RootDObjectManager omgr, InvocationManager invmgr)
{
super.init(plmgr, omgr, invmgr);
// Create these intervals HERE after the _omgr is actually initialized.
_turnTimeoutInterval = new Interval(_omgr) {
@Override
public void expired () {
_turnTimedOut = true;
turnTimedOut();
}
};
_endTrickInterval = new Interval(_omgr) {
@Override
public void expired () {
endTrick();
}
};
}
@Override
public void didInit (PlaceConfig config)
{
super.didInit(config);
_cgmgr = (CardGameManager)_plmgr;
}
@Override
public void didStartup (PlaceObject plobj)
{
super.didStartup(plobj);
_deck = new Deck();
_trickCardGame = (TrickCardGameObject)plobj;
_cardGame = (CardGameObject)plobj;
_trickCardGame.setTrickCardGameService(addProvider(this, TrickCardGameMarshaller.class));
}
@Override
public void gameWillStart ()
{
super.gameWillStart();
// clear out the last cards played
_trickCardGame.setLastCardsPlayed(null);
// initialize the turn duration scales
float[] scales = new float[_cardGame.getPlayerCount()];
Arrays.fill(scales, 1.0f);
_trickCardGame.setTurnDurationScales(scales);
}
/**
* Called when the game has started. Default implementation starts the first hand.
*/
@Override
public void gameDidStart ()
{
super.gameDidStart();
// start the first hand
startHand();
}
@Override
public void gameDidEnd ()
{
super.gameDidEnd();
// make sure all intervals are cancelled
_turnTimeoutInterval.cancel();
_endTrickInterval.cancel();
// make sure trick state is back to between hands
if (_trickCardGame.getTrickState() != TrickCardGameObject.BETWEEN_HANDS) {
_trickCardGame.setTrickState(TrickCardGameObject.BETWEEN_HANDS);
}
// initialize the array of rematch requests
_trickCardGame.setRematchRequests(new int[_cardGame.getPlayerCount()]);
}
@Override
public void startTurn ()
{
super.startTurn();
// initialize the timeout flag and schedule the timeout interval
_turnTimedOut = false;
_turnTimeoutInterval.schedule(_trickCardGame.getTurnDuration());
}
@Override
public void endTurn ()
{
// cancel the timeout interval
_turnTimeoutInterval.cancel();
// reduce or increase the turn duration scale
if (_turnTimedOut) {
reduceTurnDurationScale(_turnIdx);
} else {
increaseTurnDurationScale(_turnIdx);
}
super.endTurn();
}
/**
* Starts a hand of cards. Calls {@link #handWillStart}, sets the trick
* state to PLAYING_HAND, and calls {@link #handDidStart}.
*/
public void startHand ()
{
handWillStart();
_trickCardGame.setTrickState(TrickCardGameObject.PLAYING_HAND);
handDidStart();
}
/**
* Ends the hand of cards. Calls {@link #handWillEnd}, sets the trick
* state to BETWEEN_HANDS, and calls {@link #handDidEnd}.
*/
public void endHand ()
{
handWillEnd();
_trickCardGame.setTrickState(TrickCardGameObject.BETWEEN_HANDS);
handDidEnd();
}
/**
* Starts a trick. Calls {@link #trickWillStart}, sets the trick
* state to PLAYING_TRICK, and calls {@link #trickDidStart}.
*/
public void startTrick ()
{
trickWillStart();
_trickCardGame.setTrickState(TrickCardGameObject.PLAYING_TRICK);
trickDidStart();
}
/**
* Ends the trick. Calls {@link #trickWillEnd}, sets the trick
* state to PLAYING_HAND, and calls {@link #trickDidEnd}.
*/
public void endTrick ()
{
trickWillEnd();
_trickCardGame.setTrickState(TrickCardGameObject.PLAYING_HAND);
trickDidEnd();
}
// from interface TrickCardGameProvider
public void sendCardsToPlayer (ClientObject client, int toidx, Card[] cards)
{
// make sure they're actually a player
int fromidx = _cgmgr.getPlayerIndex(client);
if (fromidx == -1) {
log.warning("Send request from non-player",
"username", ((BodyObject)client).who(), "cards", cards);
return;
}
// make sure they have the cards
if (!_hands[fromidx].containsAll(cards)) {
log.warning("Tried to send cards not held",
"username", ((BodyObject)client).who(), "cards", cards);
return;
}
// send the cards
sendCardsToPlayer(fromidx, toidx, cards);
}
// from interface TrickCardGameProvider
public void playCard (ClientObject client, Card card, int handSize)
{
// make sure we're playing a trick
if (_trickCardGame.getTrickState() != TrickCardGameObject.PLAYING_TRICK) {
return; // silently ignore play attempts after timeouts
}
// make sure it's their turn
Name username = ((BodyObject)client).getVisibleName();
if (!username.equals(_trickCardGame.getTurnHolder())) {
return;
}
// make sure they're on the right trick
int pidx = _cardGame.getPlayerIndex(username);
if (_hands[pidx].size() != handSize) {
return;
}
// make sure their hand contains the specified card
if (!_hands[pidx].contains(card)) {
log.warning("Tried to play card not held", "username", username, "card", card);
return;
}
// make sure the card is legal to play
if (!_trickCardGame.isCardPlayable(_hands[pidx], card)) {
log.warning("Tried to play illegal card", "username", username, "card", card);
return;
}
// play the card
playCard(pidx, card);
}
// from interface TrickCardGameProvider
public void requestRematch (ClientObject client)
{
// make sure the game is over
if (_cardGame.state != CardGameObject.GAME_OVER) {
log.warning("Tried to request rematch when game wasn't over " +
"[username=" + ((BodyObject)client).who() + "].");
return;
}
// make sure the requester is one of the players
int pidx = _cgmgr.getPlayerIndex(client);
if (pidx == -1) {
log.warning("Rematch request from non-player", "username", ((BodyObject)client).who());
return;
}
// make sure the player hasn't already requested
if (_trickCardGame.getRematchRequests()[pidx] != TrickCardGameObject.NO_REQUEST) {
log.warning("Repeated rematch request", "username", ((BodyObject)client).who());
return;
}
// if player is first requesting, set to request; else set to accept
int req = (getRematchRequestCount() == 0 ?
TrickCardGameObject.REQUESTS_REMATCH :
TrickCardGameObject.ACCEPTS_REMATCH);
_trickCardGame.setRematchRequestsAt(req, pidx);
// if all players accept the rematch, restart the game
if (getRematchRequestCount() == _cardGame.getPlayerCount()) {
_cgmgr.rematchGame();
}
}
/**
* Sends cards between players without error checking. Default
* implementation transfers the cards between hands and notifies
* everyone of the transfer using {@link
* CardGameManager#transferCardsBetweenPlayers(int, int, Card[])}.
*/
protected void sendCardsToPlayer (int fromidx, int toidx, Card[] cards)
{
// remove from sending player's hand
_hands[fromidx].removeAll(cards);
// add to receiving player's hand
_hands[toidx].addAll(cards);
// notify everyone of the transfer
_cgmgr.transferCardsBetweenPlayers(fromidx, toidx, cards);
}
/**
* Plays a card for a player without error checking.
*/
protected void playCard (int pidx, Card card)
{
((DObject) _trickCardGame).startTransaction();
try {
// play the card by removing it from the hand and adding it
// to the end of the cards played array
_hands[pidx].remove(card);
PlayerCard[] cards = ArrayUtil.append(
_trickCardGame.getCardsPlayed(), new PlayerCard(pidx, card));
_trickCardGame.setCardsPlayed(cards);
// end the user's turn
endTurn();
// end the trick if everyone has played a card
if (_turnIdx == -1) {
if (_endTrickDelay == 0) {
endTrick();
} else {
_endTrickInterval.schedule(_endTrickDelay);
}
}
} finally {
((DObject) _trickCardGame).commitTransaction();
}
}
/**
* Returns the number of players currently requesting or accepting a rematch.
*/
protected int getRematchRequestCount ()
{
int[] rematchRequests = _trickCardGame.getRematchRequests();
int count = 0;
for (int rematchRequest : rematchRequests) {
if (rematchRequest != TrickCardGameObject.NO_REQUEST) {
count++;
}
}
return count;
}
/**
* Checks whether the trick is complete--that is, whether each player has played a card.
*/
protected boolean isTrickComplete ()
{
return _trickCardGame.getCardsPlayed().length == _cardGame.getPlayerCount();
}
@Override
protected void setFirstTurnHolder ()
{
if (_trickCardGame.getTrickState() == TrickCardGameObject.PLAYING_TRICK) {
super.setFirstTurnHolder();
} else {
_turnIdx = -1;
}
}
@Override
protected void setNextTurnHolder ()
{
if (_trickCardGame.getTrickState() == TrickCardGameObject.PLAYING_TRICK &&
!isTrickComplete()) {
super.setNextTurnHolder();
} else {
_turnIdx = -1;
}
}
/**
* Called when the current turn times out. Default implementation
* plays a random playable card if in the trick-playing state.
*/
protected void turnTimedOut ()
{
if (_trickCardGame.getTrickState() == TrickCardGameObject.PLAYING_TRICK) {
playCard(_turnIdx, pickRandomPlayableCard(_hands[_turnIdx]));
}
}
/**
* Reduces the specified player's turn duration due to a time-out.
*/
protected void reduceTurnDurationScale (int pidx)
{
float oldScale = _trickCardGame.getTurnDurationScales()[pidx],
newScale = Math.max(oldScale - TURN_DURATION_SCALE_REDUCTION,
MINIMUM_TURN_DURATION_SCALE);
if (newScale != oldScale) {
_trickCardGame.setTurnDurationScalesAt(newScale, pidx);
}
}
/**
* Increases the specified player's turn duration due to avoiding a time-out.
*/
protected void increaseTurnDurationScale (int pidx)
{
float oldScale = _trickCardGame.getTurnDurationScales()[pidx],
newScale = Math.min(oldScale + TURN_DURATION_SCALE_INCREASE, 1.0f);
if (newScale != oldScale) {
_trickCardGame.setTurnDurationScalesAt(newScale, pidx);
}
}
/**
* Returns a random playable card from the specified hand.
*/
protected Card pickRandomPlayableCard (Hand hand)
{
List<Card> playableCards = Lists.newArrayList();
for (int ii = 0; ii < hand.size(); ii++) {
Card card = hand.get(ii);
if (_trickCardGame.isCardPlayable(hand, card)) {
playableCards.add(card);
}
}
return RandomUtil.pickRandom(playableCards);
}
/**
* Notifies the object that a new hand is about to start.
*/
protected void handWillStart ()
{
}
/**
* Notifies the object that a new hand has just started. Default
* implementation prepares the deck, deals the hands, and starts the first trick.
*/
protected void handDidStart ()
{
// prepare the deck
prepareDeck();
// deal cards to players
dealHands();
// start the first trick
startTrick();
}
/**
* Prepares the deck for a new hand of cards. Default implementation
* resets to a full deck without jokers and shuffles.
*/
protected void prepareDeck ()
{
_deck.reset(false);
_deck.shuffle();
}
/**
* Deals hands to the players. Default implementation deals the entire
* deck to the players in equal-sized hands.
*/
protected void dealHands ()
{
_hands = _cgmgr.dealHands(_deck, _deck.size() / _cardGame.getPlayerCount());
}
/**
* Notifies the object that the hand is about to end.
*/
protected void handWillEnd ()
{}
/**
* Notifies the object that the hand has ended. Default implementation
* starts the next hand.
*/
protected void handDidEnd ()
{
startHand();
}
/**
* Notifies the object that a new trick is about to start. Default
* implementation resets the array of cards played.
*/
protected void trickWillStart ()
{
_trickCardGame.setCardsPlayed(new PlayerCard[0]);
}
/**
* Notifies the object that a new trick has started. Default
* implementation sets the first turn holder and starts the turn.
*/
protected void trickDidStart ()
{
setFirstTurnHolder();
startTurn();
}
/**
* Notifies the object that the trick is about to end.
*/
protected void trickWillEnd ()
{
}
/**
* Notifies the object that the trick has ended. Default implementation
* records the last cards played and starts the next trick, unless a
* player has run out of cards, in which case it ends the hand.
*/
protected void trickDidEnd ()
{
// store the trick results for late-joiners
_trickCardGame.setLastCardsPlayed(_trickCardGame.getCardsPlayed());
// clear out the cards played in the trick
_trickCardGame.setCardsPlayed(null);
// verify that each player has at least one card
if (anyHandsEmpty()) {
endHand();
return;
}
// everyone has cards; let's play another trick
startTrick();
}
/**
* Checks whether any hands are empty.
*/
protected boolean anyHandsEmpty ()
{
for (Hand _hand : _hands) {
if (_hand.isEmpty()) {
return true;
}
}
return false;
}
/** The all-purpose turn timeout interval. */
protected Interval _turnTimeoutInterval;
/** Calls {@link #endTrick} upon expiration. */
protected Interval _endTrickInterval;
/** The card game manager. */
protected CardGameManager _cgmgr;
/** The game object as trick card game. */
protected TrickCardGameObject _trickCardGame;
/** The game object as card game. */
protected CardGameObject _cardGame;
/** The amount of time to wait before ending the trick. */
protected long _endTrickDelay;
/** The deck from which cards are dealt. */
protected Deck _deck;
/** The hands of each player. */
protected Hand[] _hands;
/** Whether or not the turn timed out. */
protected boolean _turnTimedOut;
/** Reduce turn duration scales by this amount each time the player times out. */
protected static final float TURN_DURATION_SCALE_REDUCTION = 0.25f;
/** Turn duration scales increase by this amount each time the player doesn't time out. */
protected static final float TURN_DURATION_SCALE_INCREASE = 0.5f;
/** Don't let turn duration scales get below this level. */
protected static final float MINIMUM_TURN_DURATION_SCALE = 0.1f;
}
@@ -0,0 +1,52 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.card.trick.server;
import javax.annotation.Generated;
import com.threerings.parlor.card.data.Card;
import com.threerings.parlor.card.trick.client.TrickCardGameService;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.server.InvocationProvider;
/**
* Defines the server-side of the {@link TrickCardGameService}.
*/
@Generated(value={"com.threerings.presents.tools.GenServiceTask"},
comments="Derived from TrickCardGameService.java.")
public interface TrickCardGameProvider extends InvocationProvider
{
/**
* Handles a {@link TrickCardGameService#playCard} request.
*/
void playCard (ClientObject caller, Card arg1, int arg2);
/**
* Handles a {@link TrickCardGameService#requestRematch} request.
*/
void requestRematch (ClientObject caller);
/**
* Handles a {@link TrickCardGameService#sendCardsToPlayer} request.
*/
void sendCardsToPlayer (ClientObject caller, int arg1, Card[] arg2);
}
@@ -0,0 +1,188 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.card.trick.util;
import com.threerings.parlor.card.data.Card;
import com.threerings.parlor.card.data.Hand;
import com.threerings.parlor.card.data.PlayerCard;
import com.threerings.parlor.card.trick.data.TrickCardCodes;
/**
* Methods of general utility to trick-taking card games.
*/
public class TrickCardGameUtil
implements TrickCardCodes
{
/**
* For four-player games with fixed partnerships, this returns the index of the player's team.
*
* @param pidx the player index
*/
public static int getTeamIndex (int pidx)
{
return pidx & 1;
}
/**
* For four-player games with fixed partnerships, this returns the index of the other team.
*
* @param tidx the index of the team
*/
public static int getOtherTeamIndex (int tidx)
{
return tidx ^ 1;
}
/**
* For four-player games with fixed partnerships, this returns the index of the player's
* partner.
*/
public static int getPartnerIndex (int pidx)
{
return pidx ^ 2;
}
/**
* For four-player games with fixed partnerships, this returns the index of one of the members
* of a team.
*
* @param tidx the index of the team
* @param midx the index of the player within the team
*/
public static int getTeamMemberIndex (int tidx, int midx)
{
return (midx << 1) | tidx;
}
/**
* For four-player games, this returns the index of the player after the specified player
* going clockwise around the table.
*/
public static int getNextInClockwiseSequence (int pidx)
{
// 2
// 1 3
// 0
return (pidx + 1) & 3;
}
/**
* For four-player games with fixed partnerships, this returns the relative location of one
* player from the point of view of another.
*
* @param pidx1 the index of the player to whom the location is relative
* @param pidx2 the index of the player whose location is desired
* @return the relative location (TOP, BOTTOM, LEFT, or RIGHT)
*/
public static int getRelativeLocation (int pidx1, int pidx2)
{
return (pidx2 - pidx1) & 3;
}
/**
* For four-player games, returns the index of the player to the left of the specified player.
*/
public static int getLeftIndex (int pidx)
{
return (pidx + 1) & 3;
}
/**
* For four-player games, returns the index of the player to the right of the specified
* player.
*/
public static int getRightIndex (int pidx)
{
return (pidx + 3) & 3;
}
/**
* For four-player games, returns the index of the player across from the specified player.
*/
public static int getOppositeIndex (int pidx)
{
return pidx ^ 2;
}
/**
* Checks whether the player can follow the suit lead with the hand given.
*/
public static boolean canFollowSuit (PlayerCard[] cardsPlayed, Hand hand)
{
return hand.getSuitMemberCount(cardsPlayed[0].card.getSuit()) > 0;
}
/**
* Checks whether the specified array contains the given card.
*/
public static boolean containsCard (PlayerCard[] cards, Card card)
{
for (PlayerCard pc : cards) {
if (pc.card.equals(card)) {
return true;
}
}
return false;
}
/**
* Determines the number of cards that belong to the specified suit within the array given.
*/
public static int countSuitMembers (PlayerCard[] cards, int suit)
{
int count = 0;
for (PlayerCard pc : cards) {
if (pc.card.getSuit() == suit) {
count++;
}
}
return count;
}
/**
* Checks whether the proposed card follows the suit lead.
*/
public static boolean followsSuit (PlayerCard[] cardsPlayed, Card card)
{
return cardsPlayed[0].card.getSuit() == card.getSuit();
}
/**
* Returns the highest card (according to the standard A,K,...,2 ordering) in the suit lead,
* with an optional trump suit.
*
* @param trumpSuit the trump suit, or -1 for none
*/
public static PlayerCard getHighestInLeadSuit (PlayerCard[] cardsPlayed, int trumpSuit)
{
PlayerCard highest = cardsPlayed[0];
for (int ii = 1; ii < cardsPlayed.length; ii++) {
PlayerCard other = cardsPlayed[ii];
if ((other.card.getSuit() == highest.card.getSuit() &&
other.card.compareTo(highest.card) > 0) ||
(other.card.getSuit() == trumpSuit && highest.card.getSuit() != trumpSuit)) {
highest = other;
}
}
return highest;
}
}
@@ -0,0 +1,123 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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 javax.swing.JCheckBox;
import javax.swing.JLabel;
import com.samskivert.swing.SimpleSlider;
import com.threerings.parlor.game.client.SwingGameConfigurator;
/**
* Provides a default implementation of a TableConfigurator for
* a Swing interface.
*/
public class DefaultSwingTableConfigurator extends TableConfigurator
{
/**
* Create a TableConfigurator that allows only the specified number
* of players and lets the configuring user enable private games
* only if the number of players is greater than 2.
*/
public DefaultSwingTableConfigurator (int players)
{
this(players, (players > 2));
}
/**
* Create a TableConfigurator that allows only the specified number
* of players and lets the user configure a private table, or not.
*/
public DefaultSwingTableConfigurator (int players, boolean allowPrivate)
{
this(players, players, players, allowPrivate);
}
/**
* Create a TableConfigurator that allows for the specified configuration
* parameters.
*/
public DefaultSwingTableConfigurator (int minPlayers,
int desiredPlayers, int maxPlayers, boolean allowPrivate)
{
_config.minimumPlayerCount = minPlayers;
// create a slider for players, if applicable
if (minPlayers != maxPlayers) {
_playerSlider = new SimpleSlider(
"", minPlayers, maxPlayers, desiredPlayers);
} else {
_config.desiredPlayerCount = desiredPlayers;
}
// create up the checkbox for private games, if applicable
if (allowPrivate) {
_privateCheck = new JCheckBox();
}
}
@Override
protected void createConfigInterface ()
{
super.createConfigInterface();
SwingGameConfigurator gconf = (SwingGameConfigurator) _gameConfigurator;
if (_playerSlider != null) {
// TODO: proper translation
gconf.addControl(new JLabel("Players:"), _playerSlider);
}
if (_privateCheck != null) {
// TODO: proper translation
gconf.addControl(new JLabel("Private:"), _privateCheck);
}
}
@Override
public boolean isEmpty ()
{
return (_playerSlider == null) && (_privateCheck == null);
}
@Override
protected void flushTableConfig()
{
super.flushTableConfig();
if (_playerSlider != null) {
_config.desiredPlayerCount = _playerSlider.getValue();
}
if (_privateCheck != null) {
_config.privateTable = _privateCheck.isSelected();
}
}
/** A slider for configuring the number of players at the table. */
protected SimpleSlider _playerSlider;
/** A checkbox to allow the table creator to specify if the table is
* private. */
protected JCheckBox _privateCheck;
}
@@ -0,0 +1,43 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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;
/**
* Used to inform interested parties when the {@link ParlorDirector}
* receives a game ready notification. The observers can ratify the
* decision to head directly into the game or can take responsibility
* themselves for doing so.
*/
public interface GameReadyObserver
{
/**
* Called when a game ready notification is received.
*
* @param gameOid the place oid of the ready game.
*
* @return if the observer returns true from this method, the parlor
* director assumes they will take care of entering the game room
* after performing processing of their own. If all observers return
* false, the director will enter the game room automatically.
*/
public boolean receivedGameReady (int gameOid);
}
@@ -0,0 +1,208 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.util.Name;
import com.threerings.parlor.data.ParlorCodes;
import com.threerings.parlor.game.data.GameConfig;
import com.threerings.parlor.util.ParlorContext;
import static com.threerings.parlor.Log.log;
/**
* The invitation class is used to track information related to
* outstanding invitations generated by or targeted to this client.
*/
public class Invitation
implements ParlorCodes, ParlorService.InviteListener
{
/** The unique id for this invitation (as assigned by the
* server). This is -1 until we receive an acknowledgement from
* the server that our invitation was delivered. */
public int inviteId = -1;
/** The name of the other user involved in this invitation. */
public Name opponent;
/** The configuration of the game to be created. */
public GameConfig config;
/** Constructs a new invitation record. */
public Invitation (ParlorContext ctx, ParlorService pservice,
Name opponent, GameConfig config,
InvitationResponseObserver observer)
{
_ctx = ctx;
_pservice = pservice;
_observer = observer;
this.opponent = opponent;
this.config = config;
}
/**
* Accepts this invitation.
*/
public void accept ()
{
// generate the invocation service request
_pservice.respond(inviteId, INVITATION_ACCEPTED, null, this);
}
/**
* Refuses this invitation.
*
* @param message the message to deliver to the inviting user
* explaining the reason for the refusal or null if no message is to
* be provided.
*/
public void refuse (String message)
{
// generate the invocation service request
_pservice.respond(inviteId, INVITATION_REFUSED, message, this);
}
/**
* Cancels this invitation.
*/
public void cancel ()
{
// if the invitation has not yet been acknowleged by the
// server, we make a note that it should be cancelled when we
// do receive the acknowlegement
if (inviteId == -1) {
_cancelled = true;
} else {
// otherwise, generate the invocation service request
_pservice.cancel(inviteId, this);
// and remove it from the pending table
_ctx.getParlorDirector().clearInvitation(this);
}
}
/**
* Counters this invitation with an invitation with different game
* configuration parameters.
*
* @param config the updated game configuration.
* @param observer the entity that will be notified if this
* counter-invitation is accepted, refused or countered.
*/
public void counter (GameConfig config, InvitationResponseObserver observer)
{
// update our observer (who will eventually be hearing back from
// the other client about their counter-invitation)
_observer = observer;
// generate the invocation service request
_pservice.respond(inviteId, INVITATION_COUNTERED, config, this);
}
// documentation inherited from interface
public void inviteReceived (int inviteId)
{
// fill in our invitation id
this.inviteId = inviteId;
// if the invitation was cancelled before we heard back about
// it, we need to send off a cancellation request now
if (_cancelled) {
_pservice.cancel(inviteId, this);
} else {
// otherwise, put it in the pending invites table
_ctx.getParlorDirector().registerInvitation(this);
}
}
// documentation inherited from interface
public void requestFailed (String reason)
{
// let the observer know what's up
_observer.invitationRefused(this, reason);
}
/**
* Called by the parlor director when we receive a response to an
* invitation initiated by this client.
*/
protected void receivedResponse (int code, Object arg)
{
// make sure we have an observer to notify
if (_observer == null) {
log.warning("No observer registered for invitation " +
this + ".");
return;
}
// notify the observer
try {
switch (code) {
case INVITATION_ACCEPTED:
_observer.invitationAccepted(this);
break;
case INVITATION_REFUSED:
_observer.invitationRefused(this, (String)arg);
break;
case INVITATION_COUNTERED:
_observer.invitationCountered(this, (GameConfig)arg);
break;
}
} catch (Exception e) {
log.warning("Invitation response observer choked on response " +
"[code=" + code + ", arg=" + arg +
", invite=" + this + "].", e);
}
// unless the invitation was countered, we can remove it from the
// pending table because it's resolved
if (code != INVITATION_COUNTERED) {
_ctx.getParlorDirector().clearInvitation(this);
}
}
@Override
public String toString ()
{
return "[inviteId=" + inviteId + ", opponent=" + opponent +
", config=" + config + ", observer=" + _observer +
", cancelled=" + _cancelled + "]";
}
/** Provides access to client services. */
protected ParlorContext _ctx;
/** Provides access to parlor services. */
protected ParlorService _pservice;
/** The entity to notify when we receive a response for this
* invitation. */
protected InvitationResponseObserver _observer;
/** A flag indicating that we were requested to cancel this
* invitation before we even heard back with an acknowledgement
* that it was received by the server. */
protected boolean _cancelled = false;
}
@@ -0,0 +1,45 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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;
/**
* A client entity that wishes to handle invitations received by other
* clients should implement this interface and register itself with the
* parlor director. It will subsequently be notified of any incoming
* invitations. It is also responsible for handling cancelled invitations.
*/
public interface InvitationHandler
{
/**
* Called when an invitation is received from another player.
*
* @param invite the received invitation.
*/
public void invitationReceived (Invitation invite);
/**
* Called when an invitation is cancelled by the inviting player.
*
* @param invite the cancelled invitation.
*/
public void invitationCancelled (Invitation invite);
}
@@ -0,0 +1,61 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.parlor.game.data.GameConfig;
/**
* A client entity that wishes to generate invitations for games must
* implement this interface. An invitation can be accepted, refused or
* countered. A countered invitation is one where the game configuration
* is adjusted by the invited player and proposed back to the inviting
* player.
*/
public interface InvitationResponseObserver
{
/**
* Called if the invitation was accepted.
*
* @param invite the invitation for which we received a response.
*/
public void invitationAccepted (Invitation invite);
/**
* Called if the invitation was refused.
*
* @param invite the invitation for which we received a response.
* @param message a message provided by the invited user explaining
* the reason for their refusal, or the empty string if no message was
* provided.
*/
public void invitationRefused (Invitation invite, String message);
/**
* Called if the invitation was countered with an alternate game
* configuration.
*
* @param invite the invitation for which we received a response.
* @param config the game configuration proposed by the invited
* player.
*/
public void invitationCountered (Invitation invite, GameConfig config);
}
@@ -0,0 +1,100 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.parlor.game.data.GameConfig;
import com.threerings.presents.client.InvocationDecoder;
import com.threerings.util.Name;
/**
* Dispatches calls to a {@link ParlorReceiver} instance.
*/
public class ParlorDecoder extends InvocationDecoder
{
/** The generated hash code used to identify this receiver class. */
public static final String RECEIVER_CODE = "5ef9ee0d359c42a9024498ee9aad119a";
/** The method id used to dispatch {@link ParlorReceiver#gameIsReady}
* notifications. */
public static final int GAME_IS_READY = 1;
/** The method id used to dispatch {@link ParlorReceiver#receivedInvite}
* notifications. */
public static final int RECEIVED_INVITE = 2;
/** The method id used to dispatch {@link ParlorReceiver#receivedInviteCancellation}
* notifications. */
public static final int RECEIVED_INVITE_CANCELLATION = 3;
/** The method id used to dispatch {@link ParlorReceiver#receivedInviteResponse}
* notifications. */
public static final int RECEIVED_INVITE_RESPONSE = 4;
/**
* Creates a decoder that may be registered to dispatch invocation
* service notifications to the specified receiver.
*/
public ParlorDecoder (ParlorReceiver receiver)
{
this.receiver = receiver;
}
@Override
public String getReceiverCode ()
{
return RECEIVER_CODE;
}
@Override
public void dispatchNotification (int methodId, Object[] args)
{
switch (methodId) {
case GAME_IS_READY:
((ParlorReceiver)receiver).gameIsReady(
((Integer)args[0]).intValue()
);
return;
case RECEIVED_INVITE:
((ParlorReceiver)receiver).receivedInvite(
((Integer)args[0]).intValue(), (Name)args[1], (GameConfig)args[2]
);
return;
case RECEIVED_INVITE_CANCELLATION:
((ParlorReceiver)receiver).receivedInviteCancellation(
((Integer)args[0]).intValue()
);
return;
case RECEIVED_INVITE_RESPONSE:
((ParlorReceiver)receiver).receivedInviteResponse(
((Integer)args[0]).intValue(), ((Integer)args[1]).intValue(), args[2]
);
return;
default:
super.dispatchNotification(methodId, args);
return;
}
}
}
@@ -0,0 +1,240 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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 java.util.List;
import com.google.common.collect.Lists;
import com.samskivert.util.HashIntMap;
import com.threerings.util.Name;
import com.threerings.presents.client.BasicDirector;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.InvocationService;
import com.threerings.parlor.data.ParlorCodes;
import com.threerings.parlor.game.data.GameConfig;
import com.threerings.parlor.util.ParlorContext;
import static com.threerings.parlor.Log.log;
/**
* The parlor director manages the client side of the game configuration and matchmaking
* processes. It is also the entity that is listening for game start notifications which it then
* dispatches the client entity that will actually create and display the user interface for the
* game that started.
*/
public class ParlorDirector extends BasicDirector
implements ParlorCodes, ParlorReceiver
{
/**
* Constructs a parlor director and provides it with the parlor context that it can use to
* access the client services that it needs to provide its own services. Only one parlor
* director should be active in the client at one time and it should be made available via the
* parlor context.
*
* @param ctx the parlor context in use by the client.
*/
public ParlorDirector (ParlorContext ctx)
{
super(ctx);
_ctx = ctx;
// register ourselves with the invocation director as a parlor notification receiver
_ctx.getClient().getInvocationDirector().registerReceiver(new ParlorDecoder(this));
}
/**
* Sets the invitation handler, which is the entity that will be notified when we receive
* incoming invitation notifications and when invitations have been cancelled.
*
* @param handler our new invitation handler.
*/
public void setInvitationHandler (InvitationHandler handler)
{
_handler = handler;
}
/**
* Adds the specified observer to the list of entities that are notified when we receive a game
* ready notification.
*/
public void addGameReadyObserver (GameReadyObserver observer)
{
_grobs.add(observer);
}
/**
* Removes the specified observer from the list of entities that are notified when we receive a
* game ready notification.
*/
public void removeGameReadyObserver (GameReadyObserver observer)
{
_grobs.remove(observer);
}
/**
* Requests that the named user be invited to a game described by the supplied game config.
*
* @param invitee the user to invite.
* @param config the configuration of the game to which the user is being invited.
* @param observer the entity that will be notified if this invitation is accepted, refused or
* countered.
*
* @return an invitation object that can be used to manage the outstanding invitation.
*/
public Invitation invite (Name invitee, GameConfig config, InvitationResponseObserver observer)
{
// create the invitation record
Invitation invite = new Invitation(_ctx, _pservice, invitee, config, observer);
// submit the invitation request to the server
_pservice.invite(invitee, config, invite);
// and return the invitation to the caller
return invite;
}
/**
* Requests that the specified single player game be started.
*
* @param config the configuration of the single player game to be started.
* @param listener a listener to be informed of failure if the game cannot be started.
*/
public void startSolitaire (GameConfig config, InvocationService.ConfirmListener listener)
{
_pservice.startSolitaire(config, listener);
}
@Override
public void clientDidLogoff (Client client)
{
super.clientDidLogoff(client);
_pservice = null;
_pendingInvites.clear();
}
// documentation inherited from interface
public void gameIsReady (int gameOid)
{
log.info("Handling game ready [goid=" + gameOid + "].");
// see what our observers have to say about it
boolean handled = false;
for (int ii = 0; ii < _grobs.size(); ii++) {
GameReadyObserver grob = _grobs.get(ii);
handled = grob.receivedGameReady(gameOid) || handled;
}
// if none of the observers took matters into their own hands, then we'll head on over to
// the game room ourselves
if (!handled) {
_ctx.getLocationDirector().moveTo(gameOid);
}
}
// documentation inherited from interface
public void receivedInvite (int remoteId, Name inviter, GameConfig config)
{
// create an invitation record for this invitation
Invitation invite = new Invitation(_ctx, _pservice, inviter, config, null);
invite.inviteId = remoteId;
// put it in the pending invitations table
_pendingInvites.put(remoteId, invite);
try {
// notify the invitation handler of the incoming invitation
_handler.invitationReceived(invite);
} catch (Exception e) {
log.warning("Invitation handler choked on invite notification " + invite + ".", e);
}
}
// documentation inherited from interface
public void receivedInviteResponse (int remoteId, int code, Object arg)
{
// look up the invitation record for this invitation
Invitation invite = _pendingInvites.get(remoteId);
if (invite == null) {
log.warning("Have no record of invitation for which we received a response?! " +
"[remoteId=" + remoteId + ", code=" + code + ", arg=" + arg + "].");
} else {
invite.receivedResponse(code, arg);
}
}
// documentation inherited from interface
public void receivedInviteCancellation (int remoteId)
{
// TBD
}
@Override
protected void registerServices (Client client)
{
client.addServiceGroup(PARLOR_GROUP);
}
@Override
protected void fetchServices (Client client)
{
// get a handle on our parlor services
_pservice = client.requireService(ParlorService.class);
}
/**
* Register a new invitation in our pending invitations table. The invitation will call this
* when it knows its invitation id.
*/
protected void registerInvitation (Invitation invite)
{
_pendingInvites.put(invite.inviteId, invite);
}
/**
* Called by an invitation when it knows it is no longer and can be cleared from the pending
* invitations table.
*/
protected void clearInvitation (Invitation invite)
{
_pendingInvites.remove(invite.inviteId);
}
/** An active parlor context. */
protected ParlorContext _ctx;
/** Provides access to parlor server side services. */
protected ParlorService _pservice;
/** The entity that has registered itself to handle incoming invitation notifications. */
protected InvitationHandler _handler;
/** A table of acknowledged (but not yet accepted or refused) invitation requests, keyed on
* invitation id. */
protected HashIntMap<Invitation> _pendingInvites = new HashIntMap<Invitation>();
/** We notify the entities on this list when we get a game ready notification. */
protected List<GameReadyObserver> _grobs = Lists.newArrayList();
}
@@ -0,0 +1,84 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.util.Name;
import com.threerings.presents.client.InvocationReceiver;
import com.threerings.parlor.data.ParlorCodes;
import com.threerings.parlor.game.data.GameConfig;
/**
* Defines, for the parlor services, a set of notifications delivered
* asynchronously by the server to the client. These are handled by the
* {@link ParlorDirector}.
*/
public interface ParlorReceiver extends InvocationReceiver
{
/**
* Dispatched to the client when a game in which they are a
* participant is ready for play. The client will then enter the game
* room which will trigger the loading of the appropriate game UI code
* and generally get things started.
*
* @param gameOid the object id of the game object.
*/
public void gameIsReady (int gameOid);
/**
* Called by the invocation services when another user has invited us
* to play a game.
*
* @param remoteId the unique indentifier for this invitation (used
* when countering or responding).
* @param inviter the username of the inviting user.
* @param config the configuration information for the game to which
* we've been invited.
*/
public void receivedInvite (int remoteId, Name inviter, GameConfig config);
/**
* Called by the invocation services when another user has responded
* to our invitation by either accepting, refusing or countering it.
*
* @param remoteId the indentifier for the invitation on question.
* @param code the response code, either {@link
* ParlorCodes#INVITATION_ACCEPTED} or {@link
* ParlorCodes#INVITATION_REFUSED} or {@link
* ParlorCodes#INVITATION_COUNTERED}.
* @param arg in the case of a refused invitation, a string
* containing a message provided by the invited user explaining the
* reason for refusal (the empty string if no explanation was
* provided). In the case of a countered invitation, a new game config
* object with the modified game configuration.
*/
public void receivedInviteResponse (int remoteId, int code, Object arg);
/**
* Called by the invocation services when an outstanding invitation
* has been cancelled by the inviting user.
*
* @param remoteId the indentifier of the cancelled invitation.
*/
public void receivedInviteCancellation (int remoteId);
}
@@ -0,0 +1,92 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.util.Name;
import com.threerings.presents.client.InvocationService;
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}.
*/
public interface ParlorService extends InvocationService
{
/**
* Used to communicate responses to {@link ParlorService#invite} requests.
*/
public static interface InviteListener extends InvocationListener
{
/**
* Called in response to a successful {@link ParlorService#invite} request.
*/
public void inviteReceived (int inviteId);
}
/**
* 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 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 listener will receive and process the response.
*/
public void invite (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.
*
* @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 (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.
*
* @param inviteId the unique id previously assigned by the server to this invitation.
* @param listener will receive and process the response.
*/
public void cancel (int inviteId, InvocationListener listener);
/**
* Requests to start a single player game with the specified game configuration.
*/
public void startSolitaire (GameConfig config, ConfirmListener listener);
}
@@ -0,0 +1,38 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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;
/**
* 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 TableDirector}.
*/
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);
}
@@ -0,0 +1,118 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.parlor.data.TableConfig;
import com.threerings.parlor.game.client.GameConfigurator;
import com.threerings.parlor.util.ParlorContext;
/**
* This should be implemented some user-interface element that allows
* the user to configure whichever TableConfig options are relevant.
*/
public abstract class TableConfigurator
{
/**
* Create a TableConfigurator.
*/
public TableConfigurator ()
{
_config = createTableConfig();
}
/**
* Optionally, you can set a pre-configured TableConfig
* that will be used to initialize the display parameters (if possible).
* This should be called prior to init().
*/
public void setTableConfig (TableConfig config)
{
if (config != null) {
_config = config;
}
}
/**
* Initialize the TableConfigurator.
*
* At the time this is called, the GameConfigurator should have
* already been initialized.
*/
public void init (ParlorContext ctx, GameConfigurator gameConfigurator)
{
_ctx = ctx;
_gameConfigurator = gameConfigurator;
createConfigInterface();
}
/**
* Create the table config object that will be used.
*/
protected TableConfig createTableConfig ()
{
return new TableConfig();
}
/**
* Create the config interface.
*/
protected void createConfigInterface ()
{
// nothing by default
}
/**
* If true, the TableConfigurator is empty, which doesn't mean that
* it will not return a TableConfig object (for it must), but rather
* that there are no user-editable options being presented in the
* config interface.
*/
public abstract boolean isEmpty ();
/**
* Return the fully configured table config according to the currently
* configured user interface elements.
*/
public TableConfig getTableConfig ()
{
flushTableConfig();
return _config;
}
/**
* Derived classes will want to override this method, flushing
* values from the user interface to the table config object.
*/
protected void flushTableConfig ()
{
// nothing by default
}
/** Provides access to client services. */
protected ParlorContext _ctx;
/** The config we're configurating. */
protected TableConfig _config;
/** The game configurator, which we may wish to reference. */
protected GameConfigurator _gameConfigurator;
}
@@ -0,0 +1,373 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.google.common.base.Objects;
import com.samskivert.util.ObserverList;
import com.threerings.util.Name;
import com.threerings.presents.client.BasicDirector;
import com.threerings.presents.client.Client;
import com.threerings.presents.dobj.DObject;
import com.threerings.presents.dobj.EntryAddedEvent;
import com.threerings.presents.dobj.EntryRemovedEvent;
import com.threerings.presents.dobj.EntryUpdatedEvent;
import com.threerings.presents.dobj.SetListener;
import com.threerings.crowd.data.BodyObject;
import com.threerings.parlor.data.Table;
import com.threerings.parlor.data.TableConfig;
import com.threerings.parlor.data.TableLobbyObject;
import com.threerings.parlor.game.data.GameCodes;
import com.threerings.parlor.game.data.GameConfig;
import com.threerings.parlor.util.ParlorContext;
import static com.threerings.parlor.Log.log;
/**
* 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> 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<Table>, TableService.ResultListener
{
/**
* 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.
*/
public TableDirector (ParlorContext ctx, String tableField, TableObserver observer)
{
super(ctx);
// keep track of this stuff
_ctx = ctx;
_tableField = tableField;
_observer = observer;
}
/**
* This must be called by the entity that uses the table director when the using entity
* prepares to enter and display a place.
*/
public void setTableObject (DObject tlobj)
{
// the place should be a TableLobbyObject
_tlobj = (TableLobbyObject)tlobj;
// listen for table set changes
tlobj.addListener(this);
}
/**
* This must be called by the entity that uses the table director when the using entity has
* left and is done dealing with tables.
*/
public void clearTableObject ()
{
// remove our listenership and clear out
if (_tlobj != null) {
((DObject)_tlobj).removeListener(this);
_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.
*/
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);
}
/**
* Returns the current table this client is seated at, or null.
*/
public Table getSeatedTable ()
{
return _ourTable;
}
/**
* 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 + "].");
return;
}
// make sure we're currently in a place
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
_tlobj.getTableService().createTable(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.
*/
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 + "].");
return;
}
// make sure we're currently in a place
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
_tlobj.getTableService().joinTable(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 (_tlobj == null) {
log.warning("Requested to leave a table but we're not currently in a place " +
"[tableId=" + tableId + "].");
return;
}
// issue the leave request
_tlobj.getTableService().leaveTable(tableId, this);
}
/**
* 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 (_tlobj == null) {
log.warning("Requested to start a table but we're not currently in a place " +
"[tableId=" + tableId + "].");
return;
}
_tlobj.getTableService().startTableNow(tableId, this);
}
/**
* Sends a request to boot a player from a table.
*/
public void bootPlayer (int tableId, Name target)
{
if (_tlobj == null) {
log.warning("Requesting to boot a player from a table we're not currently in " +
"[tableId=" + tableId + ", target=" + target + "].");
return;
}
_tlobj.getTableService().bootPlayer(tableId, target, this);
}
@Override
public void clientDidLogoff (Client client)
{
super.clientDidLogoff(client);
_ourTable = null;
}
// documentation inherited
public void entryAdded (EntryAddedEvent<Table> event)
{
if (event.getName().equals(_tableField)) {
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);
}
}
// documentation inherited
public void entryUpdated (EntryUpdatedEvent<Table> event)
{
if (event.getName().equals(_tableField)) {
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);
}
}
// documentation inherited
public void entryRemoved (EntryRemovedEvent<Table> event)
{
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);
}
}
// from interface TableService.ResultListener
public void requestProcessed (Object result)
{
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;
}
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.players.length == 0) {
_ctx.getParlorDirector().gameIsReady(table.gameOid); // let's boogie!
}
}
// from interface TableService.ResultListener
public void requestFailed (String reason)
{
log.warning("Table action failed [reason=" + reason + "].");
_ctx.getChatDirector().displayFeedback(GameCodes.GAME_MESSAGE_BUNDLE, reason);
}
/**
* Checks to see if we're a member of this table and notes it as our
* table, if so.
*/
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
if (table.equals(_ourTable)) {
_ourTable = null;
}
// look for our username in the occupants array
BodyObject self = (BodyObject)_ctx.getClient().getClientObject();
if (table.containsPlayer(self.getVisibleName())) {
_ourTable = table;
}
// if nothing changed, bail now
if (Objects.equal(oldTable, _ourTable)) {
return;
}
// otherwise notify the observers
notifySeatedness(_ourTable != null);
}
/**
* Notifies the seatedness observers of a seatedness change.
*/
protected void notifySeatedness (final boolean isSeated)
{
_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;
/** Our TableLobbyObject. */
protected TableLobbyObject _tlobj;
/** The field name of the distributed set that contains our tables. */
protected String _tableField;
/** The entity that we talk to when table stuff happens. */
protected TableObserver _observer;
/** 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 ObserverList<SeatednessObserver> _seatedObservers = ObserverList.newFastUnsafe();
}
@@ -0,0 +1,48 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.parlor.data.Table;
/**
* The {@link TableDirector} converts distributed object events into
* higher level callbacks to implementers of this interface, which are
* expected to render these events sensically in a user interface.
*/
public interface TableObserver
{
/**
* Called when a new table is created.
*/
public void tableAdded (Table table);
/**
* Called when something has changed about a table (occupant list
* updated, state changed from matchmaking to in-play, etc.).
*/
public void tableUpdated (Table table);
/**
* Called when a table goes away.
*/
public void tableRemoved (int tableId);
}
@@ -0,0 +1,74 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.util.Name;
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 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 (TableConfig tableConfig, GameConfig config,
ResultListener listener);
/**
* Requests that the current user be added to the specified table at the specified position.
*
* @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 (int tableId, int position, InvocationListener listener);
/**
* Requests that the current user be removed from the specified 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 (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 (int tableId, InvocationListener listener);
/**
* Requests that another user be booted from the specified table.
*/
public void bootPlayer (int tableId, Name target, InvocationListener listener);
}
@@ -0,0 +1,57 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.data;
import com.threerings.util.ActionScript;
/**
* Models a parameter that allows the selection of one of a list of choices (specified as strings).
*/
public class ChoiceParameter extends Parameter
{
/** The set of choices available for this parameter. */
public String[] choices;
/** The starting selection. */
public String start;
/**
* Returns the translation key for the specified choice.
*/
@ActionScript(omit=true)
public String getChoiceLabel (int index)
{
return "m.choice_" + choices[index];
}
@Override @ActionScript(omit=true)
public String getLabel ()
{
return "m.choice_" + ident;
}
@Override
public Object getDefaultValue ()
{
return start;
}
}
@@ -0,0 +1,55 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.data;
import com.samskivert.util.StringUtil;
import com.threerings.io.Streamable;
/**
* Defines a configuration parameter for a game. Various derived classes exist that define
* particular types of configuration parameters including choices, toggles, ranges, etc.
*/
public abstract class Parameter implements Streamable
{
/** A string identifier that names this parameter. */
public String ident;
/** A human readable name for this configuration parameter. */
public String name;
/** A human readable tooltip to display when the mouse is hovered over this configuration
* parameter. */
public String tip;
/** Returns the translation key for this parameter's label. */
public abstract String getLabel ();
/** Returns the default value of this parameter. */
public abstract Object getDefaultValue ();
@Override
public String toString ()
{
return StringUtil.fieldsToString(this);
}
}
@@ -0,0 +1,77 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.data;
import com.threerings.presents.data.InvocationCodes;
/**
* Contains codes used by the parlor invocation services.
*/
public interface ParlorCodes extends InvocationCodes
{
/** Defines our invocation services group. */
public static final String PARLOR_GROUP = "parlor";
/** The response code for an accepted invitation. */
public static final int INVITATION_ACCEPTED = 0;
/** The response code for a refused invitation. */
public static final int INVITATION_REFUSED = 1;
/** The response code for a countered invitation. */
public static final int INVITATION_COUNTERED = 2;
/** An error code explaining that an invitation was rejected because the invited user was not
* online at the time the invitation was received. */
public static final String INVITEE_NOT_ONLINE = "m.invitee_not_online";
/** An error code returned by the table services. */
public static final String NO_SUCH_TABLE = "m.no_such_table";
/** An error code returned by the table services. */
public static final String INVALID_TABLE_POSITION = "m.invalid_table_position";
/** An error code returned by the table services. */
public static final String MUST_BE_CREATOR = "m.must_be_creator";
/** An error code returned by the table services. */
public static final String NO_SELF_BOOT = "m.no_self_boot";
/** An error code returned by the table services. */
public static final String TABLE_POSITION_OCCUPIED = "m.table_position_occupied";
/** An error code returned by the table services when a user requests to create or join a table
* but they're already sitting at another table. */
public static final String ALREADY_AT_TABLE = "m.already_at_table";
/** An error code returned by the table services 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";
/** An error code returned by the table services for a request to join a table that the
* requester been banned from. */
public static final String BANNED_FROM_TABLE = "m.banned_from_table";
/** An error code returned by the table services when a user requests to leave a table for
* which the game is already in progress. */
public static final String GAME_ALREADY_STARTED = "m.game_already_started";
}
@@ -0,0 +1,127 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.data;
import javax.annotation.Generated;
import com.threerings.parlor.client.ParlorService;
import com.threerings.parlor.game.data.GameConfig;
import com.threerings.presents.client.InvocationService;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.util.Name;
/**
* Provides the implementation of the {@link ParlorService} interface
* that marshalls the arguments and delivers the request to the provider
* on the server. Also provides an implementation of the response listener
* interfaces that marshall the response arguments and deliver them back
* to the requesting client.
*/
@Generated(value={"com.threerings.presents.tools.GenServiceTask"},
comments="Derived from ParlorService.java.")
public class ParlorMarshaller extends InvocationMarshaller
implements ParlorService
{
/**
* Marshalls results to implementations of {@code ParlorService.InviteListener}.
*/
public static class InviteMarshaller extends ListenerMarshaller
implements InviteListener
{
/** The method id used to dispatch {@link #inviteReceived}
* responses. */
public static final int INVITE_RECEIVED = 1;
// from interface InviteMarshaller
public void inviteReceived (int arg1)
{
sendResponse(INVITE_RECEIVED, new Object[] { Integer.valueOf(arg1) });
}
@Override // from InvocationMarshaller
public void dispatchResponse (int methodId, Object[] args)
{
switch (methodId) {
case INVITE_RECEIVED:
((InviteListener)listener).inviteReceived(
((Integer)args[0]).intValue());
return;
default:
super.dispatchResponse(methodId, args);
return;
}
}
}
/** The method id used to dispatch {@link #cancel} requests. */
public static final int CANCEL = 1;
// from interface ParlorService
public void cancel (int arg1, InvocationService.InvocationListener arg2)
{
ListenerMarshaller listener2 = new ListenerMarshaller();
listener2.listener = arg2;
sendRequest(CANCEL, new Object[] {
Integer.valueOf(arg1), listener2
});
}
/** The method id used to dispatch {@link #invite} requests. */
public static final int INVITE = 2;
// from interface ParlorService
public void invite (Name arg1, GameConfig arg2, ParlorService.InviteListener arg3)
{
ParlorMarshaller.InviteMarshaller listener3 = new ParlorMarshaller.InviteMarshaller();
listener3.listener = arg3;
sendRequest(INVITE, new Object[] {
arg1, arg2, listener3
});
}
/** The method id used to dispatch {@link #respond} requests. */
public static final int RESPOND = 3;
// from interface ParlorService
public void respond (int arg1, int arg2, Object arg3, InvocationService.InvocationListener arg4)
{
ListenerMarshaller listener4 = new ListenerMarshaller();
listener4.listener = arg4;
sendRequest(RESPOND, new Object[] {
Integer.valueOf(arg1), Integer.valueOf(arg2), arg3, listener4
});
}
/** The method id used to dispatch {@link #startSolitaire} requests. */
public static final int START_SOLITAIRE = 4;
// from interface ParlorService
public void startSolitaire (GameConfig arg1, InvocationService.ConfirmListener arg2)
{
InvocationMarshaller.ConfirmMarshaller listener2 = new InvocationMarshaller.ConfirmMarshaller();
listener2.listener = arg2;
sendRequest(START_SOLITAIRE, new Object[] {
arg1, listener2
});
}
}
@@ -0,0 +1,51 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.data;
import com.threerings.util.ActionScript;
/**
* Models a parameter that can contain an integer value in a specified range.
*/
public class RangeParameter extends Parameter
{
/** The minimum value of this parameter. */
public int minimum;
/** The maximum value of this parameter. */
public int maximum;
/** The starting value for this parameter. */
public int start;
@Override @ActionScript(omit=true)
public String getLabel ()
{
return "m.range_" + ident;
}
@Override
public Object getDefaultValue ()
{
return start;
}
}
@@ -0,0 +1,470 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.data;
import java.util.HashSet;
import java.util.List;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.samskivert.util.ArrayIntSet;
import com.samskivert.util.ListUtil;
import com.samskivert.util.StringUtil;
import com.threerings.util.ActionScript;
import com.threerings.util.Name;
import com.threerings.presents.dobj.DSet;
import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.data.OccupantInfo;
import com.threerings.parlor.game.data.GameConfig;
import com.threerings.parlor.game.data.GameObject;
/**
* This class represents a table that is being used to matchmake a game by the Parlor services.
*/
public class Table
implements DSet.Entry, ParlorCodes
{
/** Used to request any position at a table. */
public static final int ANY_POSITION = -1;
/** The unique identifier for this table. */
public int 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 = -1;
/** An array of the usernames of the players of this table (some slots may not be filled), or
* null if a party game. */
public Name[] players;
/** An array of the usernames of the non-player occupants of this game. For FFA party games
* this is all of a room's occupants and they are in fact players. */
public Name[] watchers = createPlayerNamesArray(0);
/** The body oids of the players of this table, or null if a party game. (This is not
* propagated to remote instances.) */
public transient int[] bodyOids;
/** The game config for the game that is being matchmade. */
public GameConfig config;
/** The table configuration object. */
public TableConfig tconfig;
/**
* Constructs a blank table instance, suitable for unserialization.
*/
public Table ()
{
}
/**
* Initializes a new table instance, and assigns it the next monotonically increasing table id.
*
* @param lobbyOid the object id of the lobby in which this table is to live.
* @param tconfig the table configuration for this table.
* @param config the configuration of the game being matchmade by this table.
*/
@ActionScript(omit=true)
public void init (int lobbyOid, TableConfig tconfig, GameConfig config)
{
// assign a unique table id
tableId = ++_tableIdCounter;
// keep track of our lobby oid
this.lobbyOid = lobbyOid;
// keep a casted reference around
this.tconfig = tconfig;
this.config = config;
// make room for the maximum number of players
if (config.getMatchType() != GameConfig.PARTY) {
players = createPlayerNamesArray(tconfig.desiredPlayerCount);
bodyOids = new int[players.length];
// fill in information on the AIs
int acount = (config.ais == null) ? 0 : config.ais.length;
for (int ii = 0; ii < acount; ii++) {
// TODO: handle this naming business better
players[ii] = new Name("AI " + (ii+1));
}
} else {
players = createPlayerNamesArray(0);
bodyOids = new int[0];
}
}
/**
* Returns true if there is no one sitting at this table.
*/
@ActionScript(omit=true)
public boolean isEmpty ()
{
for (int bodyOid : bodyOids) {
if (bodyOid != 0) {
return false;
}
}
return true;
}
/**
* Count the number of players currently occupying this table.
*/
public int getOccupiedCount ()
{
int count = 0;
if (players != null) {
for (Name player : players) {
if (player != null) {
count++;
}
}
}
return count;
}
/**
* Once a table is ready to play (see {@link #mayBeStarted} and {@link #shouldBeStarted}), 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 Name[] getPlayers ()
{
// seated party games need a spot for every seat
if (GameConfig.SEATED_CONTINUOUS == config.getMatchType()) {
return createPlayerNamesArray(tconfig.desiredPlayerCount);
}
// FFA party games have 0-length players array, and non-party games will have the players
// who are ready-to-go for the game start.
Name[] plist = createPlayerNamesArray(getOccupiedCount());
if (players != null) {
for (int ii = 0, dex = 0; ii < players.length; ii++) {
if (players[ii] != null) {
plist[dex++] = players[ii];
}
}
}
return plist;
}
/**
* For a team game, get the team member indices of the compressed players array returned by
* {@link #getPlayers}.
*/
public int[][] getTeamMemberIndices ()
{
int[][] teams = tconfig.teamMemberIndices;
if (teams == null) {
return null;
}
// compress the team indexes down
ArrayIntSet set = new ArrayIntSet();
int[][] newTeams = new int[teams.length][];
Name[] plist = getPlayers();
for (int ii=0; ii < teams.length; ii++) {
set.clear();
for (int jj=0; jj < teams[ii].length; jj++) {
Name occ = players[teams[ii][jj]];
if (occ != null) {
set.add(ListUtil.indexOf(plist, occ));
}
}
newTeams[ii] = set.toIntArray();
}
return newTeams;
}
/**
* Requests to seat the specified user at the specified position in this table.
*
* @param position the position in which to seat the user.
* @param player the player to set.
*
* @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.
*/
@ActionScript(omit=true)
public String setPlayer (int position, BodyObject player)
{
// check whether this player has been banned
if (_bannedUsers != null && _bannedUsers.contains(player.getVisibleName())) {
return BANNED_FROM_TABLE;
}
// if they just want any position, see if we have one available
if (position == ANY_POSITION) {
for (int ii = 0; ii < tconfig.desiredPlayerCount; ii++) {
if (players[ii] == null) {
setPlayerPos(ii, player);
return null;
}
}
return TABLE_POSITION_OCCUPIED;
}
// make sure the requested position is a valid one
if (position >= tconfig.desiredPlayerCount || position < 0) {
return INVALID_TABLE_POSITION;
}
// make sure the requested position is not already occupied
if (players[position] != null) {
return TABLE_POSITION_OCCUPIED;
}
// otherwise all is well, stick 'em in
setPlayerPos(position, player);
return null;
}
/**
* This method is used for party games, it does no bounds checking or verification of the
* player's ability to join, if you are unsure you should call 'setPlayer'.
*/
@ActionScript(omit=true)
public void setPlayerPos (int position, BodyObject player)
{
players[position] = player.getVisibleName();
bodyOids[position] = player.getOid();
}
/**
* Indicate to this table that a user was booted and should
* be prevented from rejoining.
*/
public void addBannedUser (Name player)
{
if (_bannedUsers == null) {
_bannedUsers = Sets.newHashSet();
}
_bannedUsers.add(player);
}
/**
* 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.
*/
@ActionScript(omit=true)
public boolean clearPlayer (Name username)
{
if (players != null) {
for (int ii = 0; ii < players.length; ii++) {
if (username.equals(players[ii])) {
clearPlayerPos(ii);
return true;
}
}
}
return false;
}
/**
* Requests that the user identified by the specified body object id 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.
*/
@ActionScript(omit=true)
public boolean clearPlayerByOid (int bodyOid)
{
if (bodyOids != null) {
for (int ii = 0; ii < bodyOids.length; ii++) {
if (bodyOid == bodyOids[ii]) {
clearPlayerPos(ii);
return true;
}
}
}
return false;
}
/**
* Called to clear a player at the specified position. Only call this method if you know what
* you're doing.
*/
@ActionScript(omit=true)
public void clearPlayerPos (int position)
{
players[position] = null;
bodyOids[position] = 0;
}
/**
* Returns true if this table contains the specified player.
*/
@ActionScript(omit=true)
public boolean containsPlayer (Name player)
{
return (players != null && ListUtil.indexOf(players, player) != -1);
}
/**
* Called by the table manager when the game object's players have changed. Regenerates the
* {@link #watchers} array.
*/
@ActionScript(omit=true)
public void updateOccupants (GameObject gameobj)
{
List<Name> wlist = Lists.newArrayList();
for (OccupantInfo info : gameobj.occupantInfo) {
if (containsPlayer(info.username)) { // skip players
continue;
}
wlist.add(info.username);
}
watchers = wlist.toArray(createPlayerNamesArray(wlist.size()));
}
/**
* Returns true if this table has a sufficient number of players that the game can be
* started.
*/
public boolean mayBeStarted ()
{
switch (config.getMatchType()) {
case GameConfig.SEATED_CONTINUOUS:
case GameConfig.PARTY:
return true;
}
if (tconfig.teamMemberIndices == null) {
// for a normal game, just check to see if we're past the minimum
return tconfig.minimumPlayerCount <= getOccupiedCount();
} else {
// for a team game, make sure each team has the minimum players
int[][] teams = tconfig.teamMemberIndices;
for (int[] team : teams) {
int teamCount = 0;
for (int element : team) {
if (players[element] != null) {
teamCount++;
}
}
if (teamCount < tconfig.minimumPlayerCount) {
return false;
}
}
return true;
}
}
/**
* Returns true if sufficient seats are occupied that the game should be automatically started.
*/
public boolean shouldBeStarted ()
{
switch (config.getMatchType()) {
case GameConfig.SEATED_CONTINUOUS:
case GameConfig.PARTY:
return true;
default:
return (tconfig.desiredPlayerCount <= getOccupiedCount());
}
}
/**
* Returns true if this table is in play, false if it is still being matchmade.
*/
public boolean inPlay ()
{
return gameOid != -1;
}
// documentation inherited
public Comparable<?> getKey ()
{
return tableId;
}
@Override
public boolean equals (Object other)
{
return (other instanceof Table) && (tableId == ((Table) other).tableId);
}
@Override
public int hashCode ()
{
return tableId;
}
@Override
public String toString ()
{
StringBuilder buf = new StringBuilder();
buf.append(StringUtil.shortClassName(this));
buf.append(" [");
toString(buf);
buf.append("]");
return buf.toString();
}
/**
* Helper method for toString, ripe for overrideability.
*/
@ActionScript(name="toStringBuilder")
protected void toString (StringBuilder buf)
{
buf.append("tableId=").append(tableId);
buf.append(", lobbyOid=").append(lobbyOid);
buf.append(", gameOid=").append(gameOid);
buf.append(", players=").append(StringUtil.toString(players));
buf.append(", config=").append(config);
}
/**
* Creates a names array of the appropriate type.
*/
protected Name[] createPlayerNamesArray (int length)
{
return new Name[length];
}
/** A counter for assigning table ids. */
protected static int _tableIdCounter = 0;
/** On the server, the usernames that have been banned from this table. */
protected transient HashSet<Name> _bannedUsers;
}
@@ -0,0 +1,46 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.data;
import com.threerings.io.SimpleStreamableObject;
/**
* Table configuration parameters for a game that is to be matchmade using the table services.
*/
public class TableConfig extends SimpleStreamableObject
{
/** The total number of players that are desired for the table. For team games, this should be
* set to the total number of players overall, as teams may be unequal. */
public int desiredPlayerCount;
/** The minimum number of players needed overall (or per-team if a team-based game) for the
* game to start at the creator's discretion. */
public int minimumPlayerCount;
/** If non-null, indicates that this is a team-based game and contains the team assignments for
* each player. For example, a game with three players in two teams- players 0 and 2 versus
* player 1- would have { {0, 2}, {1} }; */
public int[][] teamMemberIndices;
/** Whether the table is "private". */
public boolean privateTable;
}
@@ -0,0 +1,67 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.data;
import com.threerings.presents.dobj.DSet;
/**
* This interface must be implemented by the place object used by a lobby
* that wishes to make use of the table services.
*/
public interface TableLobbyObject
{
/**
* Returns a reference to the distributed set instance that will be
* holding the tables.
*/
DSet<Table> getTables ();
/**
* Adds the supplied table instance to the tables set (using the
* appropriate distributed object mechanisms).
*/
void addToTables (Table table);
/**
* Updates the value of the specified table instance in the tables
* distributed set (using the appropriate distributed object
* mechanisms).
*/
void updateTables (Table table);
/**
* Removes the table instance that matches the specified key from the
* tables set (using the appropriate distributed object mechanisms).
*/
void removeFromTables (Comparable<?> key);
/**
* Returns a reference to the table service configured in this object.
*/
TableMarshaller getTableService ();
/**
* Configures the table service that clients should use to communicate table requests back to
* the table manager handling these tables.
*/
void setTableService (TableMarshaller service);
}
@@ -0,0 +1,108 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.data;
import javax.annotation.Generated;
import com.threerings.parlor.client.TableService;
import com.threerings.parlor.game.data.GameConfig;
import com.threerings.presents.client.InvocationService;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.util.Name;
/**
* Provides the implementation of the {@link TableService} interface
* that marshalls the arguments and delivers the request to the provider
* on the server. Also provides an implementation of the response listener
* interfaces that marshall the response arguments and deliver them back
* to the requesting client.
*/
@Generated(value={"com.threerings.presents.tools.GenServiceTask"},
comments="Derived from TableService.java.")
public class TableMarshaller extends InvocationMarshaller
implements TableService
{
/** The method id used to dispatch {@link #bootPlayer} requests. */
public static final int BOOT_PLAYER = 1;
// from interface TableService
public void bootPlayer (int arg1, Name arg2, InvocationService.InvocationListener arg3)
{
ListenerMarshaller listener3 = new ListenerMarshaller();
listener3.listener = arg3;
sendRequest(BOOT_PLAYER, new Object[] {
Integer.valueOf(arg1), arg2, listener3
});
}
/** The method id used to dispatch {@link #createTable} requests. */
public static final int CREATE_TABLE = 2;
// from interface TableService
public void createTable (TableConfig arg1, GameConfig arg2, InvocationService.ResultListener arg3)
{
InvocationMarshaller.ResultMarshaller listener3 = new InvocationMarshaller.ResultMarshaller();
listener3.listener = arg3;
sendRequest(CREATE_TABLE, new Object[] {
arg1, arg2, listener3
});
}
/** The method id used to dispatch {@link #joinTable} requests. */
public static final int JOIN_TABLE = 3;
// from interface TableService
public void joinTable (int arg1, int arg2, InvocationService.InvocationListener arg3)
{
ListenerMarshaller listener3 = new ListenerMarshaller();
listener3.listener = arg3;
sendRequest(JOIN_TABLE, new Object[] {
Integer.valueOf(arg1), Integer.valueOf(arg2), listener3
});
}
/** The method id used to dispatch {@link #leaveTable} requests. */
public static final int LEAVE_TABLE = 4;
// from interface TableService
public void leaveTable (int arg1, InvocationService.InvocationListener arg2)
{
ListenerMarshaller listener2 = new ListenerMarshaller();
listener2.listener = arg2;
sendRequest(LEAVE_TABLE, new Object[] {
Integer.valueOf(arg1), listener2
});
}
/** The method id used to dispatch {@link #startTableNow} requests. */
public static final int START_TABLE_NOW = 5;
// from interface TableService
public void startTableNow (int arg1, InvocationService.InvocationListener arg2)
{
ListenerMarshaller listener2 = new ListenerMarshaller();
listener2.listener = arg2;
sendRequest(START_TABLE_NOW, new Object[] {
Integer.valueOf(arg1), listener2
});
}
}
@@ -0,0 +1,45 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.data;
import com.threerings.util.ActionScript;
/**
* Models a parameter that allows the toggling of a single value.
*/
public class ToggleParameter extends Parameter
{
/** The starting state for this parameter. */
public boolean start;
@Override @ActionScript(omit=true)
public String getLabel ()
{
return "m.toggle_" + ident;
}
@Override
public Object getDefaultValue ()
{
return start;
}
}
@@ -0,0 +1,118 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.game.client;
import com.threerings.parlor.game.data.GameConfig;
import com.threerings.parlor.util.ParlorContext;
import static com.threerings.parlor.Log.log;
/**
* Provides the base from which interfaces can be built to configure games prior to starting
* them. Derived classes would extend the base configurator adding interface elements and wiring
* them up properly to allow the user to configure an instance of their game.
*
* <p> Clients that use the game configurator will want to instantiate one based on the class
* returned from the {@link GameConfig} and then initialize it with a call to {@link #init}.
*/
public abstract class GameConfigurator
{
/**
* Initializes this game configurator, creates its user interface elements and prepares it for
* display.
*/
public void init (ParlorContext ctx)
{
// save this for later
_ctx = ctx;
// create our interface elements
createConfigInterface();
}
/**
* The default implementation creates nothing.
*/
protected void createConfigInterface ()
{
}
/**
* Provides this configurator with its configuration. It should set up all of its user
* interface elements to reflect the configuration.
*/
public void setGameConfig (GameConfig config)
{
_config = config;
// set up the user interface
try {
gotGameConfig();
} catch (RuntimeException re) {
// This config is booched in a way that is unfathomable to our simple GameConfigurator
// ways, but GameConfigs are SimpleStreamables and should be valid when created with
// their default constructors. Try creating one of those to keep things rolling.
log.warning("Unable to fill in interface from config, trying to use a default config",
re);
try {
_config = config.getClass().newInstance();
} catch (Exception e) {
log.warning("Unable to create replacement game config", "class", e.getClass(), e);
throw re;// Rethrow the original exception since it's a little more relevant
}
// We've got a default config, see how that does
gotGameConfig();
}
}
/**
* Derived classes will likely want to override this method and configure their user interface
* elements accordingly.
*/
protected void gotGameConfig ()
{
}
/**
* Obtains a configured game configuration.
*/
public GameConfig getGameConfig ()
{
// flush our changes to the config object
flushGameConfig();
return _config;
}
/**
* Derived classes will want to override this method, flushing values from the user interface
* to the game config object so that it is properly configured prior to being returned to the
* {@link #getGameConfig} caller.
*/
protected void flushGameConfig ()
{
}
/** Provides access to client services. */
protected ParlorContext _ctx;
/** Our game configuration. */
protected GameConfig _config;
}
@@ -0,0 +1,318 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.game.client;
import java.awt.event.ActionEvent;
import com.threerings.presents.dobj.AttributeChangeListener;
import com.threerings.presents.dobj.AttributeChangedEvent;
import com.threerings.crowd.client.PlaceController;
import com.threerings.crowd.client.PlaceControllerDelegate;
import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.data.PlaceConfig;
import com.threerings.crowd.data.PlaceObject;
import com.threerings.crowd.util.CrowdContext;
import com.threerings.parlor.game.data.GameCodes;
import com.threerings.parlor.game.data.GameConfig;
import com.threerings.parlor.game.data.GameObject;
import com.threerings.parlor.util.ParlorContext;
import static com.threerings.parlor.Log.log;
/**
* The game controller manages the flow and control of a game on the client side. This class serves
* as the root of a hierarchy of controller classes that aim to provide functionality shared
* between various similar games. The base controller provides functionality for starting and
* ending the game and for calculating ratings adjustments when a game ends normally. It also
* handles the basic house keeping like subscription to the game object and dispatch of commands
* and distributed object events.
*/
public abstract class GameController extends PlaceController
implements AttributeChangeListener
{
/**
* Initializes this game controller with the game configuration that was established during the
* match making process. Derived classes may want to override this method to initialize
* themselves with game-specific configuration parameters but they should be sure to call
* <code>super.init</code> in such cases.
*
* @param ctx the client context.
* @param config the configuration of the game we are intended to
* control.
*/
@Override
public void init (CrowdContext ctx, PlaceConfig config)
{
// cast our references before we call super.init() so that when super.init() calls
// createPlaceView(), we have our casted references already in place
_ctx = (ParlorContext)ctx;
_config = (GameConfig)config;
super.init(ctx, config);
}
/**
* Adds this controller as a listener to the game object (thus derived classes need not do so)
* and lets the game manager know that we are now ready to go.
*/
@Override
public void willEnterPlace (PlaceObject plobj)
{
super.willEnterPlace(plobj);
// obtain a casted reference
_gobj = (GameObject)plobj;
// if this place object is not our current location we'll need to add it as an auxiliary
// chat source
BodyObject bobj = (BodyObject)_ctx.getClient().getClientObject();
if (bobj.location == null || bobj.location.placeOid != plobj.getOid()) {
_ctx.getChatDirector().addAuxiliarySource(_gobj, GameCodes.GAME_CHAT_TYPE);
}
// and add ourselves as a listener
_gobj.addListener(this);
// we don't want to claim to be finished until any derived classes that overrode this
// method have executed, so we'll queue up a runnable here that will let the game manager
// know that we're ready on the next pass through the distributed event loop
log.info("Entering game " + _gobj.which() + ".");
if (_gobj.getPlayerIndex(bobj.getVisibleName()) != -1) {
_ctx.getClient().getRunQueue().postRunnable(new Runnable() {
public void run () {
// finally let the game manager know that we're ready to roll
playerReady();
}
});
}
}
/**
* Removes our listener registration from the game object and cleans house.
*/
@Override
public void didLeavePlace (PlaceObject plobj)
{
super.didLeavePlace(plobj);
_ctx.getChatDirector().removeAuxiliarySource(_gobj);
// unlisten to the game object
_gobj.removeListener(this);
_gobj = null;
}
/**
* Convenience method to determine the type of game.
*/
public int getMatchType ()
{
return _config.getMatchType();
}
/**
* Returns whether the game is over.
*/
public boolean isGameOver ()
{
boolean gameOver = (_gobj == null) || (_gobj.state != GameObject.IN_PLAY);
return (_gameOver || gameOver);
}
/**
* Sets the client game over override. This is used in situations where we determine that the
* game is over before the server has informed us of such.
*/
public void setGameOver (boolean gameOver)
{
_gameOver = gameOver;
}
/**
* Calls {@link #gameWillReset}, ends the current game (locally, it does not tell the server to
* end the game), and waits to receive a reset notification (which is simply an event setting
* the game state to <code>IN_PLAY</code> even though it's already set to <code>IN_PLAY</code>)
* from the server which will start up a new game. Derived classes should override {@link
* #gameWillReset} to perform any game-specific animations.
*/
public void resetGame ()
{
// let derived classes do their thing
gameWillReset();
// end the game until we receive a new board
setGameOver(true);
}
/**
* Returns the unique session identifier for the current gameplay session.
*/
public int getSessionId ()
{
return (_gobj == null) ? -1 : _gobj.sessionId;
}
/**
* Handles basic game controller action events. Derived classes should be sure to call
* <code>super.handleAction</code> for events they don't specifically handle.
*/
@Override
public boolean handleAction (ActionEvent action)
{
return super.handleAction(action);
}
/**
* A way for controllers to display a game-related system message.
*/
public void systemMessage (String bundle, String msg)
{
_ctx.getChatDirector().displayInfo(bundle, msg, GameCodes.GAME_CHAT_TYPE);
}
// documentation inherited
public void attributeChanged (AttributeChangedEvent event)
{
// deal with game state changes
if (event.getName().equals(GameObject.STATE)) {
int newState = event.getIntValue();
if (!stateDidChange(newState)) {
log.warning("Game transitioned to unknown state", "gobj", _gobj, "state", newState);
}
}
}
/**
* Derived classes can override this method if they add additional game states and should
* handle transitions to those states, returning true to indicate they were handled and calling
* super for the normal game states.
*/
protected boolean stateDidChange (int state)
{
switch (state) {
case GameObject.PRE_GAME:
return true;
case GameObject.IN_PLAY:
gameDidStart();
return true;
case GameObject.GAME_OVER:
gameDidEnd();
return true;
case GameObject.CANCELLED:
gameWasCancelled();
return true;
}
return false;
}
/**
* Called after we've entered the game and everything has initialized to notify the server that
* we, as a player, are ready to play.
*/
protected void playerReady ()
{
log.info("Reporting ready " + _gobj.which() + ".");
_gobj.manager.invoke("playerReady");
}
/**
* Called when the game transitions to the <code>IN_PLAY</code> state. This happens when all of
* the players have arrived and the server starts the game.
*/
protected void gameDidStart ()
{
if (_gobj == null) {
log.info("Received gameDidStart() after leaving game room.");
return;
}
// clear out our game over flag
setGameOver(false);
// let our delegates do their business
applyToDelegates(new DelegateOp(GameControllerDelegate.class) {
@Override
public void apply (PlaceControllerDelegate delegate) {
((GameControllerDelegate)delegate).gameDidStart();
}
});
}
/**
* Called when the game transitions to the <code>GAME_OVER</code> state. This happens when the
* game reaches some end condition by normal means (is not cancelled or aborted).
*/
protected void gameDidEnd ()
{
// let our delegates do their business
applyToDelegates(new DelegateOp(GameControllerDelegate.class) {
@Override
public void apply (PlaceControllerDelegate delegate) {
((GameControllerDelegate)delegate).gameDidEnd();
}
});
}
/**
* Called when the game was cancelled for some reason.
*/
protected void gameWasCancelled ()
{
// let our delegates do their business
applyToDelegates(new DelegateOp(GameControllerDelegate.class) {
@Override
public void apply (PlaceControllerDelegate delegate) {
((GameControllerDelegate)delegate).gameWasCancelled();
}
});
}
/**
* Called to give derived classes a chance to display animations, send a final packet, or do
* any other business they care to do when the game is about to reset.
*/
protected void gameWillReset ()
{
// let our delegates do their business
applyToDelegates(new DelegateOp(GameControllerDelegate.class) {
@Override
public void apply (PlaceControllerDelegate delegate) {
((GameControllerDelegate)delegate).gameWillReset();
}
});
}
/** A reference to the active parlor context. */
protected ParlorContext _ctx;
/** Our game configuration information. */
protected GameConfig _config;
/** A reference to the game object for the game that we're controlling. */
protected GameObject _gobj;
/** A local flag overriding the game over state for situations where the client knows the game
* is over before the server has transitioned the game object accordingly. */
protected boolean _gameOver;
}
@@ -0,0 +1,74 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.game.client;
import com.threerings.crowd.client.PlaceControllerDelegate;
/**
* Extends the {@link PlaceControllerDelegate} mechanism with game
* controller specific methods.
*/
public class GameControllerDelegate extends PlaceControllerDelegate
{
/**
* Provides the delegate with a reference to the game controller for
* which it is delegating.
*/
public GameControllerDelegate (GameController ctrl)
{
super(ctrl);
}
/**
* Called when the game transitions to the <code>IN_PLAY</code>
* state. This happens when all of the players have arrived and the
* server starts the game.
*/
public void gameDidStart ()
{
}
/**
* Called when the game transitions to the <code>GAME_OVER</code>
* state. This happens when the game reaches some end condition by
* normal means (is not cancelled or aborted).
*/
public void gameDidEnd ()
{
}
/**
* Called when the game was cancelled for some reason.
*/
public void gameWasCancelled ()
{
}
/**
* Called to give derived classes a chance to display animations, send
* a final packet, or do any other business they care to do when the
* game is about to reset.
*/
public void gameWillReset ()
{
}
}
@@ -0,0 +1,112 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.game.client;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.JComponent;
import javax.swing.JPanel;
import com.threerings.parlor.game.data.GameConfig;
/**
* Provides the base from which interfaces can be built to configure games
* prior to starting them. Derived classes would extend the base
* configurator adding interface elements and wiring them up properly to
* allow the user to configure an instance of their game.
*
* <p> Clients that use the game configurator will want to instantiate one
* based on the class returned from the {@link GameConfig} and then
* initialize it with a call to {@link GameConfigurator#init}.
*/
public abstract class SwingGameConfigurator extends GameConfigurator
{
/**
* Get the Swing JPanel that contains the UI elements for this configurator.
*/
public JPanel getPanel ()
{
return _panel;
}
/**
* Add a control to the interface. This should be the standard way
* that configurator controls are added, but note also that external
* entities may add their own controls that are related to the game,
* but do not directly alter the game config, so that all the controls
* are added in a uniform manner and are well aligned.
*/
public void addControl (JComponent label, JComponent control)
{
// Set up the constraints. There's really no point in saving
// these somewhere, as they're cloned anyway with every component
// insertion.
GridBagConstraints lc = new GridBagConstraints();
lc.gridx = 0;
lc.anchor = GridBagConstraints.NORTHWEST;
lc.insets = _labelInsets;
GridBagConstraints cc = new GridBagConstraints();
cc.gridx = 1;
cc.anchor = GridBagConstraints.NORTHWEST;
cc.insets = _controlInsets;
cc.gridwidth = GridBagConstraints.REMAINDER;
// add the components
_panel.add(label, lc);
_panel.add(control, cc);
}
/**
* Add a control to the interface. This should be the standard way
* that configurator controls are added, but note also that external
* entities may add their own controls that are related to the game,
* but do not directly alter the game config, so that all the controls
* are added in a uniform manner and are well aligned. This version adds
* a single component spanning the entire width.
*/
public void addWideControl (JComponent control)
{
// Set up the constraints. There's really no point in saving
// these somewhere, as they're cloned anyway with every component
// insertion.
GridBagConstraints cc = new GridBagConstraints();
cc.gridx = 0;
cc.anchor = GridBagConstraints.NORTHWEST;
cc.insets = _controlInsets;
cc.gridwidth = GridBagConstraints.REMAINDER;
// add the components
_panel.add(control, cc);
}
/** The panel on which the config options are placed. */
protected JPanel _panel = new JPanel(new GridBagLayout());
/** Insets for configuration labels. */
protected Insets _labelInsets = new Insets(1, 0, 1, 4);
/** Insets for configuration controls. */
protected Insets _controlInsets = new Insets(1, 4, 1, 0);
}
@@ -0,0 +1,50 @@
//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.game.data;
import com.threerings.io.SimpleStreamableObject;
/**
* Represents attributes of an AI player.
*/
public class GameAI extends SimpleStreamableObject
{
/** The "personality" of the AI, which can be interpreted by each puzzle. */
public int personality;
/** The skill level of the AI. */
public int skill;
/** A blank constructor for serialization. */
public GameAI ()
{
}
/**
* Constructs an AI with the specified (game-interpreted) skill and personality.
*/
public GameAI (int personality, int skill)
{
this.personality = personality;
this.skill = skill;
}
}

Some files were not shown because too many files have changed in this diff Show More