Players¶
What is a player¶
Before writing a ‘player’ it’s best to understand what a ‘player’ is in the context of D20. A ‘Player’ in d20 is a class which provides the ability to react to ‘facts’ seen by the framework and using these fact either derive more facts or objects which can further the workflow of processing an object
Attack of the clones¶
When a fact is seen that a player has registered interest in, a player instance will be created, called a clone and will be instructed to work on that fact. What that means is that for every fact a new instance of a player will be launched. To maintain any state or relationship between clones, the console (below) must be used.
Getting started¶
Every Player must inherit from the PlayerTemplate
class and use the registerPlayer
decorator to register the player with the framework.
Further, if the player is not included in the base distribution its path must be included in the config for it to be found.
Although multiple players can be defined in a file, it might be cleaner to keep players in their own files.
The following is an example of a simple player:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | from d20.Manual.Templates import (PlayerTemplate,
registerPlayer)
from d20.Manual.Facts import *
@registerPlayer(
name="MyPlayer",
description="An example player",
creator="Me",
# The version of the player
# must conform to PEP440 version numbering
version="0.1",
# The minimum version of the game engine supported
# The game engine version conforms to PEP440 so this
# should be comparable, e.g., 0.1.0 and 0.1 are equivalent
engine_version="0.1",
help="This is just an example player",
interests=['hash'],
)
class MyPlayer(PlayerTemplate):
def __init__(self, **kwargs):
# PlayerTemplate registers the console as self.console
# Remember to init the parent class!!
super().__init__(**kwargs)
def handleFact(self, **kwargs):
"""A function to handle facts"""
def handleHyp(self, **kwargs):
"""A function to handle hyps"""
|
Interests¶
Every player must register their interests
to receive data they might find relevant. By default, if a player isn’t interested in dealing with hyps, they can register interest only in facts by simply passing a list
of types as the example code above does. If a player is also interested in hyps, they can pass a dict
with the keys facts
and hyps
. Any other keys in the dict
will be ignored.
The following snippet is an exampe of what it would look like
1 2 3 4 5 6 7 8 9 10 11 12 | @registerPlayer(
name="MyPlayer",
description="An example player",
creator="Me",
version="0.1",
engine_version="0.1",
help="This is just an example player",
interests={'facts': ['hash'],
'hyps': ['hash']},
)
class MyPlayer(PlayerTemplate):
"""Class Definition"""
|
The console¶
Every player instance instance has access to a console
in their class which provides a way of interacting with the framework.
The console
includes many convenience functions and calls to get more information when necessary.
The following are the functions available to your player from the console
:
requests¶
The console provides simple pre-configured access to a python requests
session instance which can be used to make web requests. This instance works
like a regular requests
instance:
..code-block:: python
r = self.console.requests.get(’http://google.com’) print(r.status_code)
requests configuration¶
You can configure the underlying behavior of requests by using the following two functions:
self.console.configureRequestsRetry()
self.console.configureRequestsSession()
print¶
Using the built in print
statement is generally a bad idea for any player.
If a player needs to print to the screen they can use the print function provided by the console which should act the same as the native python 3 print function.
self.console.print('Test')
Temporary directories¶
The console has the capability to create and provide temporary directories in case a player needs to store information on disk for any reason.
mydir = self.console.myDirectory
tmpdir = self.console.createTempDirectory()
The myDirectory
property returns a temporary player directory which should
not change during the life of a player instance. The createTempDirectory
function will return a new unique temporary directory every time when called.
Warning
The directories should be considered transient and should not include important information. The D20 save/load system does not take these directories into consideration, so import or presistent data should not be stored there.
Memory¶
The console provides locations where information may be stored to be used across player instances (clones) or within an clone.
foo = self.console.memory
foo = self.console.data
The memory
property of the console is player-wide memory which all clones share, whereas the data
property is specific to a clone.
Object Interaction¶
A player can interact with objects in the framework using the console:
Getting Objects¶
There are two functions to get objects. Use getObject
to get a specific object by id.
Use getAllObjects
to get a list of all objects
obj = self.console.getObject(0)
ojbs = self.console.getAllObjects()
Adding Objects¶
If your player has a new object to add to the framework, this can be
accomplished using the addObject
function
# assume object data is in variable 'data'
obj_id = self.console.addObject(data)
# The console returns the unique id of the object just added
Facts¶
A player can interact with facts in the framwork using the console:
Getting Facts¶
There are multiple ways to get facts from the framework.
To get a specific fact if the id is available, the getFact
function may be used.
If all facts of a given type is required, this can be accomplished via the getAllFacts
function.
f = getFact(0)
fs = getAllFacts('md5')
Adding Facts¶
If a player needs to add facts about an object, hyp (or another fact) it can do so using the addFact
function:
fact = MimeTypeFact(mimetype='application/javascript',
object_id=0)
self.console.addFact(fact)
Hyps¶
A player can interact with the hyps in the framework using the console:
Getting Hyps¶
There are multiple ways to get hyps from the framework. To get a specific
fact if the id is available, the getHyp
function may be used. If all hyps
of a given type is required, this can be accomplished via the
getAllHyps
function.
f = getHyp(0)
fs = getAllHyps('md5')
Adding Hyps¶
If a player needs to add hyps about an object, fact, or another hyp it can do
so using the addHyp
function:
fact = MimeTypeHyp(mimetype='application/javascript',
object_id=0)
self.console.addHyp(fact)
waitOn¶
A family of generator functions exist in the console to allow a player to wait/block until an object, fact, or hyp has been added to the framework by another player or npc based on certain conditions.
waitOnFacts¶
The waitOnFacts function will block until facts of a certain type are provided.
By default this will return all facts of the given types and then start blocking.
It is safer to do this than to use the only_latest
argument, as you might miss a fact having been added between calling waitOnFacts and its execution (aka a race condition).
for f in self.console.waitOnFacts('hash'):
# do stuff with f
break
waitOnHyps¶
The waitOnHyps function is the hyp version of the above waitOnFacts
function.
The same conditions and caveats apply.
for h in self.console.waitOnHyps('hash'):
# do stuff with h
break
waitOnChildFacts¶
The waitOnChildFacts function allows a player to wait on any facts of a given type that are children for a given object id, fact id, or hyp id. This enables a player to effectively watch the facts being added for a given object, fact, or hyp.
for f in self.console.waitOnChildFacts(object_id=0):
# do stuff with f
break
waitOnChildHyps¶
The waitOnChildHyps function allows a player to wait on any hyps of a given type that are children for a given object id, fact id, or hyp id. This enables a player to effectively watch the hyps being added for a given object, fact, or hyp.
for h in self.console.waitOnChildHyps(fact_id=0):
# do stuff with h
break
waitOnChildObjects¶
The waitOnChildObjects function allows a player to wait on any objects that are children for a given object id, fact id, or hyp id. This enables a player to effectively watch the objects being added for a given object, fact, or hyp.
for o in self.console.waitOnChildObjects(hyp_id=0):
# do stuff with o
break
waitTillFact¶
The waitTillFact
function will return a single fact of a given type or until timeout, if set
1 2 3 4 | try:
f = self.console.waitTillFact('md5', timeout=5)
except WaitTimeoutError as e:
pass
|
This function can be used to reliably wait for the ‘next’ fact to be submitted of a given type by utilizing the last_fact
argument
1 2 3 4 5 6 | fs = self.console.getAllFacts('hash')
# assumng len(fs) > 0
try:
f = self.console.waitTillFact('hash', timeout=1, last_fact=fs[-1].id)
except WaitTimeoutError as e:
pass
|