Scopes
Every game is defined by building what we call the Scope tree when the game’s modelservice starts up.
The Scope tree starts at Game
, and is defined as follows:
The arity of each relationship is read by following the direction of the relationship line. For example, the arity of the Game-Run relationsip is 1:n – one game may be related to zero or more runs, but every run is related to exactly one game.
Because the Scope relationships are one-to-many, they can be viewed as forming a parent-to-children tree. Although an instance of the Scenario model could in theory be related to both a RunUser and a World, this does not occur in practice. Each Scenario Scope object has a single parent – either a RunUser or a World.
The modelservice.games
module provides all the base classes to build this tree.
Each of these classes is considered a Scope. The logic of your game will be implemented by subclassing
the necessary scope and adding your own custom logic.
Your game is defined by registering your top-level Scope with the modelservices.games.game
decorator.
Inside any Scope, you can make any method that returns a value callable from javascript by decorating it with the modelservice.games.register
decorator.
Similarly, you can use the modelservice.games.subscribe
decorator to subscribe any method to a topic:
from modelservice.games import Scenario, Game
from modelservice.games import register, subscribe
class ZeroSumScenario(Scenario):
players = {}
@register
def add(self, term1, term2, details):
return term1 + term2
@subscribe
def player_quit(self, player, reason, details):
self.players.pop(player['id'])
Game.register('zero-sum', [ZeroSumScenario])
In this example, ZeroSumScenario.add
will be registered at the uri {settings.ROOT_TOPIC}.models.{resource_name}.{scope_pk}.add
and ZeroSumScenario.player_quit
will subscribe to {settings.ROOT_TOPIC}.models.resource_name}.{scope_pk}.player_quit
.
Alternatively, the name that will be used for the topic can be passed to the decorator:
class ZeroSumScenario(Scenario):
@subscribe('player.quit')
def player_quit(self, player, reason):
self.players.pop(player['id'])
Game.register('zero-sum', [ZeroSumScenario])
ZeroSumScenario.player_quit
will now be subscribed to {settings.ROOT_TOPIC}.models.{resource_name}.{scope_pk}.player.quit
.
Further customization can be achieved by overriding the Scope.get_routing(name)
method.
Scope methods
All Scopes inherit from a common Scope
subclass and share these common methods among many others:
.get_routing(name)
: given a topic short name, returns the full topic path. This is to avoid repeating the same topic namespace over and over, and allow using shorter topic names..publish(topic, *args, **kwargs)
: Publishes a message on the specifiedtopic
. The actual topic name will be what is returned by.get_routing(topic)
.save()
: Persist the scope by submitting it to theSimpl-Games-API
service..get_scope()
: Registered procedure at{scope.topic}.get_scope
returning the scope’s serialized version. Call this procedure if you want to retrieve the scope’s state from the UI.get_scope_tree()
: Same as.get_scope
, but will also walk down the tree from scope, returning its children recursively..onConnected()
.onStop()
.onStart()
Scope properties
All Scopes inherits from a common Scope
subclass and share these common properties among many others:
.pk
: The scopes primary key.json
: Adict
that contains the serialized representation of the scope. This is what will be passed to the browser and to the Simpl-Games-API service. Any user-defined state for the scope should be stored in.json['data']
.game
: TheGame
with which this scope belongs..child_scopes
: AScopeManager
of the children scopes..games_client
: An instance of a REST API client for the Simpl-Games-API service..my
: This special property will contain scopes that are related to the scope. See Traversing Scopes for more details..log
: atxaio
logging instance. See “Logging” below.
Logging
To log from a Scope
, you can use the .log
attribute.
The best practice is to use a string with the r
formatting operator for your variables. For example, if you’d want to log the somevar
variable to the INFO
level, you’d use:
myscope.log.info("Something happened: {myvar!r}", myvar=somevar)
Concrete Classes
For convenience, the following scopes are defined in modelservices.games
:
modelservices.games.Game
modelservices.games.Phase
modelservices.games.Role
modelservices.games.Run
modelservices.games.World
modelservices.games.RunUser
modelservices.games.Scenario
modelservices.games.Period
modelservices.games.Decision
modelservices.games.Result
modelservices.games.Game
In addition to the methods inherited from Scope
, Game
has the following methods:
.get_phases()
: Registered procedure to fetch the game’s phases over WAMP..get_roles()
: Registered procedure to fetch the game’s roles over WAMP.
In addition to the properties inherited from Scope
, Game
has the following properties:
.phases
: A list of the game’sPhase
s..roles
: A list of the game’sRoles
s..runs
: A list of the game’sRun
s.
modelservices.games.Run
In addition to the methods inherited from Scope
, Run
has the following methods:
.advance_phase()
: Subscribed method to make theRun
advance to the next phase..on_advance_phase(next_phase)
: Override this method to perform custom logic right after the run is advanced to the next phase..rollback_phase()
: Subscribed method to make theRun
rollback to the previous phase..on_rollback_phase(previous_phase)
: Override this method to perform custom logic right after the run is rolled back to the previous phase..on_runuser_created(runuser_id)
: Override this method to perform custom logic right after a runuser has been added to the run..on_runuser_deleted(payload)
: Override this method to perform custom logic right after a runuser has been removed from the run.
In addition to the properties inherited from Scope
, Run
has the following properties:
.current_phase
: The Run’s current phase..worlds
: A list ofWorld
s within thisRun
.runusers
: A list ofRunUsers
s within thisRun
modelservices.games.World
In addition to the properties inherited from Scope
, World
has the following properties:
.run
: The Run to which this World belongs..scenarios
: A list of the World’sScenarios
s..runusers
: A list of the World’sRunUser
s.
modelservices.games.RunUser
In addition to the methods inherited from Scope
, RunUser
has the following methods:
.get_scenarios()
: Registered procedure to fetch the RunUser’s Scenarios over WAMP.
In addition to the properties inherited from Scope
, Run
has the following properties:
.run
: The Run to which this Runuser belongs..scenarios
: A list of the RunUser’sScenarios
s..leader
: Flags whether the RunUser is a leader or a player in thisRun
..role
: The RunUser’sRole
if the user is a player in thisRun
..world
: The RunUser’sWorld
if the user is a player in thisRun
and belongs to aWorld
.
modelservices.games.Scenario
In addition to the properties inherited from Scope
, Scenario
has the following properties:
.periods
: A list of the Scenario’sPeriod
’s..world
: returns theWorld
to which the Scenario belongs orNone
if the Scenario doesn’t belong to aWorld
..runuser
: returns theRunuser
to which the Scenario belongs orNone
if the Scenario doesn’t belong to aRunuser
.
modelservices.games.Period
In addition to the properties inherited from Scope
, Period
has the following properties:
.decisions
: A list of the Period’sDecisions
s..results
: A list of the Period’sResult
s..scenario
: returns theScenario
to which the Period belongs.
modelservices.games.Decision
In addition to the properties inherited from Scope
, Decision
has the following properties:
.period
: returns thePeriod
to which the Decision belongs..role
: returns the Decision’sRole
.
modelservices.games.Result
In addition to the properties inherited from Scope
, Result
has the following properties:
.period
: returns thePeriod
to which the Result belongs..role
: returns the Result’sRole
.