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