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
@@ -0,0 +1,246 @@
//
// $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.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.util.Byte;
import com.threerings.util.Comparable;
import com.threerings.util.Hashable;
import com.threerings.presents.dobj.DSet;
import com.threerings.presents.dobj.DSet_Entry;
/**
* Instances of this class represent individual playing cards.
*/
public class Card
implements DSet_Entry, Comparable, Hashable
{
/**
* Creates a new card.
*
* @param number the number of the card
* @param suit the suit of the card
*/
public function Card (number :int = 0, suit :int = 0)
{
_value = ((suit << 5) | number);
}
/**
* 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 function getSuit () :int
{
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 function isNumber () :Boolean
{
var number :int = 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 function isFace () :Boolean
{
var number :int = getNumber();
return number == CardCodes.KING || number == CardCodes.QUEEN ||
number == CardCodes.JACK;
}
/**
* Checks whether the card is an ace.
*
* @return true if the card is an ace, false otherwise
*/
public function isAce () :Boolean
{
return getNumber() == CardCodes.ACE;
}
/**
* Checks whether the card is a joker.
*
* @return true if the card is a joker, false otherwise
*/
public function isJoker () :Boolean
{
var number :int = getNumber();
return number == CardCodes.RED_JOKER || number == CardCodes.BLACK_JOKER;
}
/**
* Returns a hash code for this card.
*
* @return this card's hash code
*/
public function hashCode () :int
{
return _value;
}
/**
* 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 function compareTo (other :Object) :int
{
var otherValue :int = (other as Card)._value;
if (_value > otherValue) {
return +1;
} else if (_value < otherValue) {
return -1;
} else {
return 0;
}
}
/**
* Checks this card for equality with another.
*
* @param other the other card to compare
* @return true if the cards are equal, false otherwise
*/
public function equals (other :Object) :Boolean
{
if (other is Card) {
return _value == (other as Card)._value;
} else {
return false;
}
}
/**
* Returns a string representation of this card.
*
* @return a description of this card
*/
public function toString () :String
{
var number :int = getNumber();
if (number == CardCodes.RED_JOKER) {
return "RJ";
} else if (number == CardCodes.BLACK_JOKER) {
return "BJ";
} else {
return rankToString(number) + suitToString(getSuit());
}
}
// from interface DSet_Entry
public function getKey () :Object
{
return _value;
}
/**
* 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 function getNumber () :int
{
return (_value & 0x1F);
}
/**
* 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 function isValid () :Boolean
{
var number :int = getNumber(), suit :int = getSuit();
return number == CardCodes.RED_JOKER ||
number == CardCodes.BLACK_JOKER ||
(number >= 2 && number <= CardCodes.ACE &&
suit >= CardCodes.SPADES && suit <= CardCodes.DIAMONDS);
}
// from interface Streamable
public function readObject (ins :ObjectInputStream) :void
{
_value = ins.readByte();
}
// from interface Streamable
public function writeObject (out :ObjectOutputStream) :void
{
out.writeByte(_value);
}
protected function rankToString (rank :int) :String
{
if (rank >= 2 && rank <= 9) {
return String(rank);
}
switch (rank) {
case 10: return "T";
case CardCodes.JACK: return "J";
case CardCodes.QUEEN: return "Q";
case CardCodes.KING: return "K";
case CardCodes.ACE: return "A";
}
return "?";
}
protected function suitToString (suit :int) :String
{
switch (suit) {
case CardCodes.SPADES: return "s";
case CardCodes.HEARTS: return "h";
case CardCodes.CLUBS: return "c";
case CardCodes.DIAMONDS: return "d";
}
return "?";
}
/** The number of the card. */
protected var _value :int;
}
}
@@ -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.data {
/**
* Constants relating to the card services.
*/
public class CardCodes
{
/** The suit of spades. */
public static const SPADES :int = 0;
/** The suit of hearts. */
public static const HEARTS :int = 1;
/** The suit of clubs. */
public static const CLUBS :int = 2;
/** The suit of diamonds. */
public static const DIAMONDS :int = 3;
/** The number of the jack. */
public static const JACK :int = 11;
/** The number of the queen. */
public static const QUEEN :int = 12;
/** The number of the king. */
public static const KING :int = 13;
/** The number of the ace. */
public static const ACE :int = 14;
/** The number of the red joker. */
public static const RED_JOKER :int = 15;
/** The number of the black joker. */
public static const BLACK_JOKER :int = 16;
}
}
@@ -0,0 +1,107 @@
//
// $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.Arrays;
import com.threerings.util.StreamableArrayList;
/**
* Instances of this class represent decks of cards.
*/
public class Deck extends StreamableArrayList
{
/**
* Constructor.
*
* @param includeJokers whether or not to include the two jokers
* in the deck
*/
public function Deck (includeJokers :Boolean = false)
{
reset(includeJokers);
}
/**
* 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 function dealHand (size :int) :Hand
{
if (length < size) {
return null;
} else {
var hand :Hand = new Hand();
var offset :int = length - size;
for (var ii :int = 0; ii < size; ii++) {
hand.add(removeAt(offset));
}
return hand;
}
}
/**
* Returns a hand of cards to the deck.
*
* @param hand the hand of cards to return
*/
public function returnHand (hand :Hand) :void
{
addAll(hand);
hand.clear();
}
/**
* 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 function reset (includeJokers :Boolean) :void
{
clear();
for (var ii :int = CardCodes.SPADES; ii <= CardCodes.DIAMONDS; ii++) {
for (var jj :int = 2; jj <= CardCodes.ACE; jj++) {
add(new Card(jj, ii));
}
}
if (includeJokers) {
add(new Card(CardCodes.RED_JOKER, 3));
add(new Card(CardCodes.BLACK_JOKER, 3));
}
}
/**
* Shuffles the deck.
*/
public function shuffle () :void
{
Arrays.shuffle(_array);
}
}
}
@@ -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.card.data {
import com.threerings.util.StreamableArrayList;
/**
* Instances of this class represent hands of cards.
*/
public class Hand extends StreamableArrayList
{
public function Hand ()
{
// nothing needed
}
/**
* 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 function getSuitMemberCount (suit :int) :int
{
var members :int = 0;
for (var ii :int = 0; ii < length; ii++) {
if ((get(ii) as Card).getSuit() == suit) {
members++;
}
}
return members;
}
/**
* Get an array of the cards in this hand.
*/
public function getCards () :Array
{
return toArray();
}
/**
* Adds all of the specified cards to this hand.
*/
public function addAllCards (cards :Array) :void
{
addAll(cards);
}
/**
* Checks whether this hand contains all of the specified cards.
*/
public function containsAllCards (cards :Array) :Boolean
{
for (var ii :int = 0; ii < cards.length; ii++) {
if (!contains(cards[ii])) {
return false;
}
}
return true;
}
/**
* Removes all of the specified cards from this hand.
*/
public function removeAllCards (cards :Array) :void
{
for (var ii :int = 0; ii < cards.length; ii++) {
remove(cards[ii]);
}
}
}
}
@@ -0,0 +1,66 @@
//
// $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.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
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 var pidx :int;
/** The card that the player played. */
public var card :Card;
/**
* Creates a new player card.
*
* @param pidx the index of the player
* @param card the card played
*/
public function PlayerCard (pidx :int = 0, card :Card = null)
{
this.pidx = pidx;
this.card = card;
}
// from interface Streamable
public function readObject (ins :ObjectInputStream) :void
{
pidx = ins.readInt();
card = Card(ins.readObject());
}
// from interface Streamable
public function writeObject (out :ObjectOutputStream) :void
{
out.writeInt(pidx);
out.writeObject(card);
}
}
}
@@ -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.parlor.card.trick.client {
import com.threerings.io.TypedArray;
import com.threerings.presents.client.InvocationService;
import com.threerings.parlor.card.data.Card;
/**
* An ActionScript version of the Java TrickCardGameService interface.
*/
public interface TrickCardGameService extends InvocationService
{
// from Java interface TrickCardGameService
function playCard (arg1 :Card, arg2 :int) :void;
// from Java interface TrickCardGameService
function requestRematch () :void;
// from Java interface TrickCardGameService
function sendCardsToPlayer (arg1 :int, arg2 :TypedArray /* of class com.threerings.parlor.card.data.Card */) :void;
}
}
@@ -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.parlor.card.trick.data {
import com.threerings.io.TypedArray;
import com.threerings.util.Integer;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.parlor.card.data.Card;
import com.threerings.parlor.card.trick.client.TrickCardGameService;
/**
* Provides the implementation of the <code>TrickCardGameService</code> 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.
*/
public class TrickCardGameMarshaller extends InvocationMarshaller
implements TrickCardGameService
{
/** The method id used to dispatch <code>playCard</code> requests. */
public static const PLAY_CARD :int = 1;
// from interface TrickCardGameService
public function playCard (arg1 :Card, arg2 :int) :void
{
sendRequest(PLAY_CARD, [
arg1, Integer.valueOf(arg2)
]);
}
/** The method id used to dispatch <code>requestRematch</code> requests. */
public static const REQUEST_REMATCH :int = 2;
// from interface TrickCardGameService
public function requestRematch () :void
{
sendRequest(REQUEST_REMATCH, [
]);
}
/** The method id used to dispatch <code>sendCardsToPlayer</code> requests. */
public static const SEND_CARDS_TO_PLAYER :int = 3;
// from interface TrickCardGameService
public function sendCardsToPlayer (arg1 :int, arg2 :TypedArray /* of class com.threerings.parlor.card.data.Card */) :void
{
sendRequest(SEND_CARDS_TO_PLAYER, [
Integer.valueOf(arg1), arg2
]);
}
}
}
@@ -0,0 +1,152 @@
//
// $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 mx.controls.CheckBox;
import mx.controls.ComboBox;
import mx.controls.Label;
import com.threerings.parlor.data.RangeParameter;
import com.threerings.parlor.data.TableConfig;
import com.threerings.parlor.data.ToggleParameter;
import com.threerings.parlor.game.client.FlexGameConfigurator;
import com.threerings.parlor.game.data.GameConfig;
/**
* Provides a default implementation of a TableConfigurator for
* a Swing interface.
*/
public class DefaultFlexTableConfigurator extends TableConfigurator
{
/**
* Create a TableConfigurator that allows for the specified configuration parameters.
*/
public function DefaultFlexTableConfigurator (
players :RangeParameter, watchable :ToggleParameter = null, prvate :ToggleParameter = null)
{
_playersParam = players;
_watchableParam = watchable;
_privateParam = prvate;
_config.minimumPlayerCount = players.minimum;
// create a slider for players, if applicable
if (players.minimum != players.maximum) {
_playersBox = new ComboBox();
var values :Array = [];
var startDex :int = 0;
for (var ii :int = players.minimum; ii <= players.maximum; ii++) {
if (ii == players.start) {
startDex = ii;
}
values.push(ii);
}
_playersBox.dataProvider = values;
_playersBox.selectedIndex = startDex;
} else {
_config.desiredPlayerCount = players.start;
}
if (watchable != null) {
_watchableCheck = new CheckBox();
_watchableCheck.selected = watchable.start;
}
if (prvate != null) {
_privateCheck = new CheckBox();
_privateCheck.selected = prvate.start;
}
}
// documentation inherited
override protected function createConfigInterface () :void
{
super.createConfigInterface();
var gconf :FlexGameConfigurator = (_gameConfigurator as FlexGameConfigurator);
if (_playersBox != null) {
var playerLabel :Label = new Label();
playerLabel.text = _playersParam.name;
playerLabel.toolTip = _playersParam.tip;
playerLabel.styleName = "lobbyLabel";
gconf.addControl(playerLabel, _playersBox);
}
if (_watchableCheck != null) {
var watchableLabel :Label = new Label();
watchableLabel.text = _watchableParam.name;
watchableLabel.toolTip = _watchableParam.tip;
watchableLabel.styleName = "lobbyLabel";
gconf.addControl(watchableLabel, _watchableCheck);
}
if (_privateCheck != null) {
var privateLabel :Label = new Label();
privateLabel.text = _privateParam.name;
privateLabel.toolTip = _privateParam.tip;
privateLabel.styleName = "lobbyLabel";
gconf.addControl(privateLabel, _privateCheck);
}
}
// documentation inherited
override public function isEmpty () :Boolean
{
return (_playersBox == null) && (_watchableCheck == null) && (_privateCheck == null);
}
// documentation inherited
override protected function flushTableConfig () :void
{
super.flushTableConfig();
if (_playersBox != null) {
_config.desiredPlayerCount = int(_playersBox.value);
}
// TODO: it is wacky for the TableConfig.privateTable to mean two different things; it
// should be extended to have separate privateTable and watchableTable options.
if (_watchableCheck != null) {
_config.privateTable = !_watchableCheck.selected;
}
if (_privateCheck != null) {
_config.privateTable = _privateCheck.selected;
}
}
/** A component for configuring the number of players at the table. */
protected var _playersBox :ComboBox;
/** A checkbox to allow the table creator to specify if the table is watchable */
protected var _watchableCheck :CheckBox;
/** A checkbox to allow the table creator to specifiy if the table is private */
protected var _privateCheck :CheckBox;
/** Configuration passed in by the caller */
protected var _playersParam :RangeParameter;
protected var _watchableParam :ToggleParameter;
protected var _privateParam :ToggleParameter;
}
}
@@ -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.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.
*/
function receivedGameReady (gameOid :int) :Boolean;
}
}
@@ -0,0 +1,209 @@
//
// $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.Log;
import com.threerings.util.Name;
import com.threerings.parlor.data.ParlorCodes;
import com.threerings.parlor.game.data.GameConfig;
import com.threerings.parlor.util.ParlorContext;
/**
* The invitation class is used to track information related to
* outstanding invitations generated by or targeted to this client.
*/
public class Invitation
implements 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 var inviteId :int = -1;
/** The name of the other user involved in this invitation. */
public var opponent :Name;
/** The configuration of the game to be created. */
public var config :GameConfig;
/** Constructs a new invitation record. */
public function Invitation (
ctx :ParlorContext, pservice :ParlorService,
opponent :Name, config :GameConfig,
observer :InvitationResponseObserver)
{
_ctx = ctx;
_pservice = pservice;
_observer = observer;
this.opponent = opponent;
this.config = config;
}
/**
* Accepts this invitation.
*/
public function accept () :void
{
// generate the invocation service request
_pservice.respond(inviteId, ParlorCodes.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 function refuse (message :String) :void
{
// generate the invocation service request
_pservice.respond(inviteId, ParlorCodes.INVITATION_REFUSED, message, this);
}
/**
* Cancels this invitation.
*/
public function cancel () :void
{
// 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 function counter (
config :GameConfig, observer :InvitationResponseObserver) :void
{
// 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, ParlorCodes.INVITATION_COUNTERED, config, this);
}
// documentation inherited from interface
public function inviteReceived (inviteId :int) :void
{
// 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 function requestFailed (reason :String) :void
{
// 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.
*/
internal function receivedResponse (code :int, arg :Object) :void
{
// make sure we have an observer to notify
if (_observer == null) {
Log.getLog(this).warning("No observer registered for invitation " +
this + ".");
return;
}
// notify the observer
try {
switch (code) {
case ParlorCodes.INVITATION_ACCEPTED:
_observer.invitationAccepted(this);
break;
case ParlorCodes.INVITATION_REFUSED:
_observer.invitationRefused(this, (arg as String));
break;
case ParlorCodes.INVITATION_COUNTERED:
_observer.invitationCountered(this, (arg as GameConfig));
break;
}
} catch (e :Error) {
Log.getLog(this).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 != ParlorCodes.INVITATION_COUNTERED) {
_ctx.getParlorDirector().clearInvitation(this);
}
}
/** Returns a string representation of this invitation record. */
public function toString () :String
{
return "[inviteId=" + inviteId + ", opponent=" + opponent +
", config=" + config + ", observer=" + _observer +
", cancelled=" + _cancelled + "]";
}
/** Provides access to client services. */
protected var _ctx :ParlorContext;
/** Provides access to parlor services. */
protected var _pservice :ParlorService;
/** The entity to notify when we receive a response for this
* invitation. */
protected var _observer :InvitationResponseObserver;
/** 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 var _cancelled :Boolean = false;
}
}
@@ -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.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.
*/
function invitationReceived (invite :Invitation) :void;
/**
* Called when an invitation is cancelled by the inviting player.
*
* @param invite the cancelled invitation.
*/
function invitationCancelled (invite :Invitation) :void;
}
}
@@ -0,0 +1,62 @@
//
// $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.
*/
function invitationAccepted (invite :Invitation) :void;
/**
* 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.
*/
function invitationRefused (invite :Invitation, message :String) :void;
/**
* 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.
*/
function invitationCountered (invite :Invitation, config :GameConfig) :void;
}
}
@@ -0,0 +1,105 @@
//
// $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.InvocationDecoder;
import com.threerings.parlor.client.ParlorReceiver;
import com.threerings.parlor.game.data.GameConfig;
/**
* Dispatches calls to a {@link ParlorReceiver} instance.
*/
public class ParlorDecoder extends InvocationDecoder
{
/** The generated hash code used to identify this receiver class. */
public static const RECEIVER_CODE :String = "5ef9ee0d359c42a9024498ee9aad119a";
/** The method id used to dispatch {@link ParlorReceiver#gameIsReady}
* notifications. */
public static const GAME_IS_READY :int = 1;
/** The method id used to dispatch {@link ParlorReceiver#receivedInvite}
* notifications. */
public static const RECEIVED_INVITE :int = 2;
/** The method id used to dispatch {@link ParlorReceiver#receivedInviteCancellation}
* notifications. */
public static const RECEIVED_INVITE_CANCELLATION :int = 3;
/** The method id used to dispatch {@link ParlorReceiver#receivedInviteResponse}
* notifications. */
public static const RECEIVED_INVITE_RESPONSE :int = 4;
/**
* Creates a decoder that may be registered to dispatch invocation
* service notifications to the specified receiver.
*/
public function ParlorDecoder (receiver :ParlorReceiver)
{
this.receiver = receiver;
}
// documentation inherited
override public function getReceiverCode () :String
{
return RECEIVER_CODE;
}
// documentation inherited
override public function dispatchNotification (
methodId :int, args :Array) :void
{
var prec :ParlorReceiver = (receiver as ParlorReceiver);
switch (methodId) {
case GAME_IS_READY:
prec.gameIsReady(args[0] as int);
return;
case RECEIVED_INVITE:
prec.receivedInvite(
(args[0] as int), (args[1] as Name),
(args[2] as GameConfig)
);
return;
case RECEIVED_INVITE_CANCELLATION:
prec.receivedInviteCancellation(
(args[0] as int)
);
return;
case RECEIVED_INVITE_RESPONSE:
prec.receivedInviteResponse(
(args[0] as int), (args[1] as int), args[2]
);
return;
default:
super.dispatchNotification(methodId, args);
return;
}
}
}
}
@@ -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.parlor.client {
import com.threerings.util.Arrays;
import com.threerings.util.Log;
import com.threerings.util.Map;
import com.threerings.util.Maps;
import com.threerings.util.Name;
import com.threerings.presents.client.BasicDirector;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.ClientEvent;
import com.threerings.presents.client.InvocationService;
import com.threerings.presents.client.InvocationService_ConfirmListener;
import com.threerings.parlor.data.ParlorCodes;
import com.threerings.parlor.data.ParlorMarshaller;
import com.threerings.parlor.game.data.GameConfig;
import com.threerings.parlor.util.ParlorContext;
/**
* 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 ParlorReceiver
{
// statically reference classes we require
ParlorMarshaller;
/**
* 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 function ParlorDirector (ctx :ParlorContext)
{
super(ctx);
_pctx = ctx;
// register ourselves with the invocation director as a parlor notification receiver
_pctx.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 function setInvitationHandler (handler :InvitationHandler) :void
{
_handler = handler;
}
/**
* Adds the specified observer to the list of entities that are notified when we receive a game
* ready notification.
*/
public function addGameReadyObserver (observer :GameReadyObserver) :void
{
_grobs.push(observer);
}
/**
* Removes the specified observer from the list of entities that are notified when we receive a
* game ready notification.
*/
public function removeGameReadyObserver (observer :GameReadyObserver) :void
{
Arrays.removeFirst(_grobs, 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 function invite (
invitee :Name, config :GameConfig, observer :InvitationResponseObserver) :Invitation
{
// create the invitation record
var invite :Invitation = new Invitation(_pctx, _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 function startSolitaire (
config :GameConfig, listener :InvocationService_ConfirmListener) :void
{
_pservice.startSolitaire(config, listener);
}
// documentation inherited
override public function clientDidLogoff (event :ClientEvent) :void
{
super.clientDidLogoff(event);
_pservice = null;
_pendingInvites.clear();
}
// documentation inherited from interface
public function gameIsReady (gameOid :int) :void
{
_log.info("Handling game ready", "goid", gameOid);
// see what our observers have to say about it
var handled :Boolean = false;
for (var ii :int = 0; ii < _grobs.length; ii++) {
var grob :GameReadyObserver = (_grobs[ii] as GameReadyObserver);
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) {
_pctx.getLocationDirector().moveTo(gameOid);
}
}
// documentation inherited from interface
public function receivedInvite (remoteId :int, inviter :Name, config :GameConfig) :void
{
// create an invitation record for this invitation
var invite :Invitation = new Invitation(_pctx, _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 (err :Error) {
_log.warning("Invitation handler choked on invite notification", "invite", invite, err);
}
}
// documentation inherited from interface
public function receivedInviteResponse (remoteId :int, code :int, arg :Object) :void
{
// look up the invitation record for this invitation
var invite :Invitation = (_pendingInvites.get(remoteId) as Invitation);
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 function receivedInviteCancellation (remoteId :int) :void
{
// TBD
}
/**
* Register a new invitation in our pending invitations table. The invitation will call this
* when it knows its invitation id.
*/
public function registerInvitation (invite :Invitation) :void
{
_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.
*/
public function clearInvitation (invite :Invitation) :void
{
_pendingInvites.remove(invite.inviteId);
}
// from BasicDirector
override protected function registerServices (client :Client) :void
{
client.addServiceGroup(ParlorCodes.PARLOR_GROUP);
}
// from BasicDirector
override protected function fetchServices (client :Client) :void
{
// get a handle on our parlor services
_pservice = (client.requireService(ParlorService) as ParlorService);
super.fetchServices(client);
}
/** An active parlor context. */
protected var _pctx :ParlorContext;
/** Provides access to parlor server side services. */
protected var _pservice :ParlorService;
/** The entity that has registered itself to handle incoming invitation notifications. */
protected var _handler :InvitationHandler;
/** A table of acknowledged (but not yet accepted or refused) invitation requests, keyed on
* invitation id. */
protected var _pendingInvites :Map = Maps.newMapOf(int);
/** We notify the entities on this list when we get a game ready notification. */
protected var _grobs :Array = new Array();
/** For great logging. */
private var _log :Log = Log.getLog(this);
}
}
@@ -0,0 +1,86 @@
//
// $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.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.
*/
function gameIsReady (gameOid :int) :void;
/**
* 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.
*/
function receivedInvite (
remoteId :int, inviter :Name, config :GameConfig) :void;
/**
* 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.
*/
function receivedInviteResponse (
remoteId :int, code :int, arg :Object) :void;
/**
* Called by the invocation services when an outstanding invitation
* has been cancelled by the inviting user.
*
* @param remoteId the indentifier of the cancelled invitation.
*/
function receivedInviteCancellation (remoteId :int) :void;
}
}
@@ -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.parlor.client {
import com.threerings.util.Name;
import com.threerings.presents.client.InvocationService;
import com.threerings.presents.client.InvocationService_ConfirmListener;
import com.threerings.presents.client.InvocationService_InvocationListener;
import com.threerings.parlor.game.data.GameConfig;
/**
* An ActionScript version of the Java ParlorService interface.
*/
public interface ParlorService extends InvocationService
{
// from Java interface ParlorService
function cancel (arg1 :int, arg2 :InvocationService_InvocationListener) :void;
// from Java interface ParlorService
function invite (arg1 :Name, arg2 :GameConfig, arg3 :ParlorService_InviteListener) :void;
// from Java interface ParlorService
function respond (arg1 :int, arg2 :int, arg3 :Object, arg4 :InvocationService_InvocationListener) :void;
// from Java interface ParlorService
function startSolitaire (arg1 :GameConfig, arg2 :InvocationService_ConfirmListener) :void;
}
}
@@ -0,0 +1,35 @@
//
// $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.presents.client.InvocationService_InvocationListener;
/**
* An ActionScript version of the Java ParlorService_InviteListener interface.
*/
public interface ParlorService_InviteListener
extends InvocationService_InvocationListener
{
// from Java ParlorService_InviteListener
function inviteReceived (arg1 :int) :void
}
}
@@ -0,0 +1,39 @@
//
// $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.
*/
function seatednessDidChange (isSeated :Boolean) :void;
}
}
@@ -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 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 function 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 function setTableConfig (config :TableConfig) :void
{
if (config != null) {
_config = config;
}
}
/**
* Initialize the TableConfigurator.
*
* At the time this is called, the GameConfigurator should have
* already been initialized.
*/
public function init (
ctx :ParlorContext, gameConfigurator :GameConfigurator) :void
{
_ctx = ctx;
_gameConfigurator = gameConfigurator;
createConfigInterface();
}
/**
* Create the table config object that will be used.
*/
protected function createTableConfig () :TableConfig
{
return new TableConfig();
}
/**
* Create the config interface.
*/
protected function createConfigInterface () :void
{
// 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*/ function isEmpty () :Boolean
{
throw new Error("abstract");
}
/**
* Return the fully configured table config according to the currently
* configured user interface elements.
*/
public function getTableConfig () :TableConfig
{
flushTableConfig();
return _config;
}
/**
* Derived classes will want to override this method, flushing
* values from the user interface to the table config object.
*/
protected function flushTableConfig () :void
{
// nothing by default
}
/** Provides access to client services. */
protected var _ctx :ParlorContext;
/** The config we're configurating. */
protected var _config :TableConfig;
/** The game configurator, which we may wish to reference. */
protected var _gameConfigurator :GameConfigurator;
}
}
@@ -0,0 +1,426 @@
//
// $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.Arrays;
import com.threerings.util.Log;
import com.threerings.util.Name;
import com.threerings.util.ObserverList;
import com.threerings.util.Util;
import com.threerings.presents.client.BasicDirector;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.ClientEvent;
import com.threerings.presents.client.InvocationService_ResultListener;
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.GameConfig;
import com.threerings.parlor.util.ParlorContext;
/**
* As tables are created and managed within the scope of a place (a lobby), we want to fold the
* table management functionality into the standard hierarchy of place controllers that deal with
* place-related functionality on the client. Thus, instead of forcing places that expect to have
* tables to extend a <code>TableLobbyController</code> or something similar, we instead provide
* the table director which can be instantiated by the place controller (or specific table related
* views) to handle the table matchmaking services.
*
* <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>
*
* <p> The table services expect that the place object being used as a lobby in which the table
* matchmaking takes place implements the {@link TableLobbyObject} interface. </p>
*/
public class TableDirector extends BasicDirector
implements SetListener, InvocationService_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 bundle the message bundle to use when reporting errors.
*/
public function TableDirector (
ctx :ParlorContext, tableField :String, errorBundle :String = null)
{
super(ctx);
// keep track of this stuff
_pctx = ctx;
_tableField = tableField;
_errorBundle = errorBundle;
}
/**
* This must be called by the entity that uses the table director when the using entity
* prepares to enter and display a "place".
*/
public function setTableObject (tlobj :DObject) :void
{
// the distributed object should be a TableLobbyObject
_tlobj = TableLobbyObject(tlobj);
// listen for table set updates
tlobj.addListener(this);
}
/**
* This must be called by the entity that uses the table director when the using entity has
* left and is done displaying the "place".
*/
public function clearTableObject () :void
{
// remove our listenership and clear out
if (_tlobj != null) {
(_tlobj as DObject).removeListener(this);
_tlobj = null;
_ourTable = null;
}
}
/**
* Requests that the specified observer be added to the list of observers that are notified
* of table updates
*/
public function addTableObserver (observer :TableObserver) :void
{
_tableObservers.add(observer);
}
/**
* Requests that the specified observer be removed from to the list of observers that are
* notified of table updates
*/
public function removeTableObserver (observer :TableObserver) :void
{
_tableObservers.remove(observer);
}
/**
* 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 function addSeatednessObserver (observer :SeatednessObserver) :void
{
_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 function removeSeatednessObserver (observer :SeatednessObserver) :void
{
_seatedObservers.remove(observer);
}
/**
* Returns true if this client is currently seated at a table, false if they are not.
*/
public function isSeated () :Boolean
{
return (_ourTable != null);
}
/**
* Returns the table this client is currently seated at, or null.
*/
public function getSeatedTable () :Table
{
return _ourTable;
}
/**
* By default, failure is handled by reporting feedback via the chat director. Clients that
* require custom error handling can provide a function with the signature:
* <pre>
* function (cause :String) :void
* </pre>
*/
public function setFailureHandler (handler :Function) :void
{
_failureHandler = handler;
}
/**
* 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 function createTable (tableConfig :TableConfig, config :GameConfig) :void
{
// 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 function joinTable (tableId :int, position :int) :void
{
// 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
log.info("Joining table", "tid", tableId, "pos", position);
_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 function leaveTable (tableId :int) :void
{
// 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 function startTableNow (tableId :int) :void
{
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 function bootPlayer (tableId :int, target :Name) :void
{
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);
}
// documentation inherited
override public function clientDidLogoff (event :ClientEvent) :void
{
super.clientDidLogoff(event);
_tlobj = null;
_ourTable = null;
}
// documentation inherited
override protected function fetchServices (client :Client) :void
{
}
// documentation inherited
public function entryAdded (event :EntryAddedEvent) :void
{
if (event.getName() == _tableField) {
var table :Table = (event.getEntry() as Table);
// check to see if we just joined a table
checkSeatedness(table);
// now let the observers know what's up
_tableObservers.apply(function (to :TableObserver) :void {
to.tableAdded(table);
});
}
}
// documentation inherited
public function entryUpdated (event :EntryUpdatedEvent) :void
{
if (event.getName() == _tableField) {
var table :Table = (event.getEntry() as Table);
// check to see if we just joined or left a table
checkSeatedness(table);
// now let the observers know what's up
_tableObservers.apply(function (to :TableObserver) :void {
to.tableUpdated(table);
});
}
}
// documentation inherited
public function entryRemoved (event :EntryRemovedEvent) :void
{
if (event.getName() == _tableField) {
var tableId :int = (event.getKey() as int);
// 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
_tableObservers.apply(function (to :TableObserver) :void {
to.tableRemoved(tableId);
});
}
}
// documentation inherited from interface
public function requestProcessed (result :Object) :void
{
var tableId :int = int(result);
if (_tlobj == null) {
// we've left, it's none of our concern anymore
log.info("Table created, but no lobby.", "tableId", tableId);
return;
}
var table :Table = (_tlobj.getTables().get(tableId) as Table);
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)) {
// let's boogie!
_pctx.getParlorDirector().gameIsReady(table.gameOid);
}
}
// documentation inherited from interface
public function requestFailed (reason :String) :void
{
if (_failureHandler != null) {
_failureHandler(reason);
} else {
_pctx.getChatDirector().displayFeedback(_errorBundle, reason);
}
}
/**
* Checks to see if we're a member of this table and notes it as our table, if so.
*/
protected function checkSeatedness (table :Table) :void
{
var oldTable :Table = _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 players array
var self :BodyObject = (_pctx.getClient().getClientObject() as BodyObject);
if (Arrays.contains(table.players, self.getVisibleName())) {
_ourTable = table;
}
// if nothing changed, bail now
if (Util.equals(oldTable, _ourTable)) {
return;
}
// otherwise notify the observers
notifySeatedness(_ourTable != null);
}
/**
* Notifies the seatedness observers of a seatedness change.
*/
protected function notifySeatedness (isSeated :Boolean) :void
{
_seatedObservers.apply(function (so :SeatednessObserver) :void {
so.seatednessDidChange(isSeated);
});
}
/** A context by which we can access necessary client services. */
protected var _pctx :ParlorContext;
/** The place object, cast as a TableLobbyObject. */
protected var _tlobj :TableLobbyObject;
/** The field name of the distributed set that contains our tables. */
protected var _tableField :String;
/** The message bundle to use when reporting errors. */
protected var _errorBundle :String;
/** The table of which we are a member if any. */
protected var _ourTable :Table;
/** An array of entities that want to hear about table updates. */
protected var _tableObservers :ObserverList = new ObserverList();
/** An array of entities that want to hear about when we stand up or sit down. */
protected var _seatedObservers :ObserverList = new ObserverList();
/** A custom failure handler, or null. */
protected var _failureHandler :Function;
private static const log :Log = Log.getLog(TableDirector);
}
}
@@ -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.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.
*/
function tableAdded (table :Table) :void;
/**
* Called when something has changed about a table (occupant list
* updated, state changed from matchmaking to in-play, etc.).
*/
function tableUpdated (table :Table) :void;
/**
* Called when a table goes away.
*/
function tableRemoved (tableId :int) :void;
}
}
@@ -0,0 +1,53 @@
//
// $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.presents.client.InvocationService_InvocationListener;
import com.threerings.presents.client.InvocationService_ResultListener;
import com.threerings.parlor.data.TableConfig;
import com.threerings.parlor.game.data.GameConfig;
/**
* An ActionScript version of the Java TableService interface.
*/
public interface TableService extends InvocationService
{
// from Java interface TableService
function bootPlayer (arg1 :int, arg2 :Name, arg3 :InvocationService_InvocationListener) :void;
// from Java interface TableService
function createTable (arg1 :TableConfig, arg2 :GameConfig, arg3 :InvocationService_ResultListener) :void;
// from Java interface TableService
function joinTable (arg1 :int, arg2 :int, arg3 :InvocationService_InvocationListener) :void;
// from Java interface TableService
function leaveTable (arg1 :int, arg2 :InvocationService_InvocationListener) :void;
// from Java interface TableService
function startTableNow (arg1 :int, arg2 :InvocationService_InvocationListener) :void;
}
}
@@ -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.data {
import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.io.TypedArray;
/**
* 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 var choices :TypedArray;
/** The starting selection. */
public var start :String;
public function ChoiceParameter ()
{
}
// documentation inherited
override public function getDefaultValue () :Object
{
return start;
}
// from interface Streamable
override public function readObject (ins :ObjectInputStream) :void
{
super.readObject(ins);
choices = TypedArray(ins.readObject());
start = (ins.readField(String) as String);
}
// from interface Streamable
override public function writeObject (out :ObjectOutputStream) :void
{
super.writeObject(out);
out.writeObject(choices);
out.writeField(start);
}
}
}
@@ -0,0 +1,81 @@
//
// $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.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
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 var ident :String;
/** A human readable name for this configuration parameter. */
public var name :String;
/** A human readable tooltip to display when the mouse is hovered over this configuration
* parameter. */
public var tip :String;
public function Parameter ()
{
}
public function toString () :String
{
return ident;
}
public function getDefaultValue () :Object
{
throw new Error("Abstract");
}
/** Returns the translation key for this parameter's label. */
public function getLabel () :String
{
return "m." + ident;
}
// from interface Streamable
public function readObject (ins :ObjectInputStream) :void
{
ident = (ins.readField(String) as String);
name = (ins.readField(String) as String);
tip = (ins.readField(String) as String);
}
// from interface Streamable
public function writeObject (out :ObjectOutputStream) :void
{
out.writeField(ident);
out.writeField(name);
out.writeField(tip);
}
}
}
@@ -0,0 +1,75 @@
//
// $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 class ParlorCodes extends InvocationCodes
{
/** Defines our invocation services group. */
public static const PARLOR_GROUP :String = "parlor";
/** The response code for an accepted invitation. */
public static const INVITATION_ACCEPTED :int = 0;
/** The response code for a refused invitation. */
public static const INVITATION_REFUSED :int = 1;
/** The response code for a countered invitation. */
public static const INVITATION_COUNTERED :int = 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 const INVITEE_NOT_ONLINE :String = "m.invitee_not_online";
/** An error code returned when a user requests to join a table that
* doesn't exist. */
public static const NO_SUCH_TABLE :String = "m.no_such_table";
/** An error code returned by the table services. */
public static const INVALID_TABLE_POSITION :String = "m.invalid_table_position";
/** An error code returned by the table services. */
public static const MUST_BE_CREATOR :String = "m.must_be_creator";
/** An error code returned by the table services. */
public static const NO_SELF_BOOT :String = "m.no_self_boot";
/** An error code returned by the table services. */
public static const TABLE_POSITION_OCCUPIED :String = "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 const ALREADY_AT_TABLE :String = "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 const NOT_AT_TABLE :String = "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 const BANNED_FROM_TABLE :String = "m.banned_from_table";
}
}
@@ -0,0 +1,99 @@
//
// $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.Integer;
import com.threerings.util.Name;
import com.threerings.presents.client.InvocationService_ConfirmListener;
import com.threerings.presents.client.InvocationService_InvocationListener;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.presents.data.InvocationMarshaller_ConfirmMarshaller;
import com.threerings.presents.data.InvocationMarshaller_ListenerMarshaller;
import com.threerings.parlor.client.ParlorService;
import com.threerings.parlor.client.ParlorService_InviteListener;
import com.threerings.parlor.game.data.GameConfig;
/**
* Provides the implementation of the <code>ParlorService</code> 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.
*/
public class ParlorMarshaller extends InvocationMarshaller
implements ParlorService
{
/** The method id used to dispatch <code>cancel</code> requests. */
public static const CANCEL :int = 1;
// from interface ParlorService
public function cancel (arg1 :int, arg2 :InvocationService_InvocationListener) :void
{
var listener2 :InvocationMarshaller_ListenerMarshaller = new InvocationMarshaller_ListenerMarshaller();
listener2.listener = arg2;
sendRequest(CANCEL, [
Integer.valueOf(arg1), listener2
]);
}
/** The method id used to dispatch <code>invite</code> requests. */
public static const INVITE :int = 2;
// from interface ParlorService
public function invite (arg1 :Name, arg2 :GameConfig, arg3 :ParlorService_InviteListener) :void
{
var listener3 :ParlorMarshaller_InviteMarshaller = new ParlorMarshaller_InviteMarshaller();
listener3.listener = arg3;
sendRequest(INVITE, [
arg1, arg2, listener3
]);
}
/** The method id used to dispatch <code>respond</code> requests. */
public static const RESPOND :int = 3;
// from interface ParlorService
public function respond (arg1 :int, arg2 :int, arg3 :Object, arg4 :InvocationService_InvocationListener) :void
{
var listener4 :InvocationMarshaller_ListenerMarshaller = new InvocationMarshaller_ListenerMarshaller();
listener4.listener = arg4;
sendRequest(RESPOND, [
Integer.valueOf(arg1), Integer.valueOf(arg2), arg3, listener4
]);
}
/** The method id used to dispatch <code>startSolitaire</code> requests. */
public static const START_SOLITAIRE :int = 4;
// from interface ParlorService
public function startSolitaire (arg1 :GameConfig, arg2 :InvocationService_ConfirmListener) :void
{
var listener2 :InvocationMarshaller_ConfirmMarshaller = new InvocationMarshaller_ConfirmMarshaller();
listener2.listener = arg2;
sendRequest(START_SOLITAIRE, [
arg1, listener2
]);
}
}
}
@@ -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.data {
import com.threerings.presents.data.InvocationMarshaller_ListenerMarshaller;
import com.threerings.parlor.client.ParlorService_InviteListener;
/**
* Marshalls instances of the ParlorService_InviteMarshaller interface.
*/
public class ParlorMarshaller_InviteMarshaller
extends InvocationMarshaller_ListenerMarshaller
{
/** The method id used to dispatch <code>inviteReceived</code> responses. */
public static const INVITE_RECEIVED :int = 1;
// from InvocationMarshaller_ListenerMarshaller
override public function dispatchResponse (methodId :int, args :Array) :void
{
switch (methodId) {
case INVITE_RECEIVED:
(listener as ParlorService_InviteListener).inviteReceived(
(args[0] as int));
return;
default:
super.dispatchResponse(methodId, args);
return;
}
}
}
}
@@ -0,0 +1,69 @@
//
// $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.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
/**
* 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 var minimum :int;
/** The maximum value of this parameter. */
public var maximum :int;
/** The starting value for this parameter. */
public var start :int;
public function RangeParameter ()
{
}
// documentation inherited
override public function getDefaultValue () :Object
{
return start;
}
// from interface Streamable
override public function readObject (ins :ObjectInputStream) :void
{
super.readObject(ins);
minimum = ins.readInt();
maximum = ins.readInt();
start = ins.readInt();
}
// from interface Streamable
override public function writeObject (out :ObjectOutputStream) :void
{
super.writeObject(out);
out.writeInt(minimum);
out.writeInt(maximum);
out.writeInt(start);
}
}
}
@@ -0,0 +1,265 @@
//
// $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.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.io.TypedArray;
import com.threerings.util.Arrays;
import com.threerings.util.Hashable;
import com.threerings.util.Joiner;
import com.threerings.util.Name;
import com.threerings.presents.dobj.DSet;
import com.threerings.presents.dobj.DSet_Entry;
import com.threerings.parlor.game.data.GameConfig;
/**
* This class represents a table that is being used to matchmake a game by the Parlor services.
*/
public class Table
implements DSet_Entry, Hashable
{
/** Used to request any position at a table. */
public static const ANY_POSITION :int = -1;
/** The unique identifier for this table. */
public var tableId :int;
/** The object id of the lobby object with which this table is associated. */
public var lobbyOid :int;
/** The oid of the game that was created from this table or -1 if the table is still in
* matchmaking mode. */
public var gameOid :int = -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 var players :TypedArray;
/** 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 var watchers :TypedArray;
/** The body oids of the players of this table, or null if a party game. (This is not
* propagated to remote instances.) */
public var bodyOids :TypedArray;
/** The game config for the game that is being matchmade. */
public var config :GameConfig;
/** The table configuration object. */
public var tconfig :TableConfig;
/** Suitable for unserialization. */
public function Table ()
{
}
/**
* 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 function getPlayers () :Array
{
var parray :Array = new Array();
for (var ii :int = 0; ii < players.length; ii++) {
if (players[ii] != null) {
parray.push(players[ii]);
}
}
return parray;
}
/**
* Count the number of players currently occupying this table.
*/
public function getOccupiedCount () :int
{
var count :int = 0;
if (players != null) {
for (var ii :int = 0; ii < players.length; ii++) {
if (players[ii] != null) {
count++;
}
}
}
return count;
}
/**
* For a team game, get the team member indices of the compressed players array returned by
* getPlayers().
*/
public function getTeamMemberIndices () :TypedArray /* of Array of int */
{
var teams :Array = tconfig.teamMemberIndices;
if (teams == null) {
return null;
}
// compress the team indexes down
var newTeams :TypedArray = new TypedArray("[[I");
var players :Array = getPlayers();
for (var ii :int = 0; ii < teams.length; ii++) {
var subTeams :Array = (teams[ii] as Array);
var newSubTeams :TypedArray = TypedArray.create(int);
for (var jj :int = 0; jj < subTeams.length; jj++) {
var occ :Name = (players[(subTeams[jj] as int)] as Name);
if (occ != null) {
newSubTeams.push(Arrays.indexOf(players, occ));
}
}
newSubTeams.sort(null, Array.NUMERIC);
newTeams[ii] = newSubTeams;
}
return newTeams;
}
/**
* Returns true if this table has a sufficient number of players that the game can be
* started.
*/
public function mayBeStarted () :Boolean
{
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
var teams :Array = tconfig.teamMemberIndices;
for (var ii :int = 0; ii < teams.length; ii++) {
var teamCount :int = 0;
var members :Array = (teams[ii] as Array);
for (var jj :int = 0; jj < members.length; jj++) {
if (players[members[jj]] != 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 function shouldBeStarted () :Boolean
{
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 function inPlay () :Boolean
{
return gameOid != -1;
}
// from Hashable
public function hashCode () :int
{
return tableId;
}
// from Hashable
public function equals (other :Object) :Boolean
{
return (other is Table) && (tableId == (other as Table).tableId);
}
/**
* Generates a string representation of this table instance.
*/
public function toString () :String
{
var j :Joiner = Joiner.createFor(this);
toStringJoiner(j);
return j.toString();
}
// documentation inherited
public function getKey () :Object
{
return tableId;
}
// from Streamable
public function readObject (ins :ObjectInputStream) :void
{
tableId = ins.readInt();
lobbyOid = ins.readInt();
gameOid = ins.readInt();
players = TypedArray(ins.readObject());
watchers = TypedArray(ins.readObject());
config = GameConfig(ins.readObject());
tconfig = TableConfig(ins.readObject());
}
// from Streamable
public function writeObject (out :ObjectOutputStream) :void
{
out.writeInt(tableId);
out.writeInt(lobbyOid);
out.writeInt(gameOid);
out.writeObject(players);
out.writeObject(watchers);
out.writeObject(config);
out.writeObject(tconfig);
}
/**
* Helper method for toString, ripe for overrideability.
*/
protected function toStringJoiner (j :Joiner) :void
{
j.add("tableId", tableId, "lobbyOid", lobbyOid, "gameOid", gameOid,
"players", players, "watchers", watchers, "config", config);
}
/** A counter for assigning table ids. */
protected static var _tableIdCounter :int = 0;
}
}
@@ -0,0 +1,75 @@
//
// $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.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.io.SimpleStreamableObject;
import com.threerings.io.TypedArray;
/**
* 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 var desiredPlayerCount :int;
/** 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 var minimumPlayerCount :int;
/** 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 var teamMemberIndices :TypedArray;
/** Whether the table is "private". */
public var privateTable :Boolean;
public function TableConfig ()
{
// nothing needed
}
// from Streamable
override public function readObject (ins :ObjectInputStream) :void
{
super.readObject(ins);
desiredPlayerCount = ins.readInt();
minimumPlayerCount = ins.readInt();
teamMemberIndices = TypedArray(ins.readObject());
privateTable = ins.readBoolean();
}
// from Streamable
override public function writeObject (out :ObjectOutputStream) :void
{
super.writeObject(out);
out.writeInt(desiredPlayerCount);
out.writeInt(minimumPlayerCount);
out.writeObject(teamMemberIndices);
out.writeBoolean(privateTable);
}
}
}
@@ -0,0 +1,66 @@
//
// $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.
*/
function getTables () :DSet;
/**
* Adds the supplied table instance to the tables set (using the appropriate distributed object
* mechanisms).
*/
function addToTables (table :Table) :void;
/**
* Updates the value of the specified table instance in the tables distributed set (using the
* appropriate distributed object mechanisms).
*/
function updateTables (table :Table) :void;
/**
* Removes the table instance that matches the specified key from the tables set (using the
* appropriate distributed object mechanisms).
*/
function removeFromTables (key :Object) :void;
/**
* Returns a reference to the table service configured in this object.
*/
function getTableService () :TableMarshaller;
/**
* Configures the table service that clients should use to communicate table requests back to
* the table manager handling these tables.
*/
function setTableService (service :TableMarshaller) :void;
}
}
@@ -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.parlor.data {
import com.threerings.util.Integer;
import com.threerings.util.Name;
import com.threerings.presents.client.InvocationService_InvocationListener;
import com.threerings.presents.client.InvocationService_ResultListener;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.presents.data.InvocationMarshaller_ListenerMarshaller;
import com.threerings.presents.data.InvocationMarshaller_ResultMarshaller;
import com.threerings.parlor.client.TableService;
import com.threerings.parlor.game.data.GameConfig;
/**
* Provides the implementation of the <code>TableService</code> 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.
*/
public class TableMarshaller extends InvocationMarshaller
implements TableService
{
/** The method id used to dispatch <code>bootPlayer</code> requests. */
public static const BOOT_PLAYER :int = 1;
// from interface TableService
public function bootPlayer (arg1 :int, arg2 :Name, arg3 :InvocationService_InvocationListener) :void
{
var listener3 :InvocationMarshaller_ListenerMarshaller = new InvocationMarshaller_ListenerMarshaller();
listener3.listener = arg3;
sendRequest(BOOT_PLAYER, [
Integer.valueOf(arg1), arg2, listener3
]);
}
/** The method id used to dispatch <code>createTable</code> requests. */
public static const CREATE_TABLE :int = 2;
// from interface TableService
public function createTable (arg1 :TableConfig, arg2 :GameConfig, arg3 :InvocationService_ResultListener) :void
{
var listener3 :InvocationMarshaller_ResultMarshaller = new InvocationMarshaller_ResultMarshaller();
listener3.listener = arg3;
sendRequest(CREATE_TABLE, [
arg1, arg2, listener3
]);
}
/** The method id used to dispatch <code>joinTable</code> requests. */
public static const JOIN_TABLE :int = 3;
// from interface TableService
public function joinTable (arg1 :int, arg2 :int, arg3 :InvocationService_InvocationListener) :void
{
var listener3 :InvocationMarshaller_ListenerMarshaller = new InvocationMarshaller_ListenerMarshaller();
listener3.listener = arg3;
sendRequest(JOIN_TABLE, [
Integer.valueOf(arg1), Integer.valueOf(arg2), listener3
]);
}
/** The method id used to dispatch <code>leaveTable</code> requests. */
public static const LEAVE_TABLE :int = 4;
// from interface TableService
public function leaveTable (arg1 :int, arg2 :InvocationService_InvocationListener) :void
{
var listener2 :InvocationMarshaller_ListenerMarshaller = new InvocationMarshaller_ListenerMarshaller();
listener2.listener = arg2;
sendRequest(LEAVE_TABLE, [
Integer.valueOf(arg1), listener2
]);
}
/** The method id used to dispatch <code>startTableNow</code> requests. */
public static const START_TABLE_NOW :int = 5;
// from interface TableService
public function startTableNow (arg1 :int, arg2 :InvocationService_InvocationListener) :void
{
var listener2 :InvocationMarshaller_ListenerMarshaller = new InvocationMarshaller_ListenerMarshaller();
listener2.listener = arg2;
sendRequest(START_TABLE_NOW, [
Integer.valueOf(arg1), listener2
]);
}
}
}
@@ -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.data {
import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
/**
* Models a parameter that allows the toggling of a single value.
*/
public class ToggleParameter extends Parameter
{
/** The starting state for this parameter. */
public var start :Boolean;
public function ToggleParameter ()
{
}
// documentation inherited
override public function getDefaultValue () :Object
{
return start;
}
// from interface Streamable
override public function readObject (ins :ObjectInputStream) :void
{
super.readObject(ins);
start = ins.readBoolean();
}
// from interface Streamable
override public function writeObject (out :ObjectOutputStream) :void
{
super.writeObject(out);
out.writeBoolean(start);
}
}
}
@@ -0,0 +1,97 @@
//
// $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 mx.containers.Grid;
import mx.containers.GridItem;
import mx.containers.GridRow;
import mx.core.Container;
import mx.core.UIComponent;
import com.threerings.parlor.game.data.GameConfig;
import com.threerings.flex.GridUtil;
/**
* 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}. </p>
*/
public /*abstract*/ class FlexGameConfigurator extends GameConfigurator
{
/**
* Configures the number of columns to use when laying out our controls. This must be called
* before any calls to {@link #addControl}.
*/
public function setColumns (columns :int) :void
{
_columns = columns;
}
/**
* Get the Container that contains the UI elements for this configurator.
*/
public function getContainer () :Container
{
return _grid;
}
/**
* 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 function addControl (label :UIComponent, control :UIComponent,
verticalAlign :String = "middle") :void
{
if (_gridRow == null) {
_gridRow = new GridRow();
_grid.addChild(_gridRow);
}
var item :GridItem = GridUtil.addToRow(_gridRow, label);
item.setStyle("verticalAlign", verticalAlign);
if (_gridRow.numChildren > 1) {
item.setStyle("paddingLeft", 15);
}
item = GridUtil.addToRow(_gridRow, control);
item.setStyle("verticalAlign", verticalAlign);
if (_gridRow.numChildren == _columns * 2) {
_gridRow = null;
}
}
/** The grid on which the config options are placed. */
protected var _grid :Grid = new Grid();
/** The current row to which we're adding controls. */
protected var _gridRow :GridRow;
/** The number of columns in which to lay out our configuration. */
protected var _columns :int = 2;
}
}
@@ -0,0 +1,101 @@
//
// $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;
/**
* 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}. </p>
*/
public /*abstract*/ class GameConfigurator
{
/**
* Initializes this game configurator, creates its user interface elements and prepares it for
* display.
*/
public function init (ctx :ParlorContext) :void
{
// save this for later
_ctx = ctx;
// create our interface elements
createConfigInterface();
}
/**
* The default implementation creates nothing.
*/
protected function createConfigInterface () :void
{
}
/**
* Provides this configurator with its configuration. It should set up all of its user
* interface elements to reflect the configuration.
*/
public function setGameConfig (config :GameConfig) :void
{
_config = config;
// set up the user interface
gotGameConfig();
}
/**
* Derived classes will likely want to override this method and configure their user interface
* elements accordingly.
*/
protected function gotGameConfig () :void
{
}
/**
* Obtains a configured game configuration.
*/
public function getGameConfig () :GameConfig
{
// 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 function flushGameConfig () :void
{
}
/** Provides access to client services. */
protected var _ctx :ParlorContext;
/** Our game configuration. */
protected var _config :GameConfig;
}
}
@@ -0,0 +1,326 @@
//
// $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.util.Log;
import com.threerings.util.Name;
import com.threerings.presents.dobj.AttributeChangeListener;
import com.threerings.presents.dobj.AttributeChangedEvent;
import com.threerings.crowd.client.PlaceController;
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;
/**
* 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 adjustements 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 function init (ctx :CrowdContext, config :PlaceConfig) :void
{
// cast our references before we call super.init() so that when
// super.init() calls createPlaceView(), we have our casted
// references already in place
_pctx = (ctx as ParlorContext);
_gconfig = (config as GameConfig);
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 function willEnterPlace (plobj :PlaceObject) :void
{
super.willEnterPlace(plobj);
// obtain a casted reference
_gobj = (plobj as GameObject);
// if this place object is not our current location we'll need to add it as an auxiliary
// chat source
var bobj :BodyObject = (_ctx.getClient().getClientObject() as BodyObject);
if (bobj.getPlaceOid() != plobj.getOid()) {
_ctx.getChatDirector().addAuxiliarySource(_gobj, GameCodes.GAME_CHAT_TYPE);
}
// and add ourselves as a listener
_gobj.addListener(this);
// potentially let the game manager know that we're ready to roll
if (shouldAutoPlayerReady(bobj)) {
// 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
_ctx.getClient().callLater(playerReady);
}
log.info("Entered game " + _gobj.which() + ".");
}
/**
* Removes our listener registration from the game object and cleans
* house.
*/
override public function didLeavePlace (plobj :PlaceObject) :void
{
super.didLeavePlace(plobj);
_ctx.getChatDirector().removeAuxiliarySource(_gobj);
// unlisten to the game object
_gobj.removeListener(this);
_gobj = null;
}
/**
* Returns whether the game is over.
*/
public function isGameOver () :Boolean
{
var gameOver :Boolean = (_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 function setGameOver (gameOver :Boolean) :void
{
_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 function resetGame () :void
{
// let derived classes do their thing
gameWillReset();
// end the game until we receive a new board
setGameOver(true);
}
/**
* Returns the unique identifier for the current gameplay session.
*/
public function getSessionId () :int
{
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 function handleAction (cmd :String, arg :Object) :Boolean
{
return super.handleAction(cmd, arg);
}
/**
* A way for controllers to display a game-related system message.
*/
public function systemMessage (bundle :String, msg :String) :void
{
_ctx.getChatDirector().displayInfo(
bundle, msg, GameCodes.GAME_CHAT_TYPE);
}
// from interface AttributeChangeListener
public function attributeChanged (event :AttributeChangedEvent) :void
{
// deal with game state changes
var name :String = event.getName();
if (GameObject.STATE == name) {
var newState :int = int(event.getValue());
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 function stateDidChange (state :int) :Boolean
{
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;
}
/**
* Returns true if we should automatically call playerReady() in willEnterPlace().
*/
protected function shouldAutoPlayerReady (bobj :BodyObject) :Boolean
{
return (_gobj.getPlayerIndex(bobj.getVisibleName()) != -1);
}
/**
* 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 function playerReady () :void
{
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 function gameDidStart () :void
{
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(function (del :GameControllerDelegate) :void {
del.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 function gameDidEnd () :void
{
// let our delegates do their business
applyToDelegates(function (del :GameControllerDelegate) :void {
del.gameDidEnd();
});
}
/**
* Called when the game was cancelled for some reason.
*/
protected function gameWasCancelled () :void
{
// let our delegates do their business
applyToDelegates(function (del :GameControllerDelegate) :void {
del.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 function gameWillReset () :void
{
// let our delegates do their business
applyToDelegates(function (del :GameControllerDelegate) :void {
del.gameWillReset();
});
}
/**
* Convenience method to determine the type of game.
*/
protected function getMatchType () :int
{
return _gconfig.getMatchType();
}
/** A reference to the active parlor context. */
protected var _pctx :ParlorContext;
/** Our game configuration information. */
protected var _gconfig :GameConfig;
/** A reference to the game object for the game that we're
* controlling. */
protected var _gobj :GameObject;
/** 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 var _gameOver :Boolean;
private static const log :Log = Log.getLog(GameController);
}
}
@@ -0,0 +1,75 @@
//
// $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 function GameControllerDelegate (ctrl :GameController)
{
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 function gameDidStart () :void
{
}
/**
* 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 function gameDidEnd () :void
{
}
/**
* Called when the game was cancelled for some reason.
*/
public function gameWasCancelled () :void
{
}
/**
* 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 function gameWillReset () :void
{
}
}
}
@@ -0,0 +1,66 @@
//
// $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.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
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 var personality :int;
/** The skill level of the AI. */
public var skill :int;
/**
* Constructs an AI with the specified (game-interpreted) skill and
* personality.
*/
public function GameAI (personality :int = 0, skill :int = 0)
{
this.personality = personality;
this.skill = skill;
}
// from Streamable
override public function readObject (ins :ObjectInputStream) :void
{
super.readObject(ins);
personality = ins.readInt();
skill = ins.readInt();
}
// from Streamable
override public function writeObject (out :ObjectOutputStream) :void
{
super.writeObject(out);
out.writeInt(personality);
out.writeInt(skill);
}
}
}
@@ -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.game.data {
import com.threerings.presents.data.InvocationCodes;
/**
* Constants relating to the game services.
*/
public class GameCodes extends InvocationCodes
{
/** The message bundle identifier for general game messages. */
public static const GAME_MESSAGE_BUNDLE :String = "game.general";
/** The name of the message event to a placeObject that a player
* was knocked out of a puzzle. */
public static const PLAYER_KNOCKED_OUT :String = "playerKnocked";
/** The name of the message event to a placeObject that reports
* the winners and losers of a game. */
public static const WINNERS_AND_LOSERS :String = "winnersAndLosers";
/** A chat type for chatting on the game object. */
public static const GAME_CHAT_TYPE :String = "gameChat";
}
}
@@ -0,0 +1,187 @@
//
// $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.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.io.TypedArray;
import com.threerings.util.ClassUtil;
import com.threerings.util.Cloneable;
import com.threerings.util.Hashable;
import com.threerings.util.Name;
import com.threerings.util.StringUtil;
import com.threerings.crowd.data.PlaceConfig;
import com.threerings.parlor.client.TableConfigurator;
import com.threerings.parlor.game.client.GameConfigurator;
/**
* The game config class encapsulates the configuration information for a particular type of
* game. The hierarchy of game config objects mimics the hierarchy of game managers and
* controllers. Both the game manager and game controller are provided with the game config object
* when the game is created.
*
* <p> The game config object is also the mechanism used to instantiate the appropriate game
* manager and controller. Every game must have an associated game config derived class that
* overrides {@link #createController} and {@link #getManagerClassName}, returning the appropriate
* game controller and manager class for that game. Thus the entire chain of events that causes a
* particular game to be created is the construction of the appropriate game config instance which
* is provided to the server as part of an invitation or via some other matchmaking mechanism. </p>
*/
public /*abstract*/ class GameConfig extends PlaceConfig
implements Cloneable, Hashable
{
/** Game type constant: a game that is started with a list of players, and those are the only
* players that may play. */
public static const SEATED_GAME :int = 0;
/** Game type constant: a game that starts immediately, but only has a certain number of player
* slots. Users enter the game room, and then choose where to sit. */
public static const SEATED_CONTINUOUS :int = 1;
/** Game type constant: a game that starts immediately, and every user that enters is a
* player. */
public static const PARTY :int = 2;
/** The usernames of the players involved in this game, or an empty array if such information
* is not needed by this particular game. */
public var players :TypedArray = TypedArray.create(Name);
/** Indicates whether or not this game is rated. */
public var rated :Boolean = true;
/** Configurations for AIs to be used in this game. Slots with real players should be null and
* slots with AIs should contain configuration for those AIs. A null array indicates no use of
* AIs at all. */
public var ais :TypedArray = TypedArray.create(GameAI);
public function GameConfig ()
{
// nothing needed
}
/**
* Returns a numeric identifier for this game class. This may be used to track persisent
* information on a per-game basis.
*/
public function getGameId () :int
{
throw new Error("abstract");
}
public function getGameIdent () :String
{
throw new Error("abstract");
}
/**
* Get the type of game.
*/
public function getMatchType () :int
{
return SEATED_GAME;
}
/**
* Creates a configurator that can be used to create a user interface for configuring this
* instance prior to starting the game. If no configuration is necessary, this method should
* return null.
*/
public /*abstract*/ function createConfigurator () :GameConfigurator
{
throw new Error("abstract");
}
/**
* Creates a table configurator for initializing 'table' properties of the game. The default
* implementation returns null.
*/
public function createTableConfigurator () :TableConfigurator
{
return null;
}
/**
* Computes a hashcode for this game config object that supports our {@link #equals}
* implementation. Objects that are equal should have the same hashcode.
*/
public function hashCode () :int
{
// look ma, it's so sophisticated!
return StringUtil.hashCode(ClassUtil.getClassName(this)) + (rated ? 1 : 0);
}
// from Cloneable
public function clone () :Object
{
var copy :GameConfig = (ClassUtil.newInstance(this) as GameConfig);
copy.players = this.players;
copy.rated = this.rated;
copy.ais = this.ais;
return copy;
}
/**
* Returns true if this game config object is equal to the supplied object (meaning it is also
* a game config object and its configuration settings are the same as ours).
*/
public function equals (other :Object) :Boolean
{
// make sure they're of the same class
if (ClassUtil.isSameClass(other, this)) {
var that :GameConfig = GameConfig(other);
return this.getGameId() == that.getGameId() && this.rated == that.rated;
} else {
return false;
}
}
/**
* Returns an Array of strings that describe the configuration of this game. Default
* implementation returns an empty array.
*/
public function getDescription () :Array
{
return new Array(); // nothing by default
}
// from interface Streamable
override public function readObject (ins :ObjectInputStream) :void
{
super.readObject(ins);
players = TypedArray(ins.readObject());
rated = ins.readBoolean();
ais = TypedArray(ins.readObject());
}
// from interface Streamable
override public function writeObject (out :ObjectOutputStream) :void
{
super.writeObject(out);
out.writeObject(players);
out.writeBoolean(rated);
out.writeObject(ais);
}
}
}
@@ -0,0 +1,435 @@
//
// $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.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.io.TypedArray;
import com.threerings.util.Arrays;
import com.threerings.util.Integer;
import com.threerings.util.Joiner;
import com.threerings.util.Name;
import com.threerings.util.langBoolean;
import com.threerings.crowd.data.PlaceObject;
/**
* A game object hosts the shared data associated with a game played by one or more players. The
* game object extends the place object so that the game can act as a place where players actually
* go when playing the game. Only very basic information is maintained in the base game object. It
* serves as the base for a hierarchy of game object derivatives that handle basic gameplay for a
* suite of different game types (ie. turn based games, party games, board games, card games,
* etc.).
*/
public class GameObject extends PlaceObject
{
// AUTO-GENERATED: FIELDS START
/** The field name of the <code>state</code> field. */
public static const STATE :String = "state";
/** The field name of the <code>isRated</code> field. */
public static const IS_RATED :String = "isRated";
/** The field name of the <code>isPrivate</code> field. */
public static const IS_PRIVATE :String = "isPrivate";
/** The field name of the <code>players</code> field. */
public static const PLAYERS :String = "players";
/** The field name of the <code>winners</code> field. */
public static const WINNERS :String = "winners";
/** The field name of the <code>sessionId</code> field. */
public static const SESSION_ID :String = "sessionId";
/** The field name of the <code>playerStatus</code> field. */
public static const PLAYER_STATUS :String = "playerStatus";
// AUTO-GENERATED: FIELDS END
/** A game state constant indicating that the game has not yet started and is still awaiting
* the arrival of all of the players. */
public static const PRE_GAME :int = 0;
/** A game state constant indicating that the game is in play. */
public static const IN_PLAY :int = 1;
/** A game state constant indicating that the game ended normally. */
public static const GAME_OVER :int = 2;
/** A game state constant indicating that the game was cancelled. */
public static const CANCELLED :int = 3;
/** The player status constant for a player whose game is in play. */
public static const PLAYER_IN_PLAY :int = 0;
/** The player status constant for a player whose has been knocked out of the game. NOTE: This
* can include a player choosing to leave a game prematurely. */
public static const PLAYER_LEFT_GAME :int = 1;
/** The game state, one of {@link #PRE_GAME}, {@link #IN_PLAY}, {@link #GAME_OVER}, or
* {@link #CANCELLED}. */
public var state :int = PRE_GAME;
/** Indicates whether or not this game is rated. */
public var isRated :Boolean;
/** Indicates whether the game is "private". */
public var isPrivate :Boolean;
/** The usernames of the players involved in this game. */
public var players :TypedArray; /* of Name */
/** Whether each player in the game is a winner, or null if the game is not yet over. */
public var winners :TypedArray; /* of Boolean */
/** A unique identifier for each game session. Every time the game is started, this value will
* be incremented to provide a unique identifier for that particular session. */
public var sessionId :int;
/** If null, indicates that all present players are active, or for more complex games can be
* non-null to indicate the current status of each player in the game. The status value is one
* of {@link #PLAYER_LEFT_GAME} or {@link #PLAYER_IN_PLAY}. */
public var playerStatus :TypedArray; /* of int */
/**
* Returns the number of players in the game.
*/
public function getPlayerCount () :int
{
var count :int = 0;
var size :int = players.length;
for (var ii :int = 0; ii < size; ii++) {
if (players[ii] != null) {
count++;
}
}
return count;
}
/**
* Returns the number of active players in the game.
*/
public function getActivePlayerCount () :int
{
var count :int = 0;
var size :int = players.length;
for (var ii :int = 0; ii < size; ii++) {
if (isActivePlayer(ii)) {
count++;
}
}
return count;
}
/**
* Returns whether the given player is still an active player in the game. (Ie. whether or not
* they are still participating.)
*/
public function isActivePlayer (pidx :int) :Boolean
{
return isOccupiedPlayer(pidx) &&
(playerStatus == null || isActivePlayerStatus(playerStatus[pidx]));
}
/**
* Returns the player index of the given user in the game, or <code>-1</code> if the player is
* not involved in the game.
*/
public function getPlayerIndex (username :Name) :int
{
return Arrays.indexOf(players, username);
}
/**
* Returns whether the game is in play. A game that is not in play could either be awaiting
* players, ended, or cancelled.
*/
public function isInPlay () :Boolean
{
return (state == IN_PLAY);
}
/**
* Returns whether the given player index in the game is occupied.
*/
public function isOccupiedPlayer (pidx :int) :Boolean
{
return (pidx >= 0 && pidx < players.length) && (players[pidx] != null);
}
/**
* Returns whether the given player index is a winner, or false if the winners are not yet
* assigned.
*/
public function isWinner (pidx :int) :Boolean
{
return (winners != null) && winners[pidx];
}
/**
* Returns the number of winners for this game, or <code>0</code> if the winners array is not
* populated, e.g., the game is not yet over.
*/
public function getWinnerCount () :int
{
var count :int = 0;
if (winners != null) {
for (var ii :int = 0; ii < winners.length; ii++) {
if (winners[ii]) {
count++;
}
}
}
return count;
}
/**
* Returns true if the game is ended in a draw.
*/
public function isDraw () :Boolean
{
return getWinnerCount() == getPlayerCount();
}
/**
* Returns the winner index of the first winning player for this game, or <code>-1</code> if
* there are no winners or the winners array is not yet assigned. This is only likely to be
* useful for games that are known to have a single winner.
*/
public function getWinnerIndex () :int
{
return Arrays.indexOf(winners, true);
}
/**
* Used by {@link #isActivePlayer} to determine if the supplied status is associated with an
* active player (one that has not resigned from the game and/or left the game room).
*/
protected function isActivePlayerStatus (playerStatus :int) :Boolean
{
return playerStatus == PLAYER_IN_PLAY;
}
override protected function whichJoiner (j :Joiner) :void
{
super.whichJoiner(j);
j.addArgs("(" + players.join() + ")", state);
}
// // AUTO-GENERATED: METHODS START
// /**
// * Requests that the <code>state</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.
// */
// public function setState (value :int) :void
// {
// var ovalue :int = this.state;
// requestAttributeChange(
// STATE, Integer.valueOf(value), Integer.valueOf(ovalue));
// this.state = value;
// }
//
// /**
// * Requests that the <code>isRated</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.
// */
// public function setIsRated (value :Boolean) :void
// {
// var ovalue :Boolean = this.isRated;
// requestAttributeChange(
// IS_RATED, langBoolean.valueOf(value), langBoolean.valueOf(ovalue));
// this.isRated = value;
// }
//
// /**
// * Requests that the <code>isPrivate</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.
// */
// public function setIsPrivate (value :Boolean) :void
// {
// var ovalue :Boolean = this.isPrivate;
// requestAttributeChange(
// IS_PRIVATE, langBoolean.valueOf(value),
// langBoolean.valueOf(ovalue));
// this.isPrivate = value;
// }
//
// /**
// * Requests that the <code>players</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.
// */
// public function setPlayers (value :TypedArray) :void
// {
// var ovalue :TypedArray = this.players;
// requestAttributeChange(
// PLAYERS, value, ovalue);
// this.players = (value == null) ? null : (value.clone() as TypedArray);
// }
//
// /**
// * Requests that the <code>index</code>th element of
// * <code>players</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.
// */
// public function setPlayersAt (value :Name, index :int) :void
// {
// var ovalue :Name = (this.players[index] as Name);
// requestElementUpdate(
// PLAYERS, index, value, ovalue);
// this.players[index] = value;
// }
//
// /**
// * Requests that the <code>winners</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.
// */
// public function setWinners (value :TypedArray) :void
// {
// var ovalue :TypedArray = this.winners;
// requestAttributeChange(
// WINNERS, value, ovalue);
// this.winners = (value == null) ? null : (value.clone() as TypedArray);
// }
//
// /**
// * Requests that the <code>index</code>th element of
// * <code>winners</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.
// */
// public function setWinnersAt (value :Boolean, index :int) :void
// {
// var ovalue :Boolean = (this.winners[index] as Boolean);
// requestElementUpdate(
// WINNERS, index, langBoolean.valueOf(value),
// langBoolean.valueOf(ovalue));
// this.winners[index] = value;
// }
//
// /**
// * Requests that the <code>sessionId</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.
// */
// public function setSessionId (value :int) :void
// {
// var ovalue :int = this.sessionId;
// requestAttributeChange(
// SESSION_ID, Integer.valueOf(value), Integer.valueOf(ovalue));
// this.sessionId = value;
// }
//
// /**
// * Requests that the <code>playerStatus</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.
// */
// public function setPlayerStatus (value :TypedArray) :void
// {
// var ovalue :TypedArray = this.playerStatus;
// requestAttributeChange(
// PLAYER_STATUS, value, ovalue);
// this.playerStatus = (value == null) ? null
// : (value.clone() as TypedArray);
// }
//
// /**
// * Requests that the <code>index</code>th element of
// * <code>playerStatus</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.
// */
// public function setPlayerStatusAt (value :int, index :int) :void
// {
// var ovalue :int = (this.playerStatus[index] as int);
// requestElementUpdate(
// PLAYER_STATUS, index, Integer.valueOf(value),
// Integer.valueOf(ovalue));
// this.playerStatus[index] = value;
// }
// // AUTO-GENERATED: METHODS END
//
// override public function writeObject (out :ObjectOutputStream) :void
// {
// super.writeObject(out);
//
// out.writeObject(gameService);
// out.writeInt(state);
// out.writeBoolean(isRated);
// out.writeBoolean(isPrivate);
// out.writeObject(players);
// out.writeField(winners);
// out.writeInt(sessionId);
// out.writeField(playerStatus);
// }
override public function readObject (ins :ObjectInputStream) :void
{
super.readObject(ins);
state = ins.readInt();
isRated = ins.readBoolean();
isPrivate = ins.readBoolean();
players = TypedArray(ins.readObject());
winners = (ins.readField(TypedArray.getJavaType(Boolean)) as TypedArray);
sessionId = ins.readInt();
playerStatus = (ins.readField(TypedArray.getJavaType(int)) as TypedArray);
}
}
}
@@ -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.game.data {
import com.threerings.util.Name;
public class UserIdentifier
{
/**
* Get the user id for the specified user, or 0 if they're not valid.
*/
public static function getUserId (name :Name) :int
{
return (_userIder == null) ? 0 : _userIder(name);
}
public static function setIder (userIder :Function) :void
{
_userIder = userIder;
}
protected static var _userIder :Function;
}
}
@@ -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.turn.client {
import com.threerings.util.Name;
/**
* Games that wish to make use of the turn game services should have their
* controller implement this interface and create an instance of {@link
* TurnGameControllerDelegate} which should be passed to {@link
* GameController#addDelegate}.
*/
public interface TurnGameController
{
/**
* Called when the turn changed. This indicates the start of a turn
* and the user interface should adjust itself accordingly (activating
* controls if it is our turn and deactivating them if it is not).
*
* @param turnHolder the username of the new holder of the turn.
*/
function turnDidChange (turnHolder :Name) :void;
}
}
@@ -0,0 +1,156 @@
//
// $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.turn.client {
import com.threerings.util.Name;
import com.threerings.presents.dobj.AttributeChangeListener;
import com.threerings.presents.dobj.AttributeChangedEvent;
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.client.GameController;
import com.threerings.parlor.game.client.GameControllerDelegate;
import com.threerings.parlor.game.data.GameObject;
import com.threerings.parlor.turn.data.TurnGameObject;
/**
* Performs the client-side processing for a turn-based game. Games which
* wish to make use of these services must construct a delegate and call
* out to it at the appropriate times (see the method documentation for
* which methods should be called when). The game's controller must also
* implement the {@link TurnGameController} interface so that it can be
* notified when turn-based game events take place.
*/
public class TurnGameControllerDelegate extends GameControllerDelegate
implements AttributeChangeListener
{
/** A special value used to communicate to the client that the current
* turn holder was replaced (perhaps due to disconnection or departure
* and being replaced by an AI). */
public static const TURN_HOLDER_REPLACED :Name =
new Name("__TURN_HOLDER_REPLACED__");
/**
* Constructs a delegate which will call back to the supplied {@link
* TurnGameController} implementation wen turn-based game related
* things happen.
*/
public function TurnGameControllerDelegate (tgctrl :TurnGameController)
{
super(GameController(tgctrl));
// keep this around for later
_tgctrl = tgctrl;
}
/**
* Returns true if the game is in progress and it is our turn; false
* otherwise.
*/
public function isOurTurn () :Boolean
{
var self :BodyObject =
(_ctx.getClient().getClientObject() as BodyObject);
return (_gameObj.isInPlay() &&
self.getVisibleName().equals(_turnGame.getTurnHolder()));
}
/**
* Returns the index of the current turn holder as configured in the
* game object.
*
* @return the index into the players array of the current turn holder
* or -1 if there is no current turn holder.
*/
public function getTurnHolderIndex () :int
{
return _gameObj.getPlayerIndex(_turnGame.getTurnHolder());
}
// documentation inherited
override public function init (ctx :CrowdContext, config :PlaceConfig) :void
{
_ctx = ctx;
}
// documentation inherited
override public function willEnterPlace (plobj :PlaceObject) :void
{
// get a casted reference to the object
_gameObj = (plobj as GameObject);
_turnGame = (plobj as TurnGameObject);
_thfield = _turnGame.getTurnHolderFieldName();
// and add ourselves as a listener
plobj.addListener(this);
}
// documentation inherited
override public function didLeavePlace (plobj :PlaceObject) :void
{
// remove our listenership
plobj.removeListener(this);
// clean up
_turnGame = null;
}
// documentation inherited
public function attributeChanged (event :AttributeChangedEvent) :void
{
// handle turn changes
if (event.getName() == _thfield) {
var name :Name = (event.getValue() as Name);
var oname :Name = (event.getOldValue() as Name);
if (TURN_HOLDER_REPLACED.equals(name) ||
TURN_HOLDER_REPLACED.equals(oname)) {
// small hackery: ignore the turn holder being set to
// TURN_HOLDER_REPLACED as it means that we're replacing
// the current turn holder rather than switching turns;
// also ignore the new turn holder when we switch from THR
// to a real name again
} else {
_tgctrl.turnDidChange(name);
}
}
}
/** The turn game controller for whom we are delegating. */
protected var _tgctrl :TurnGameController;
/** A reference to our client context. */
protected var _ctx :CrowdContext;
/** A reference to our game object. */
protected var _gameObj :GameObject;
/** A casted reference to our game object as a turn game. */
protected var _turnGame :TurnGameObject;
/** The name of the turn holder field. */
protected var _thfield :String;
}
}
@@ -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.turn.data {
import com.threerings.io.TypedArray;
import com.threerings.util.Name;
/**
* Games that wish to support turn-based play must implement this
* interface with their {@link GameObject}.
*/
public interface TurnGameObject
{
/**
* Returns the distributed object field name of the
* <code>turnHolder</code> field in the object that implements this
* interface.
*/
function getTurnHolderFieldName () :String;
/**
* Returns the username of the player who is currently taking their
* turn in this turn-based game or <code>null</code> if no user
* currently holds the turn.
*/
function getTurnHolder () :Name;
// /**
// * Requests that the <code>turnHolder</code> field be set to the specified
// * value.
// */
// function setTurnHolder (turnHolder :Name) :void;
/**
* Returns the array of player names involved in the game.
*/
function getPlayers () :TypedArray /* of Name */;
/**
* Returns true if the game is in play, false if not. If a game is not in
* play after a turn has ended, the next turn will not be started.
*/
function isInPlay () :Boolean;
}
}
@@ -0,0 +1,39 @@
//
// $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.util {
import com.threerings.crowd.util.CrowdContext;
import com.threerings.parlor.client.ParlorDirector;
/**
* The parlor context provides access to the various managers, etc. that
* are needed by the parlor client code.
*/
public interface ParlorContext extends CrowdContext
{
/**
* Returns a reference to the parlor director.
*/
function getParlorDirector () :ParlorDirector;
}
}