| Frivolity documentation | view source | Contained in the Frivolity distribution. |
Volity::Game - base class for Volity game modules
See Volity::Game::TicTacToe and its source code for a simple but full-featured example.
This class provides a framework for writing Volity game modules in Perl. A Volity game module will be a subclass of this class.
To turn your subclass into an active Volity parlor, you can pass it to
the volityd program via its game_class config option (see volityd).
To use this module, subclass it. Create your own Perl package for your
game, and have it inherit from Volity::Game. Then define game logic
and other behavior primarily by writing callback methods, as described in
"CALLBACK METHODS".
See Volity::Game::TicTacToe for a simple but complete example of a Volity::Game subclass.
Some things to keep in mind while writing your subclass:
The object that results from your class will be a Perl pseudohash that
makes use of the fields pragma. (See fields.) As the example
shows, you should declare the the instance variables you intend to use
with a use fields() invocation.
Other than that, an instance if your subclass will work just like a hash-based Perl object.
The Volity::Game base class constructor calls initialize() as a
final step.
If you override this method to peform game-specific initialization on
your subclass, it must have a return value of
$self->SUPER::initialize(@_).
These methods are used to set some general configuration information about the game, rather than specific information about any particular instance thereof.
A brief name of this game module, which the game server will use to advertise itself through service discovery and other means. If left undefined, a boring default value will be used (probably the JID that this server is running under.
A longer text description of this game module.
Required. The URI of the ruleset that this particular game module implements. Consult the core Volity documentation for more information on how this works: http://www.volity.org/wiki/index.cgi?Ruleset_URI
Required. The version number of the ruleset that this particular game module implements. A client-side UI file consults this number to determine its own compatibility with a game server.
An array reference of strings representing the IDs of all the seats that this game implementation supports. Example:
My::Game->seat_ids([qw(black white)]); my $seat_ids = My::Game->seat_ids; # $seat_ids is now ['black', 'white']
An array reference of strings representing the IDs of role-differentiated seats that players should be aware of, as defined by the ruleset.
The class that this game's seats belong to. When the game wants to make new seats, it calls this class's constructor.
It defaults to using the base Volity::Seat class.
Returns a list of all the seat objects currently at the table.
The objects will be instances of Volity::Seat, unless you specified
another class to use with the seat_class() method.
Returns a list of all the player objects currently at the table. This
includes all seated and standing players, and doesn't discriminate
between humans and bots. (Call methods such as seat() and
is_bot() on the resulting objects to help you sort out which is
which.)
The objects will be of the Volity::Player class.
If the game is still being set up, returns falsehood. If the game has already started, returns truth.
If the game is in play but suspended (as a result of a player asking the ref to suspend the game, or the ref reacting to a player's sudden departure), this returns 1. Otherwise, returns 0.
Convenience method: If the game is afoot and not suspended, returns truth. Otherwise, returns falsehood.
This is an accessor to an internal list of seat IDs representing the
game's turn order. Sets the list if called with arguments, and returns
it in any case. If set, has the side effect of setting the value of
the current_seat instance variable to the seat whose ID is first on
the list.
However, if any of the provided seat IDs aren't those of known seats, you'll get a fatal error here.
This is mainly useful if you plan on using the rotate_current_seat
method (see "Other object methods"), which is itself just a
convenience method for the common case of having a fixed,
round-the-table turn order, which not every game has.
Note that a game module implementing a ruleset that doesn't use turns won't use this.
Returns a list of the seats in play -- that is, seats that contained
players the last time the game started or resumed, with eliminated
seats filtered out. Each member of the list is an instance of
Volity::Seat or the sublcass your game module specified through the
seat_class accessor method.
This is distinct from the referee object's seats methods, which
returns all seats at the table, regardless of status or population.
Called with no arguments, returns the seat whose turn is up.
Called with a Volity::Seat object as an argument, sets that seat as the current seat, and then returns it. Called with anything else, it logs a warning and returns the current seat (regardless of your argument).
If you are making use of the turn_order list, setting a new current
seat does not affect the list, but it does advance the internal
turn-order pointer to this seat's position on it, if the seat is a
member of the list. Subsequently calling rotate_current_seat()
will advance the pointer to (and return) the seat that is after the
given one on the turn order list.
If the given seat doesn't exist in the turn order list (as is the case when the list is not defined), then the list remains unaffected.
Note that a game module implementing a ruleset that doesn't use turns needn't use this method at all.
Convenience method that simply sets the next seat in the turn order list, skipping over any eliminated seats. Returns that seat object.
If said list is empty, the current seat remains the same, and a warning is logged. If the list is not empty but the current seat is not a member of the list, then the pointer advances to the next player based on the last current player who was a member (or the first member if there weren't any) and you'll also get a warning because that's kind of weird, don't you think?
This method is useful to call at the end of a turn, at least in games
where the turn order is stable enough for the turn_order method to
be useful as well. Game modules can always advance the turn manually
by calling the current_player accessor with arguments. (And some
games don't have turns at all...)
Note that a game module implementing a ruleset that doesn't use turns won't use this.
Registers the given instance variables (which should be declared in your
subclass's use fields pragma) as holding game configuration information.
This will allow your game to accept RPC calls of the form
"game.$variable_name([args])" even when there is no game active. (The
referee normally kicks back such requests with an RPC fault.)
Normally you'll only call this method once, as part of your initialize()
method definition.
Returns this game's Volity::WinnersList object. If you want your game
do generate proper game records for storage with the Volity
bookkeeper, then you must use this object to specify the seats'
winning order before you call the game object's end method. See
Volity::WinnersList for the list object's API.
A convenience method for blasting a game.* call to all players at a table, seated and otherwise.
A convenience method for blasting a game.* call to every player at the table who is not seated.
A convenience method for blasting a game.* call to every seat, but not to players who are standing.
Ends the game. The referee will automatically handle seat notification. The bookkeeper will be sent a record of the game's results at this time, so be sure you have the game's winner-list arranged correctly.
Note that the balancing start method is actually a callback; see
"Callback methods".
You must define a callback in your subclass for every player-to-referee method defined in the ruleset that your module implements.
The name of the callback method will be exacty the same as the name of
the RPC, except with the "game." prefix replaced by "rpc_". So, for
example, the PRC "game.move_piece" would trigger the method
rpc_move_piece() in your subclass.
The first argument to the method (after the usual reference to the
object) is the Volity::Seat object that made the call, and any
remaining arguments are the arguments of the RPC itself. Therefore, if
the ruleset decrees that the arguments to game.move_piece are
piece_id and destination, then the first few lines of your
callback might look like this:
sub rpc_move_piece {
my $self = shift;
my ($seat, $piece_id, $destination) = @_;
# Game logic here....
return "volity.ok";
}
The callback's return value must be a Volity token. (See http://www.volity.org/wiki/index.cgi?Token). This will most commonly be a ruleset-defined error token to express a rejection of the caller's move or request, or a "volity.ok" token otherwise.
If the token takes additional arguments, simply add them to the return-value list after the token.
See Volity::Game::TicTacToe for an illustration of both successful
and errorful token returns, particularly in its rpc_mark() method.
Volity::Game provides default handlers for these methods, called on the
game object by different parts of Frivolity. You may override these methods
if you want your game module to behave in some way other than the default
(usually a no-op).
Called by the referee after it creates the game object and is ready to begin play. It gives the object a chance to perform whatever it would like to do as its first actions, prior to seats starting to send messages to it.
The default behavior is a no-op. A common reason to override this method is the need to send a set-up function call to the game's seats.
Called by the referee every time a player signals readiness. Returns 1 if the current configuration settings are OK to start a new game. Returns 0 if the config settings are currently wedged in an unplayable state.
In the latter case, the referee will not allow the player to declare readiness.
By default, it just returns 1 without checking anything.
Note: You don't need to check required-seat occupancy; this is handled for you, before has_acceptable_config is called.
This method should update the given player about the table's current
game-specific configuration, probably through a series of
$player->call_ui_function calls. The argument is the Volity::Player
object who needs to be brought up to speed.
As an example, imagine that your game implements a ruleset where
players can set a goal score before play. The referee-to-player RPC
that anncounces this option happens to be
game.goal_score($score). When a player joins a table running this
game, its client will request the current state, and will ultimately
fire the send_config_state_to_player($player) method on your game
object. So it's your responsibility to make sure that it responds by
calling $player->call_ui_function("goal_score", $self-goal_score)>,
assuming that your Game object has a field called goal_score that
holds this number.
By default, does nothing. If your game doesn't have configuation beyond the usual sit/stand/ready stuff, then you can probably get away with this default.
This method is not to be confused with the active game state, which is
requested through the send_game_state_to_player method, described
below.
If your game is complex enough to carry a state between turns (and it probably is), then you'll want to define this method. It will allow players who join the table after the game has started to learn about the game's current state all at once, either because they're observers wandering into the table or they're players returning to the table (perhaps after a network drop or crash on their end). It also allows bots who jump in to replace a vanished player to catch up on what they've missed.
The argument is the Volity::Player object who needs to be brought up to speed.
Important note: Use the state_seat method of the player object, not
the seat method, to see what the player's point of view is for purposes
of sending state. This always returns the last seat that the player sat in
while the game was active, preventing "accidental" snooping of other
seats' game states while the game is suspended.
This is called after the players at a table have suspended and then resumed the game. Since there's a chance that players have joined or switched seats since the last time the game was active, you may wish to override this method in order to update players' UIs. This is particularly true for games with private information, such as hands of cards.
You don't need to do anything else to implement game suspension or resumption; the base classes take care of everything for you, including updating the table's seat and player objects.
Overriding this method is optional; by default, it does nothing.
Jason McIntosh <jmac@jmac.org>
Copyright (c) 2003-2006 by Jason McIntosh.
| Frivolity documentation | view source | Contained in the Frivolity distribution. |