Volity

Python: module volity.actor
 
 
volity.actor
index
volity/actor.py

 
Modules
       
volity.bot
zymb.jabber.interface
zymb.jabber
logging
zymb.jabber.rpc
zymb.sched
volity.volent
zymb

 
Classes
       
zymb.jabber.rpc.MethodOpset(zymb.jabber.rpc.Opset)
BotAdminOpset
BotVolityOpset
Seat
volity.volent.VolEntity(zymb.sched.Agent)
Actor
zymb.jabber.rpc.WrapperOpset(zymb.jabber.rpc.Opset)
WrapGameOpset

 
class Actor(volity.volent.VolEntity)
    Actor: The implementation of a Volity bot.
 
An Actor is dedicated to playing a particular kind of game. However,
there is no game-specific code in Actor. That is all in a subclass of
the Bot class. (So an Actor contains a Bot subclass, just as a Referee
contains a Game subclass.)
 
Actor is a Jabber client (although it is not a subclass of the
Zymb Jabber client agent; instead, it creates one as a subsidiary
object). It is created by a Referee when someone requests a new bot
at the table. The Actor acts as a normal player. It has a direct
reference to the Referee, but mostly it works by sending out RPCs,
the same as a real player.
 
Actor(referee, jid, password, muc, resource, basenick, botclass) --
    constructor.
 
All of the constructor arguments are passed in from the Referee. First
is the referee itself. The *jid*, *password*, and *resource* are used
to connect to the Jabber server. (The Actor connects with the same
parameters as the original Referee, except for the *resource* string,
which is unique for each Actor.) The *muc* is the JID of the Jabber
Multi-User Chat room. The *basenick* is a suggestion for the Actor's
room nickname (although it will take a different one if that is taken).
And finally, *botclass* is the Python class which implements the game-
specific code for the bot -- the brain, as it were.
 
The Actor has no volition in sitting or standing. It waits for a player
to seat it. Once it's seated, it is promiscuously ready -- it sends
a ready() RPC after every table state change that could possibly leave
it unready. Many of these RPCs will fail (e.g., if the seats aren't all
taken yet) but it's the easiest way to make sure that the game will
start as soon as all the humans are ready.
 
Agent states and events:
 
state 'start': Initial state. Start up the Jabber connection process.
    When Jabber is fully connected, jump to 'ready'.
state 'ready': Jumps to state 'joining', which is where the interesting
    stuff starts.
state 'joining': Watch for success or failure of the MUC join process.
    If there is a nickname conflict, try again with a different nickname.
    If the join succeeds, jump to 'running'.
event 'tryjoin': Carry out one attempt to join the MUC.
state 'running': At this point the Actor is considered to be ready.
    Send a send_state() RPC, requesting the table state.
state 'end': The connection is closed.
 
Significant fields:
 
jid -- the JID by which this Referee is connected.
conn -- the Jabber client agent.
referee -- the Referee which created this Actor.
bot -- the Bot object.
refstate -- the referee state (STATE_SETUP, etc).
parlor -- the Parlor which created that Referee.
seat -- the Seat that the bot is sitting in, or None.
seats -- dict mapping seat IDs to Seat objects.
seatlist -- list of Seat objects in the game's order.
 
Internal methods:
 
beginwork() -- 'ready' state handler.
tryjoin() -- 'tryjoin' event handler.
endwork() -- 'end' state handler.
handleseatlist() -- seat list handler.
handlerequiredseatlist() -- required seat list handler.
handlereceivestate() -- state recovery handler.
tryready() -- tell the referee that we are ready to play.
begingame() -- game-start handler.
endgame() -- game-end handler.
suspendgame() -- game-suspend handler.
unsuspendgame() -- game-resume handler.
sendref() -- send an RPC to the referee.
defaultcallback() -- generic RPC callback, used to catch errors.
handlemessage() -- handle a Jabber message stanza.
handlepresence() -- handle a Jabber presence stanza.
handlemucpresence() -- handle a Jabber MUC presence stanza.
 
 
Method resolution order:
Actor
volity.volent.VolEntity
zymb.sched.Agent

Methods defined here:
__init__(self, referee, jid, password, muc, resource, basenick, botclass)
begingame(self)
begingame() -> None
 
