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-APIservice..get_scope(): Registered procedure at{scope.topic}.get_scopereturning 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: Adictthat 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: TheGamewith which this scope belongs..child_scopes: AScopeManagerof 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: atxaiologging 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.Gamemodelservices.games.Phasemodelservices.games.Rolemodelservices.games.Runmodelservices.games.Worldmodelservices.games.RunUsermodelservices.games.Scenariomodelservices.games.Periodmodelservices.games.Decisionmodelservices.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’sPhases..roles: A list of the game’sRoless..runs: A list of the game’sRuns.
modelservices.games.Run
In addition to the methods inherited from Scope, Run has the following methods:
.advance_phase(): Subscribed method to make theRunadvance 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 theRunrollback 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 ofWorlds within thisRun.runusers: A list ofRunUserss 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’sScenarioss..runusers: A list of the World’sRunUsers.
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’sScenarioss..leader: Flags whether the RunUser is a leader or a player in thisRun..role: The RunUser’sRoleif the user is a player in thisRun..world: The RunUser’sWorldif the user is a player in thisRunand 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 theWorldto which the Scenario belongs orNoneif the Scenario doesn’t belong to aWorld..runuser: returns theRunuserto which the Scenario belongs orNoneif 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’sDecisionss..results: A list of the Period’sResults..scenario: returns theScenarioto which the Period belongs.
modelservices.games.Decision
In addition to the properties inherited from Scope, Decision has the following properties:
.period: returns thePeriodto 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 thePeriodto which the Result belongs..role: returns the Result’sRole.
