Updated parts of server/ dir to google docstrings as per #709.

This commit is contained in:
Griatch 2015-06-22 21:02:03 +02:00
parent abff559a61
commit b2ddd34efd
7 changed files with 382 additions and 85 deletions

View file

@ -75,15 +75,27 @@ class AmpServerFactory(protocol.ServerFactory):
"""
def __init__(self, server):
"""
server: The Evennia server service instance
protocol: The protocol the factory creates instances of.
Initialize the factory.
Args:
server (Server): The Evennia server service instance.
protocol (Protocol): The protocol the factory creates
instances of.
"""
self.server = server
self.protocol = AMPProtocol
def buildProtocol(self, addr):
"""
Start a new connection, and store it on the service object
Start a new connection, and store it on the service object.
Args:
addr (str): Connection address. Not used.
Returns:
protocol (Protocol): The created protocol.
"""
#print "Evennia Server connected to Portal at %s." % addr
self.server.amp_protocol = AMPProtocol()
@ -95,6 +107,7 @@ class AmpClientFactory(protocol.ReconnectingClientFactory):
"""
This factory creates an instance of the Portal, an AMPProtocol
instances to use to connect
"""
# Initial reconnect delay in seconds.
initialDelay = 1
@ -102,12 +115,24 @@ class AmpClientFactory(protocol.ReconnectingClientFactory):
maxDelay = 1
def __init__(self, portal):
"""
Initializes the client factory.
Args:
portal (Portal): Portal instance.
"""
self.portal = portal
self.protocol = AMPProtocol
def startedConnecting(self, connector):
"""
Called when starting to try to connect to the MUD server.
Args:
connector (Connector): Twisted Connector instance representing
this connection.
"""
pass
#print 'AMP started to connect:', connector
@ -115,6 +140,10 @@ class AmpClientFactory(protocol.ReconnectingClientFactory):
def buildProtocol(self, addr):
"""
Creates an AMPProtocol instance when connecting to the server.
Args:
addr (str): Connection address. Not used.
"""
#print "Portal connected to Evennia server at %s." % addr
self.resetDelay()
@ -125,6 +154,12 @@ class AmpClientFactory(protocol.ReconnectingClientFactory):
def clientConnectionLost(self, connector, reason):
"""
Called when the AMP connection to the MUD server is lost.
Args:
connector (Connector): Twisted Connector instance representing
this connection.
reason (str): Eventual text describing why connection was lost.
"""
if hasattr(self, "server_restart_mode"):
self.maxDelay = 1
@ -137,6 +172,12 @@ class AmpClientFactory(protocol.ReconnectingClientFactory):
def clientConnectionFailed(self, connector, reason):
"""
Called when an AMP connection attempt to the MUD server fails.
Args:
connector (Connector): Twisted Connector instance representing
this connection.
reason (str): Eventual text describing why connection failed.
"""
if hasattr(self, "server_restart_mode"):
self.maxDelay = 1
@ -150,7 +191,8 @@ class AmpClientFactory(protocol.ReconnectingClientFactory):
class MsgPortal2Server(amp.Command):
"""
Message portal -> server
Message Portal -> Server
"""
key = "MsgPortal2Server"
arguments = [('hashid', amp.String()),
@ -163,7 +205,8 @@ class MsgPortal2Server(amp.Command):
class MsgServer2Portal(amp.Command):
"""
Message server -> portal
Message Server -> Portal
"""
key = "MsgServer2Portal"
arguments = [('hashid', amp.String()),
@ -176,11 +219,11 @@ class MsgServer2Portal(amp.Command):
class ServerAdmin(amp.Command):
"""
Portal -> Server
Administration Portal -> Server
Sent when the portal needs to perform admin operations on the
server, such as when a new session connects or resyncs
Sent when the portal needs to perform admin
operations on the server, such as when a new
session connects or resyncs
"""
key = "ServerAdmin"
arguments = [('hashid', amp.String()),
@ -193,10 +236,11 @@ class ServerAdmin(amp.Command):
class PortalAdmin(amp.Command):
"""
Server -> Portal
Administration Server -> Portal
Sent when the server needs to perform admin operations on the
portal.
Sent when the server needs to perform admin
operations on the portal.
"""
key = "PortalAdmin"
arguments = [('hashid', amp.String()),
@ -209,11 +253,11 @@ class PortalAdmin(amp.Command):
class FunctionCall(amp.Command):
"""
Bidirectional
Bidirectional Server <-> Portal
Sent when either process needs to call an arbitrary function in
the other. This does not use the batch-send functionality.
Sent when either process needs to call an
arbitrary function in the other. This does
not use the batch-send functionality.
"""
key = "FunctionCall"
arguments = [('module', amp.String()),
@ -224,7 +268,7 @@ class FunctionCall(amp.Command):
response = [('result', amp.String())]
# Helper functions
# Helper functions for pickling.
dumps = lambda data: to_str(pickle.dumps(to_str(data), pickle.HIGHEST_PROTOCOL))
loads = lambda data: pickle.loads(to_str(data))
@ -237,19 +281,22 @@ loads = lambda data: pickle.loads(to_str(data))
class AMPProtocol(amp.AMP):
"""
This is the protocol that the MUD server and the proxy server
communicate to each other with. AMP is a bi-directional protocol, so
both the proxy and the MUD use the same commands and protocol.
communicate to each other with. AMP is a bi-directional protocol,
so both the proxy and the MUD use the same commands and protocol.
AMP specifies responder methods here and connect them to
amp.Command subclasses that specify the datatypes of the
input/output of these methods.
AMP specifies responder methods here and connect them to amp.Command
subclasses that specify the datatypes of the input/output of these methods.
"""
# helper methods
def __init__(self, *args, **kwargs):
"""
Initialize protocol with some things that need to be
in place already before connecting both on portal and server.
Initialize protocol with some things that need to be in place
already before connecting both on portal and server.
"""
self.min_batch_step = 1.0 / BATCH_RATE
self.lastsend = time()
@ -259,13 +306,13 @@ class AMPProtocol(amp.AMP):
def connectionMade(self):
"""
This is called when a connection is established
between server and portal. AMP calls it on both sides,
so we need to make sure to only trigger resync from the
portal side.
This is called when a connection is established between server
and portal. AMP calls it on both sides, so we need to make
sure to only trigger resync from the portal side.
"""
self.transport.setTcpNoDelay(True) # this makes for a factor x10 faster sends!
# this makes for a factor x10 faster sends!
self.transport.setTcpNoDelay(True)
if hasattr(self.factory, "portal"):
# only the portal has the 'portal' property, so we know we are
# on the portal side and can initialize the connection.
@ -280,7 +327,15 @@ class AMPProtocol(amp.AMP):
# Error handling
def errback(self, e, info):
"error handler, to avoid dropping connections on server tracebacks."
"""
Error callback.
Handles errors to avoid dropping connections on server tracebacks.
Args:
e (Failure): Deferred error instance.
info (str): Error string.
"""
e.trap(Exception)
print "AMP Error for %(info)s: %(e)s" % {'info': info,
'e': e.getErrorMessage()}
@ -289,8 +344,17 @@ class AMPProtocol(amp.AMP):
"""
This will batch data together to send fewer, large batches.
Args:
command (AMP Command): A protocol send command.
sessid (int): A unique Session id.
Kwargs:
force_direct: send direct
force_direct (bool): Send direct, without batching data.
Returns:
deferreds (list or None): A list of deferreds firing with
as batch parts get sent (or fails).
"""
#print "batch_send 1:", command, sessid
global _SENDBATCH
@ -343,6 +407,17 @@ class AMPProtocol(amp.AMP):
This will receive and unpack data sent as a batch. This both
handles too-long data as well as batch-sending very fast-
arriving commands.
Args:
hashid (str): Unique hash id representing this batch in
the cache buffer.
data (str): Data coming over the wire.
ipart (int): Index of this part of the batch (ipart/nparts)
nparts (int): Total number of parts in this batch.
Returns:
data (str or list): The received data.
"""
global _MSGBUFFER
if nparts == 1:
@ -364,11 +439,20 @@ class AMPProtocol(amp.AMP):
def amp_msg_portal2server(self, hashid, data, ipart, nparts):
"""
Relays message to server. This method is executed on the Server.
Relays message to server. This method is executed on the
Server.
Since AMP has a limit of 65355 bytes per message, it's
possible the data comes in multiple chunks; if so (nparts>1)
we buffer the data and wait for the remaining parts to arrive
before continuing.
Args:
hashid (str): Unique hash identifying this data batch.
data (str): Data to send (often a part of a batch)
ipart (int): Index of this part of the batch.
nparts (int): Total number of batches.
Since AMP has a limit of 65355 bytes per message, it's possible the
data comes in multiple chunks; if so (nparts>1) we buffer the data
and wait for the remaining parts to arrive before continuing.
"""
batch = self.batch_recv(hashid, data, ipart, nparts)
for (sessid, kwargs) in batch:
@ -382,6 +466,15 @@ class AMPProtocol(amp.AMP):
def call_remote_MsgPortal2Server(self, sessid, msg, data=""):
"""
Access method called by the Portal and executed on the Portal.
Args:
sessid (int): Unique Session id.
msg (str): Message to send over the wire.
data (str, optional): Optional data.
Returns:
deferred (Deferred): Asynchronous return.
"""
#print "msg portal->server (portal side):", sessid, msg, data
return self.batch_send(MsgPortal2Server, sessid,
@ -393,6 +486,18 @@ class AMPProtocol(amp.AMP):
def amp_msg_server2portal(self, hashid, data, ipart, nparts):
"""
Relays message to Portal. This method is executed on the Portal.
Since AMP has a limit of 65355 bytes per message, it's
possible the data comes in multiple chunks; if so (nparts>1)
we buffer the data and wait for the remaining parts to arrive
before continuing.
Args:
hashid (str): Unique hash identifying this data batch.
data (str): Data to send (often a part of a batch)
ipart (int): Index of this part of the batch.
nparts (int): Total number of batches.
"""
batch = self.batch_recv(hashid, data, ipart, nparts)
for (sessid, kwargs) in batch:
@ -406,6 +511,18 @@ class AMPProtocol(amp.AMP):
def amp_batch_server2portal(self, hashid, data, ipart, nparts):
"""
Relays batch data to Portal. This method is executed on the Portal.
Since AMP has a limit of 65355 bytes per message, it's
possible the data comes in multiple chunks; if so (nparts>1)
we buffer the data and wait for the remaining parts to arrive
before continuing.
Args:
hashid (str): Unique hash identifying this data batch.
data (str): Data to send (often a part of a batch)
ipart (int): Index of this part of the batch.
nparts (int): Total number of batches.
"""
batch = self.batch_recv(hashid, data, ipart, nparts)
if batch is not None:
@ -418,7 +535,13 @@ class AMPProtocol(amp.AMP):
def call_remote_MsgServer2Portal(self, sessid, msg, data=""):
"""
Access method called by the Server and executed on the Server.
Send Message - access method called by the Server and executed on the Server.
Args:
sessid (int): Unique Session id.
msg (str): Message to send over the wire.
data (str, optional): Extra data.
"""
#print "msg server->portal (server side):", sessid, msg, data
return self.batch_send(MsgServer2Portal, sessid, msg=msg, data=data)
@ -429,6 +552,17 @@ class AMPProtocol(amp.AMP):
This allows the portal to perform admin
operations on the server. This is executed on the Server.
Since AMP has a limit of 65355 bytes per message, it's
possible the data comes in multiple chunks; if so (nparts>1)
we buffer the data and wait for the remaining parts to arrive
before continuing.
Args:
hashid (str): Unique hash identifying this data batch.
data (str): Data to send (often a part of a batch)
ipart (int): Index of this part of the batch.
nparts (int): Total number of batches.
"""
#print "serveradmin (server side):", hashid, ipart, nparts
batch = self.batch_recv(hashid, data, ipart, nparts)
@ -465,7 +599,15 @@ class AMPProtocol(amp.AMP):
def call_remote_ServerAdmin(self, sessid, operation="", data=""):
"""
Access method called by the Portal and Executed on the Portal.
Administrative access method called by the Portal and Executed
on the Portal.
Args:
sessid (int): Session id.
operation (char, optional): Identifier for the server operation, as defined by the
global variables in `evennia/server/amp.py`.
data (str, optional): Data going into the adminstrative operation.
"""
#print "serveradmin (portal side):", sessid, ord(operation), data
if hasattr(self.factory, "server_restart_mode"):
@ -478,6 +620,18 @@ class AMPProtocol(amp.AMP):
"""
This allows the server to perform admin
operations on the portal. This is executed on the Portal.
Since AMP has a limit of 65355 bytes per message, it's
possible the data comes in multiple chunks; if so (nparts>1)
we buffer the data and wait for the remaining parts to arrive
before continuing.
Args:
hashid (str): Unique hash identifying this data batch.
data (str): Data to send (often a part of a batch)
ipart (int): Index of this part of the batch.
nparts (int): Total number of batches.
"""
#print "portaladmin (portal side):", sessid, ord(operation), data
batch = self.batch_recv(hashid, data, ipart, nparts)
@ -519,7 +673,17 @@ class AMPProtocol(amp.AMP):
def call_remote_PortalAdmin(self, sessid, operation="", data=""):
"""
Access method called by the server side.
Administrative access method called by the Server side and executed
onthe Portal.
Args:
sessid (int): Session id.
operation (char, optional): Identifier for the server
operation, as defined by the global variables in
`evennia/server/amp.py`.
data (str, optional): Data going into the adminstrative
operation.
"""
if operation == SSYNC:
return self.batch_send(PortalAdmin, sessid, force_direct=True, operation=operation, data=data)
@ -529,8 +693,18 @@ class AMPProtocol(amp.AMP):
def amp_function_call(self, module, function, args, **kwargs):
"""
This allows Portal- and Server-process to call an arbitrary function
in the other process. It is intended for use by plugin modules.
This allows Portal- and Server-process to call an arbitrary
function in the other process. It is intended for use by
plugin modules.
Args:
module (str or module): The module containing the
`function` to call.
function (str): The name of the function to call in
`module`.
args, kwargs (any): These will be used as args/kwargs to
`function`.
"""
args = loads(args)
kwargs = loads(kwargs)
@ -561,6 +735,7 @@ class AMPProtocol(amp.AMP):
Returns:
A deferred that fires with the return value of the remote
function call
"""
return self.callRemote(FunctionCall,
module=modulepath,

View file

@ -315,6 +315,7 @@ ERROR_NODJANGO = \
def evennia_version():
"""
Get the Evennia version info from the main package.
"""
version = "Unknown"
try:
@ -338,6 +339,7 @@ def check_main_evennia_dependencies():
Returns:
not_error (bool): True if no dependency error was found.
"""
error = False
@ -381,6 +383,7 @@ def set_gamedir(path):
"""
Set GAMEDIR based on path, by figuring out where the setting file
is inside the directory tree.
"""
global GAMEDIR
@ -405,6 +408,7 @@ def set_gamedir(path):
def create_secret_key():
"""
Randomly create the secret key for the settings file
"""
import random
import string
@ -419,6 +423,7 @@ def create_settings_file():
"""
Uses the template settings file to build a working
settings file.
"""
settings_path = os.path.join(GAMEDIR, "server", "conf", "settings.py")
with open(settings_path, 'r') as f:
@ -441,6 +446,10 @@ def create_game_directory(dirname):
Initialize a new game directory named dirname
at the current path. This means copying the
template directory from evennia's root.
Args:
dirname (str): The directory name to create.
"""
global GAMEDIR
GAMEDIR = os.path.abspath(os.path.join(CURRENT_DIR, dirname))
@ -454,14 +463,20 @@ def create_game_directory(dirname):
def create_superuser():
"Create the superuser player"
"""
Create the superuser player
"""
print "\nCreate a superuser below. The superuser is Player #1, the 'owner' account of the server.\n"
django.core.management.call_command("createsuperuser", interactive=True)
def check_database():
"""
Check database exists
Check so the database exists.
Returns:
exists (bool): `True` if the database exists, otherwise `False`.
"""
# Check so a database exists and is accessible
from django.db import connection
@ -517,7 +532,11 @@ def check_database():
def getenv():
"""
Get current environment and add PYTHONPATH
Get current environment and add PYTHONPATH.
Returns:
env (dict): Environment global dict.
"""
sep = ";" if os.name == 'nt' else ":"
env = os.environ.copy()
@ -527,8 +546,14 @@ def getenv():
def get_pid(pidfile):
"""
Get the PID (Process ID) by trying to access
an PID file.
Get the PID (Process ID) by trying to access an PID file.
Args:
pidfile (str): The path of the pid file.
Returns:
pid (str): The process id.
"""
pid = None
if os.path.exists(pidfile):
@ -542,6 +567,10 @@ def del_pid(pidfile):
The pidfile should normally be removed after a process has
finished, but when sending certain signals they remain, so we need
to clean them manually.
Args:
pidfile (str): The path of the pid file.
"""
if os.path.exists(pidfile):
os.remove(pidfile)
@ -552,6 +581,15 @@ def kill(pidfile, signal=SIG, succmsg="", errmsg="", restart_file=SERVER_RESTART
Send a kill signal to a process based on PID. A customized
success/error message will be returned. If clean=True, the system
will attempt to manually remove the pid file.
Args:
pidfile (str): The path of the pidfile to get the PID from.
signal (int, optional): Signal identifier.
succmsg (str, optional): Message to log on success.
errmsg (str, optional): Message to log on failure.
restart_file (str, optional): Restart file location.
restart (bool, optional): Are we in restart mode or not.
"""
pid = get_pid(pidfile)
if pid:
@ -579,7 +617,14 @@ def kill(pidfile, signal=SIG, succmsg="", errmsg="", restart_file=SERVER_RESTART
def show_version_info(about=False):
"""
Display version info
Display version info.
Args:
about (bool): Include ABOUT info as well as version numbers.
Returns:
version_info (str): A complete version info string.
"""
import os, sys
import twisted
@ -595,10 +640,15 @@ def show_version_info(about=False):
def error_check_python_modules():
"""
Import settings modules in settings. This will raise exceptions on
pure python-syntax issues which are hard to catch gracefully
with exceptions in the engine (since they are formatting errors in
the python source files themselves). Best they fail already here
pure python-syntax issues which are hard to catch gracefully with
exceptions in the engine (since they are formatting errors in the
python source files themselves). Best they fail already here
before we get any further.
Raises:
DeprecationWarning: For trying to access various modules
(usually in `settings.py`) which are no longer supported.
"""
from django.conf import settings
def imp(path, split=True):
@ -661,8 +711,13 @@ def error_check_python_modules():
def init_game_directory(path, check_db=True):
"""
Try to analyze the given path to find settings.py - this defines
the game directory and also sets PYTHONPATH as well as the
django path.
the game directory and also sets PYTHONPATH as well as the django
path.
Args:
path (str): Path to new game directory, including its name.
check_db (bool, optional): Check if the databae exists.
"""
# set the GAMEDIR path
set_gamedir(path)
@ -758,8 +813,14 @@ def run_dummyrunner(number_of_dummies):
"""
Start an instance of the dummyrunner
The dummy players' behavior can be customized by adding a
dummyrunner_settings.py config file in the game's conf directory.
Args:
number_of_dummies (int): The number of dummy players to start.
Notes:
The dummy players' behavior can be customized by adding a
´dummyrunner_settings.py´ config file in the game's conf/
directory.
"""
number_of_dummies = str(int(number_of_dummies)) if number_of_dummies else 1
cmdstr = [sys.executable, EVENNIA_DUMMYRUNNER, "-N", number_of_dummies]
@ -773,8 +834,12 @@ def run_dummyrunner(number_of_dummies):
def list_settings(keys):
"""
Display the server settings. We only display
the Evennia specific settings here.
Display the server settings. We only display the Evennia specific
settings here. The result will be printed to the terminal.
Args:
keys (str or list): Setting key or keys to inspect.
"""
from importlib import import_module
from evennia.utils import evtable
@ -800,6 +865,7 @@ def list_settings(keys):
def run_menu():
"""
This launches an interactive menu.
"""
while True:
# menu loop
@ -864,10 +930,12 @@ def server_operation(mode, service, interactive, profiler):
"""
Handle argument options given on the command line.
mode - str; start/stop etc
service - str; server, portal or all
interactive - bool; use interactive mode or daemon
profiler - run the service under the profiler
Args:
mode (str): Start/stop/restart and so on.
service (str): "server", "portal" or "all".
interactive (bool). Use interactive mode or daemon.
profiler (bool): Run the service under the profiler.
"""
cmdstr = [sys.executable, EVENNIA_RUNNER]
@ -936,7 +1004,8 @@ def server_operation(mode, service, interactive, profiler):
def main():
"""
Run the evennia main program.
Run the evennia launcher main program.
"""
# set up argument parser

View file

@ -46,6 +46,7 @@ WARNING_POSTGRESQL_FIX = \
def create_config_values():
"""
Creates the initial config values.
"""
ServerConfig.objects.conf("site_name", settings.SERVERNAME)
ServerConfig.objects.conf("idle_timeout", settings.IDLE_TIMEOUT)
@ -53,6 +54,7 @@ def create_config_values():
def get_god_player():
"""
Creates the god user and don't take no for an answer.
"""
try:
god_player = PlayerDB.objects.get(id=1)
@ -64,6 +66,7 @@ def get_god_player():
def create_objects():
"""
Creates the #1 player and Limbo room.
"""
print " Creating objects (Player #1 and Limbo room) ..."
@ -122,6 +125,7 @@ def create_objects():
def create_channels():
"""
Creates some sensible default channels.
"""
print " Creating default channels ..."
@ -137,6 +141,7 @@ def at_initial_setup():
setup. Called very last in the sequence. It tries to import and
srun a module settings.AT_INITIAL_SETUP_HOOK_MODULE and will fail
silently if this does not exist or fails to load.
"""
modname = settings.AT_INITIAL_SETUP_HOOK_MODULE
if not modname:
@ -152,10 +157,11 @@ def at_initial_setup():
def reset_server():
"""
We end the initialization by resetting the server. This
makes sure the first login is the same as all the following
ones, particularly it cleans all caches for the special objects.
It also checks so the warm-reset mechanism works as it should.
We end the initialization by resetting the server. This makes sure
the first login is the same as all the following ones,
particularly it cleans all caches for the special objects. It
also checks so the warm-reset mechanism works as it should.
"""
from evennia.server.sessionhandler import SESSIONS
print " Initial setup complete. Restarting Server once."
@ -164,9 +170,14 @@ def reset_server():
def handle_setup(last_step):
"""
Main logic for the module. It allows for restarting
the initialization at any point if one of the modules
should crash.
Main logic for the module. It allows for restarting the
initialization at any point if one of the modules should crash.
Args:
last_step (int): The last stored successful step, for starting
over on errors. If `< 0`, initialization has finished and no
steps need to be redone.
"""
if last_step < 0:

View file

@ -6,22 +6,31 @@ from django.db import models
class ServerConfigManager(models.Manager):
"""
This ServerConfigManager implements methods for searching
and manipulating ServerConfigs directly from the database.
This ServerConfigManager implements methods for searching and
manipulating ServerConfigs directly from the database.
These methods will all return database objects
(or QuerySets) directly.
These methods will all return database objects (or QuerySets)
directly.
ServerConfigs are used to store certain persistent settings for the
server at run-time.
Evennia-specific:
conf
ServerConfigs are used to store certain persistent settings for
the server at run-time.
"""
def conf(self, key=None, value=None, delete=False, default=None):
"""
Access and manipulate config values
Add, retrieve and manipulate config values.
Args:
key (str, optional): Name of config.
value (str, optional): Data to store in this config value.
delete (bool, optional): If `True`, delete config with `key`.
default (str, optional): Use when retrieving a config value
by a key that does not exist.
Returns:
all (list): If `key` was not given - all stored config values.
value (str): If `key` was given, this is the stored value, or
`default` if no matching `key` was found.
"""
if not key:
return self.all()
@ -43,8 +52,13 @@ class ServerConfigManager(models.Manager):
def get_mysql_db_version(self):
"""
This is a helper method for getting the version string
of a mysql database.
This is a helper method for specifically getting the version
string of a MySQL database.
Returns:
mysql_version (str): The currently used mysql database
version.
"""
from django.db import connection
conn = connection.cursor()

View file

@ -30,8 +30,9 @@ class ServerConfig(WeakSharedMemoryModel):
On-the fly storage of global settings.
Properties defined on ServerConfig:
key - main identifier
value - value stored in key. This is a pickled storage.
- key: Main identifier
- value: Value stored in key. This is a pickled storage.
"""
@ -112,7 +113,12 @@ class ServerConfig(WeakSharedMemoryModel):
def store(self, key, value):
"""
Wrap the storage (handles pickling)
Wrap the storage.
Args:
key (str): The name of this store.
value (str): The data to store with this `key`.
"""
self.key = key
self.value = value

View file

@ -237,6 +237,7 @@ def oob_report(session, *args, **kwargs):
Notes:
When the property updates, the monitor will send a MSDP_ARRAY
to the session of the form `(SEND, fieldname, new_value)`
Examples:
("REPORT", "CHARACTER_NAME")
("MSDP_TABLE", "CHARACTER_NAME", "Amanda")
@ -268,6 +269,11 @@ def oob_return_field_report(session, fieldname, obj, *args, **kwargs):
changes. It is not part of the official MSDP specification but is
a callback used by the monitor to format the result before sending
it on.
Args:
session (Session): The Session object controlling this oob function.
fieldname (str): The name of the Field to report on.
"""
session.msg(oob=("MSDP_TABLE", (),
{fieldname: to_str(getattr(obj, fieldname), force_string=True)}))
@ -283,6 +289,11 @@ def oob_return_attribute_report(session, fieldname, obj, *args, **kwargs):
This command is not part of the official MSDP specification but is
a callback used by the monitor to format the result before sending
it on.
Args:
session (Session): The Session object controlling this oob function.
fieldname (str): The name of the Attribute to report on.
"""
session.msg(oob=("MSDP_TABLE", (),
{obj.db_key: to_str(getattr(obj, fieldname), force_string=True)}))
@ -292,6 +303,10 @@ def oob_return_attribute_report(session, fieldname, obj, *args, **kwargs):
def oob_unreport(session, *args, **kwargs):
"""
This removes tracking for the given data.
Args:
session (Session): Session controling this command.
"""
obj = session.get_puppet_or_player()
if obj:
@ -330,6 +345,7 @@ def oob_list(session, mode, *args, **kwargs):
Examples:
oob in: LIST COMMANDS
oob out: (COMMANDS, (SEND, REPORT, LIST, ...)
"""
mode = mode.upper()
if mode == "COMMANDS":

View file

@ -43,6 +43,7 @@ class OOBFieldMonitor(object):
the update() method w ill be called by the
save mechanism, which in turn will call the
user-customizable func()
"""
def __init__(self, obj):
"""
@ -50,14 +51,19 @@ class OOBFieldMonitor(object):
Args:
obj (Object): object handler is defined on.
"""
self.obj = obj
self.subscribers = defaultdict(list)
def __call__(self, fieldname):
"""
Called by the save() mechanism when the given
field has updated.
Called by the save() mechanism when the given field has
updated.
Args:
fieldname (str): The field to monitor
"""
for sessid, oobtuples in self.subscribers.items():
# oobtuples is a list [(oobfuncname, args, kwargs), ...],
@ -73,12 +79,13 @@ class OOBFieldMonitor(object):
Args:
sessid (int): Session id
oobfuncname (str): oob command to call when field updates
args,kwargs: arguments to pass to oob commjand
args,kwargs (any): arguments to pass to oob commjand
Notes:
Each sessid may have a list of (oobfuncname, args, kwargs)
tuples, all of which will be executed when the
field updates.
"""
self.subscribers[sessid].append((oobfuncname, args, kwargs))
@ -88,7 +95,6 @@ class OOBFieldMonitor(object):
Args:
sessid(int): Session id
Keyword Args:
oobfuncname (str, optional): Only delete this cmdname.
If not given, delete all.