Game-start handler. Sets up all the internal state which we will
use to track the game. (Game-specific setup is handled by the
bot's begingame() method.)
beginwork(self)
beginwork() -> None
 
The 'ready' state handler. Jumps to state 'joining', which is where
the interesting stuff starts.
defaultcallback(self, tup)
defaultcallback(tup) -> None
 
Generic RPC callback, used to catch errors.
 
When an RPC completes (in any sense), the Zymb RPC-sending service
calls a completion routine. By default, it's this one. The callback
invokes sched.Deferred.extract(tup) on its *tup* argument to extract
the RPC outcome. This may return a value, or one of the following
exceptions might be raised:
 
    TimeoutException: The RPC timed out.
    RPCFault: The RPC response was an RPC fault.
    StanzaError: The response was a Jabber stanza-level error.
    Exception: Something else went wrong.
 
The defaultcallback() method logs all exceptions, ignores all normal
RPC responses, and that's all it does.
endgame(self)
endgame() -> None
 
Game-end handler. (Game-specific shutdown is handled by the bot's
endgame() method.)
endwork(self)
endwork() -> None
 
The 'end' state handler. This does its best to blow away all of
the actor's member fields, so that everything can be garbage-
collected efficiently.
handlemessage(self, msg)
handlemessage(msg) -> <stanza outcome>
 
Handle a Jabber message stanza. The Actor does not react to messages,
so this just raises StanzaHandled to end processing for the stanza.
handlemucpresence(self, typestr, resource, msg)
handlemucpresence(typestr, resource, msg) -> None
 
Handle a Jabber MUC presence stanza. The *typestr* is the presence
type (with '' representing default "I'm here" presence). The
*resource* is the resource part of the MUC JID -- that is to say,
the MUC nick. The *msg* is the full presence stanza, which we
need for further analysis.
 
This watches for one condition: the successful joining of the MUC.
When it sees that, it jumps to 'running', and sends off the request
for the table state.
handlepresence(self, msg)
handlepresence(msg) -> <stanza outcome>
 
Handle a Jabber presence stanza. If it signals a failure to join the
MUC, perform 'tryjoin' again. If it comes from the actor's MUC room,
it is passed along to handlemucpresence(). Otherwise, it is ignored.
handlereceivestate(self, dic)
handlereceivestate(dic) -> None
 
State-recovery handler. (Game-specific work is handled by the
bot's receivestate() method.)
handlerequiredseatlist(self, ls)
handlerequiredseatlist(seats) -> None
 
Required seat list handler. This marks the given seats as required,
and marks all other seats as optional.
handleseatlist(self, ls)
handleseatlist(seats) -> None
 
Seat list handler. This accepts the list of game seats as sent by
the referee. It creates Seat objects, and fills in the seats dict
and the seatlist array.
sendref(self, methname, *methargs, **keywords)
sendref(methname, *methargs, **keywords) -> None
 
Send an RPC to the referee.
 
The *methname* and *methargs* describe the RPC. The *keywords* may
contain either or both of:
 
    timeout: How long to wait (in seconds) before considering the
        RPC to have failed.
    callback: A deferral callback to invoke when the outcome of
        the RPC is known. See the defaultcallback() method for an
        example of the callback model.
suspendgame(self)
suspendgame() -> None
 
Game-suspension handler. (Game-specific work is handled by the
bot's suspendgame() method.)
tryjoin(self)
tryjoin() -> None
 
The 'tryjoin' event handler. Begins an attempt to join the MUC.
 
This is fired off by the 'joining' event, and repeated if the MUC
join does not succeed.
tryready(self)
tryready() -> None
 
Tell the referee that we are ready to play. This is called after
every received RPC that could leave us unready. (That is, after most
of the volity.* namespace RPCs.) If we are seated, we send out a
ready() RPC. It may fail, but if we can possibly be ready, we should
be ready. Bots exist to play.
 
One exception: if we have just stood up, we block ready-sending until
the stand command is actually confirmed. Otherwise we could get into
race conditions, where we are in the process of standing up but
accidentally go ready anyway.
unsuspendgame(self)
unsuspendgame() -> None
 
Game-resumption handler. (Game-specific work is handled by the
bot's unsuspendgame() method.)

Data and other attributes defined here:
logprefix = 'volity.actor'
volityrole = 'bot'

Methods inherited from volity.volent.VolEntity:
authed(self)
authed() -- handler for Jabber client 'authresource' state.
 
The Jabber client has finished connecting. Jump to 'ready'.
cappresencehook(self, msg)
cappresencehook() -- callback for presence stanza generation.
 
This hook is called whenever we generate a presence stanza. It adds
a JEP-0115 Entity Capabilities tag to the stanza.
finalize(self)
finalize() -- 'end' state handler.
 
Shut down the Jabber client agent.
rpcnotfound(self, msg, callname, callargs)
rpcnotfound() -- callback for RPC method-not-found case.
 
When an RPC arrives that we do not recognize, we want to respond
with an RPC fault. (As opposed to the default behavior, which is
an <item-not-found> stanza error.)
startpresence(self)
startpresence() -- 'ready' state handler.
 
Send out initial presence.
startup(self)
startup() -- 'start' state handler.
 
Start the Jabber client agent, and then wait for it to finish
connecting.

Static methods inherited from volity.volent.VolEntity:
uniquestamp()
uniquestamp() -> int
 
Generate a number which is unique within this process. It is
approximately a Unix timestamp, but incremented when necessary
to avoid repeats.

Data and other attributes inherited from volity.volent.VolEntity:
laststamp = 0

Methods inherited from zymb.sched.Agent:
__str__(self)
addcleanupaction(self, ac)
addcleanupaction(action) -> None
 
Add *action* to the list of actions to be removed when the agent
stops (i.e., jumps to the 'end' state).
 
This is intended for actions that an agent adds as handlers to
other agents. For example:
 
    ac = otheragent.addhandler('event', self.foreigneventhandler)
    # Ensure that ac is removed when *self* shuts down.
    addcleanupaction(ac)
 
You do not need to list pollers or socket-checkers as cleanup
actions. Nor do you need to list handlers that an agent adds
to itself. (All these are automatically removed when the agent
shuts down.) You also don't have to worry about the case of
*otheragent* shutting down; when that happens, all handlers added
to it are removed.
addhandler(self, state, op, *args)
addhandler(eventname, op, *args) -> action
 
Add a state or event handler. The *eventname* is a string naming
the state or event you want to handle. The *op* and *args* are
the operation to perform when that state or event occurs.
(See queueaction() for the use of *op* and *args*.)
 
Handlers are invoked in last-added-first-run order. If an agent
has a built-in handler, it is added first, so it runs last. Handlers
that you add on later run earlier.
 
Returns the Action. (You can cancel the handler by calling
ac.remove().)
addtimer(self, op, *args, **dic)
addtimer(op, *args, delay=num, when=num, periodic=bool) -> action
 
Add a timed event. You must supply exactly one of the keyword
arguments *delay* and *when*. A *when* is an absolute time,
expressed as a number of seconds since the epoch (e.g., the kind
of value returned by time.time()). A *delay* is a relative time,
expressed as a number of seconds in the future.
 
It is legal to give a time in the past (or a negative *delay*).
In that case, this is similar to queueaction() -- the operation
will occur very soon. (Although it's still a "low priority"
action.)
 
If you supply *periodic*=True, the event will occur repeatedly,
every *delay* seconds. (The interval is inferred if you give
*when*.) In this case, you may NOT give a negative *delay* or
a time in the past -- it is meaningless for a periodic event to
have a period less than or equal to zero.
 
You can also create a periodic timer by calling addtimer with
a PeriodicAction, which is a subclass of Action. This is slightly
more flexible; the interval between calls is specified when
you create the PeriodicAction, but the delay before the first
call may be specified with *delay* or *when*.
 
See queueaction() for the use of *op* and *args*.
 
Returns the Action. (You can cancel the timer by calling
ac.remove().)
basicerror(self, exc, agent)
basicerror(exc, agent) -> None
 
        A simple event handler for 'error'. This simply writes the
        received data to stdout:
        
            sys.stdout.write('Error: ' + str(agent) + ': ' + str(exc) + '
')
 
        This handler is not installed by default. You can enable it by
        calling:
        
            ag.addhandler('error', ag.basicerror)
jump(self, st, *exargs)
jump(state, *args) -> None
 
Jump to a different operating state. (If the agent is already
in the given state, nothing happens.)
 
This queues up all the state transition handlers registered
for the new state. If you provide *args*, they are passed to the
handlers when they are called.
perform(self, name, *exargs)
perform(eventname, *args) -> None
 
Trigger an event.
 
This queues up all the event handlers registered for the given
event. If you provide *args*, they are passed to the handlers when
they are called.
queueaction(self, op, *args)
queueaction(op, *args) -> action
 
Invoke an action. The action will be handed to the scheduler, which
will run it very soon.
 
("Very soon" means "before the next timed event or network message
is handled." However, if you queue several actions, they will be
handled in the order you queued them.)
 
You can pass any callable as *op*; the (optional) *args* will be
passed to it as arguments. You can also, if you like, create
an Action object and pass it as *op*. You can still pass *args*
in this case; they'll be appended to the arguments bound into
the Action.
 
Returns the Action.
registerpoll(self, op, *args, **dic)
registerpoll(op, *args, interval=num) -> action
 
Add a repeating activity check. *interval* is a (mandatory) keyword
argument; the scheduler will invoke your operation at least every
*interval* seconds. (And at least once per sched.process() call.)
 
See queueaction() for the use of *op* and *args*. Your operation
should not do any work except for absorbing input and queueing
actions. Typically, it will collect the input and then call
perform(), triggering an event, so that the agent's event
handlers can do something with the input.
 
Returns the Action. (You can cancel it by calling ac.remove().)
registersocket(self, fileno, op, *args)
registersocket(socket, op, *args) -> action
 
Add a socket-based activity check. The *socket* may be a socket
object, or a number representing a fileno, or any object which
provides a fileno() method. 
 
See queueaction() for the use of *op* and *args*. Your operation
should not do any work except for absorbing input and queueing
actions. Typically, it will collect the input and then call
perform(), triggering an event, so that the agent's event
handlers can do something with the input.
 
Returns the Action. (You can cancel it by calling ac.remove().)
shutdown(self)
shutdown() -- internal 'end' state handler. Do not call.
 
This is the final 'end' handler. It removes the agent from the
scheduler, and cancels all its remaining actions, timed events,
etc.
start(self)
start() -> None
 
Begin activity. The agent adds itself to the scheduler and moves
to the 'start' state. It does not actually begin doing work in
this call; that happens the next time the scheduler is invoked
(sched.process).
 
You should only call this once per agent.
stop(self)
stop() -> None
 
Stop activity. The agent moves to the 'end' state. Its 'end'
state handlers will take care of shutting it down and removing
it from the scheduler.
 
It is safe to call this on an agent which is already in 'end',
or which is completely shut down. (Nothing further will happen.)
 
(Remember, state handlers are not called as part of the stop()
method. They are run by the scheduler "soon" after stop() is
called. If you want to be sure the agent is completely stopped,
you can check the agent.live field. However, it is better to
register your own 'end' state handler, and consider the agent
stopped when that it called.)

Data and other attributes inherited from zymb.sched.Agent:
agentcounter = 1
hidden = False

 
class BotAdminOpset(zymb.jabber.rpc.MethodOpset)
    BotAdminOpset: The Opset which responds to admin.* namespace
RPCs.
 
BotAdminOpset(act) -- constructor.
 
The *act* is the Actor to which this Opset is attached.
 
Methods:
 
precondition() -- checks authorization before an RPC handler.
 
Handler methods:
 
rpc_status() -- return assorted status information.
rpc_shutdown() -- immediately shut down this Actor.
 
 
Method resolution order:
BotAdminOpset
zymb.jabber.rpc.MethodOpset
zymb.jabber.rpc.Opset

Methods defined here:
__init__(self, act)
precondition(self, sender, namehead, nametail, *callargs)
precondition(sender, namehead, nametail, *callargs)
 
Checks authorization before an RPC handler. Only the admin JID
is permitted to send admin.* namespace RPCs. If no admin JID was
set, then all admin.* RPCs are rejected.
rpc_players(self, sender, *args)
rpc_players() -> dict
 
Return the actor's view of the players in each seat.
rpc_shutdown(self, sender, *args)
rpc_shutdown() -> str
 
Immediately shut down this Actor.
rpc_status(self, sender, *args)
rpc_status() -> dict
 
Return assorted status information. This returns a Jabber-RPC struct
containing these fields:
 
    referee: The JID of the bot's referee.
    refstate: The bot's view of the referee state.
    seat: The seat ID the bot is sitting in, or ''.
    gameseatlist: The list of seats which are involved in the current
        game, if there is one.

Methods inherited from zymb.jabber.rpc.MethodOpset:
__call__(self, sender, callname, *callargs)
__call__(sender, callname, *callargs) -> value
 
Accept, parse, and handle a call. The *sender* is a JID object.
The *callname* is the method name (or fraction of a name, if
the opset is nested inside another opset which parsed the
namespace). The *callargs* are the RPC arguments, in the form
of Python objects (ints, strings, etc).
 
This method looks for an instance method named 'rpc_'+*callname*.
It invokes this (after calling
precondition(sender, callname, '', *callargs). The third argument
is empty because, in a MethodOpset, there is no more of the
callname to be matched after the method lookup.)

 
class BotVolityOpset(zymb.jabber.rpc.MethodOpset)
    BotVolityOpset: An Opset which handles all RPCs in the volity.*
namespace.
 
The methods of this Opset invoke appropriate methods in the Actor.
However, if an RPC is not found, no RPC fault is generated; instead,
this just returns success. (In other words, not all volity.* RPCs
are listed below.)
 
The Opset also checks a few universal conditions: only the referee can
send RPCs, and only after the Actor is ready to receive them.
 
BotVolityOpset(act) -- constructor.
 
The *act* is the Actor to which this Opset is attached.
 
Methods:
 
precondition() -- checks authorization and argument types before an
RPC handler.
 
Handler methods:
 
rpc_player_sat() -- notice that a player has sat (or changed seats).
rpc_player_stood() -- notice that a player has stood.
rpc_player_unready() -- notice that a player is unready.
rpc_kill_game() -- notice that the kill-game flag has changed.
rpc_show_table() -- notice that the show-table flag has changed.
rpc_record_games() -- notice that record-games flag has changed.
rpc_language() -- notice that the table language has changed.
rpc_start_game() -- notice that the game has started.
rpc_end_game() -- notice that the game has ended.
rpc_suspend_game() -- notice that the game has been suspended.
rpc_resume_game() -- notice that the game has been resumed.
 
 
Method resolution order:
BotVolityOpset
zymb.jabber.rpc.MethodOpset
zymb.jabber.rpc.Opset

Methods defined here:
__call__(self, sender, callname, *callargs)
__call__(sender, callname, *callargs) -> <rpc outcome>
 
Invoke an RPC. This is invoked by the Zymb RPC-handling service.
The CallNotFound exception is trapped and turned into a silent
success.
__init__(self, act)
precondition(self, sender, namehead, nametail, *callargs)
precondition(sender, namehead, nametail, *callargs)
 
Check the sender before an RPC handler. Only referees can send RPCs
to a bot.
rpc_end_game(self, sender, *args)
rpc_kill_game(self, sender, *args)
rpc_language(self, sender, *args)
rpc_player_sat(self, sender, *args)
rpc_player_stood(self, sender, *args)
rpc_player_unready(self, sender, *args)
rpc_receive_state(self, sender, *args)
rpc_record_games(self, sender, *args)
rpc_required_seat_list(self, sender, *args)
rpc_resume_game(self, sender, *args)
rpc_seat_list(self, sender, *args)
rpc_show_table(self, sender, *args)
rpc_start_game(self, sender, *args)
rpc_suspend_game(self, sender, *args)

 
class Seat
    Seat: Represents the Actor's view of a table seat.
 
Seat(act, id) -- constructor.
 
The *act* is the Actor which owns this seat; *id* is the seat ID.
 
  Methods defined here:
__init__(self, act, id)

 
class WrapGameOpset(zymb.jabber.rpc.WrapperOpset)
    WrapGameOpset: An Opset which wraps all RPCs in the game.* namespace.
 
This Opset passes RPCs on to the Bot's chosen opset. However, if an
RPC is not found, the RPC fault is blocked; instead, this just returns
success.
 
This Opset provides checking of argument types. The Opset also checks a
few universal conditions: only the referee can send RPCs, and only after
the Actor is ready to receive them.
 
 
Method resolution order:
WrapGameOpset
zymb.jabber.rpc.WrapperOpset
zymb.jabber.rpc.Opset

Methods defined here:
__call__(self, sender, callname, *callargs)
__call__(sender, callname, *callargs) -> <rpc outcome>
 
Invoke an RPC. This is invoked by the Zymb RPC-handling service.
The CallNotFound exception is trapped and turned into a silent
success.
__init__(self, act, subopset)
precondition(self, sender, namehead, nametail, *callargs)
precondition(sender, namehead, nametail, *callargs)
 
Check the sender before an RPC handler. Only referees can send RPCs
to a bot.

Methods inherited from zymb.jabber.rpc.WrapperOpset:
getopset(self)
getopset() -> Opset
 
Retrieve the contained opset.
setopset(self, subopset)
setopset(subopset) -> None
 
Change the contained opset.

 
Data
        REFEREE_DEFAULT_RPC_TIMEOUT = 5
STATE_ABANDONED = 'abandoned'
STATE_ACTIVE = 'active'
STATE_DISRUPTED = 'disrupted'
STATE_SETUP = 'setup'
STATE_SUSPENDED = 'suspended'