mirror of
https://github.com/evennia/evennia.git
synced 2026-03-28 18:47:16 +01:00
313 lines
11 KiB
Python
313 lines
11 KiB
Python
"""
|
|
Out-of-band default plugin commands available for OOB handler. This
|
|
follows the standards defined by the MSDP out-of-band protocol
|
|
(http://tintin.sourceforge.net/msdp/)
|
|
|
|
This module is pointed to by settings.OOB_PLUGIN_MODULES. All functions
|
|
(not classes) defined globally in this module will be made available
|
|
to the oob mechanism.
|
|
|
|
function execution - the oob protocol can execute a function directly on
|
|
the server. The available functions must be defined
|
|
as global functions via settings.OOB_PLUGIN_MODULES.
|
|
repeat func execution - the oob protocol can request a given function be
|
|
executed repeatedly at a regular interval. This
|
|
uses an internal script pool.
|
|
tracking - the oob protocol can request Evennia to track changes to
|
|
fields on objects, as well as changes in Attributes. This is
|
|
done by dynamically adding tracker-objects on entities. The
|
|
behaviour of those objects can be customized via
|
|
settings.OOB_PLUGIN_MODULES.
|
|
|
|
What goes into the OOB_PLUGIN_MODULES is a list of modules with input
|
|
for the OOB system.
|
|
|
|
oob functions have the following call signature:
|
|
function(caller, session, *args, **kwargs)
|
|
|
|
oob trackers should build upon the OOBTracker class in this module
|
|
module and implement a minimum of the same functionality.
|
|
|
|
a global function oob_error will be used as optional error management.
|
|
"""
|
|
from django.conf import settings
|
|
from src.utils.utils import to_str
|
|
_GA = object.__getattribute__
|
|
_SA = object.__setattr__
|
|
_NA = lambda o: (None, "N/A") # not implemented
|
|
|
|
# default properties defined by the MSDP protocol. These are
|
|
# used by the SEND oob function below. Each entry should point
|
|
# to a function that takes the relevant object as input and
|
|
# returns the data it is responsible for. Most of these
|
|
# are commented out, but kept for reference for each
|
|
# game to implement.
|
|
|
|
OOB_SENDABLE = {
|
|
## General
|
|
"CHARACTER_NAME": lambda o: ("db_key", o.key),
|
|
"SERVER_ID": lambda o: ("settings.SERVERNAME", settings.SERVERNAME),
|
|
#"SERVER_TIME": _NA,
|
|
## Character
|
|
#"AFFECTS": _NA,
|
|
#"ALIGNMENT": _NA,
|
|
#"EXPERIENCE": _NA,
|
|
#"EXPERIENCE_MAX": _NA,
|
|
#"EXPERIENCE_TNL": _NA,
|
|
#"HEALTH": _NA,
|
|
#"HEALTH_MAX": _NA,
|
|
#"LEVEL": _NA,
|
|
#"RACE": _NA,
|
|
#"CLASS": _NA,
|
|
#"MANA": _NA,
|
|
#"MANA_MAX": _NA,
|
|
#"WIMPY": _NA,
|
|
#"PRACTICE": _NA,
|
|
#"MONEY": _NA,
|
|
#"MOVEMENT": _NA,
|
|
#"MOVEMENT_MAX": _NA,
|
|
#"HITROLL": _NA,
|
|
#"DAMROLL": _NA,
|
|
#"AC": _NA,
|
|
#"STR": _NA,
|
|
#"INT": _NA,
|
|
#"WIS": _NA,
|
|
#"DEX": _NA,
|
|
#"CON": _NA,
|
|
## Combat
|
|
#"OPPONENT_HEALTH": _NA,
|
|
#"OPPONENT_HEALTH_MAX": _NA,
|
|
#"OPPONENT_LEVEL": _NA,
|
|
#"OPPONENT_NAME": _NA,
|
|
## World
|
|
#"AREA_NAME": _NA,
|
|
#"ROOM_EXITS": _NA,
|
|
#"ROOM_VNUM": _NA,
|
|
"ROOM_NAME": lambda o: ("db_location", o.db_location.key),
|
|
#"WORLD_TIME": _NA,
|
|
## Configurable variables
|
|
#"CLIENT_ID": _NA,
|
|
#"CLIENT_VERSION": _NA,
|
|
#"PLUGIN_ID": _NA,
|
|
#"ANSI_COLORS": _NA,
|
|
#"XTERM_256_COLORS": _NA,
|
|
#"UTF_8": _NA,
|
|
#"SOUND": _NA,
|
|
#"MXP": _NA,
|
|
## GUI variables
|
|
#"BUTTON_1": _NA,
|
|
#"BUTTON_2": _NA,
|
|
#"BUTTON_3": _NA,
|
|
#"BUTTON_4": _NA,
|
|
#"BUTTON_5": _NA,
|
|
#"GAUGE_1": _NA,
|
|
#"GAUGE_2": _NA,
|
|
#"GAUGE_3": _NA,
|
|
#"GAUGE_4": _NA,
|
|
#"GAUGE_5": _NA
|
|
}
|
|
# mapping for which properties may be tracked
|
|
OOB_REPORTABLE = OOB_SENDABLE
|
|
|
|
|
|
#------------------------------------------------------------
|
|
# Tracker classes
|
|
#
|
|
# Trackers are added to a given object's trackerhandler and
|
|
# reports back changes when they happen. They are managed using
|
|
# the oobhandler's track/untrack mechanism
|
|
#------------------------------------------------------------
|
|
|
|
class TrackerBase(object):
|
|
"""
|
|
Base class for OOB Tracker objects.
|
|
"""
|
|
def __init__(self, oobhandler, *args, **kwargs):
|
|
self.oobhandler = oobhandler
|
|
|
|
def update(self, *args, **kwargs):
|
|
"Called by tracked objects"
|
|
pass
|
|
|
|
def at_remove(self, *args, **kwargs):
|
|
"Called when tracker is removed"
|
|
pass
|
|
|
|
|
|
# Tracker objects stored on objects when using the MSDP report command
|
|
|
|
class ReportFieldTracker(TrackerBase):
|
|
"""
|
|
Tracker that passively sends data to a stored sessid whenever
|
|
a named database field changes. The TrackerHandler calls this with
|
|
the correct arguments.
|
|
"""
|
|
def __init__(self, oobhandler, fieldname, sessid, *args, **kwargs):
|
|
"""
|
|
name - name of entity to track, such as "db_key"
|
|
sessid - sessid of session to report to
|
|
"""
|
|
self.oobhandler = oobhandler
|
|
self.fieldname = fieldname
|
|
self.sessid = sessid
|
|
|
|
def update(self, new_value, *args, **kwargs):
|
|
"Called by cache when updating the tracked entitiy"
|
|
# use oobhandler to relay data
|
|
try:
|
|
# we must never relay objects across the amp, only text data.
|
|
new_value = new_value.key
|
|
except AttributeError:
|
|
new_value = to_str(new_value, force_string=True)
|
|
# this is a wrapper call for sending oob data back to session
|
|
self.oobhandler.msg(self.sessid, "report", self.fieldname,
|
|
new_value, *args, **kwargs)
|
|
|
|
|
|
class ReportAttributeTracker(TrackerBase):
|
|
"""
|
|
Tracker that passively sends data to a stored sessid whenever
|
|
the Attribute updates. Since the field here is always "db_key",
|
|
we instead store the name of the attribute to return.
|
|
"""
|
|
def __init__(self, oobhandler, fieldname, sessid, attrname, *args, **kwargs):
|
|
"""
|
|
attrname - name of attribute to track
|
|
sessid - sessid of session to report to
|
|
"""
|
|
self.oobhandler = oobhandler
|
|
self.attrname = attrname
|
|
self.sessid = sessid
|
|
|
|
def update(self, new_value, *args, **kwargs):
|
|
"Called by cache when attribute's db_value field updates"
|
|
try:
|
|
new_value = new_value.dbobj
|
|
except AttributeError:
|
|
new_value = to_str(new_value, force_string=True)
|
|
# this is a wrapper call for sending oob data back to session
|
|
self.oobhandler.msg(self.sessid, "report", self.attrname, new_value, *args, **kwargs)
|
|
|
|
|
|
#------------------------------------------------------------
|
|
# OOB commands
|
|
# This defines which internal server commands the OOB handler
|
|
# makes available to the client. These commands are called
|
|
# automatically by the OOB mechanism by triggering the
|
|
# oobhandlers's execute_cmd method with the cmdname and
|
|
# eventual args/kwargs. All functions defined globally in this
|
|
# module will be made available to call by the oobhandler. Use
|
|
# _funcname if you want to exclude one. To allow for python-names
|
|
# like "list" here, these properties are read as being case-insensitive.
|
|
#
|
|
# All OOB commands must be on the form
|
|
# cmdname(oobhandler, session, *args, **kwargs)
|
|
#------------------------------------------------------------
|
|
|
|
def oob_error(oobhandler, session, errmsg, *args, **kwargs):
|
|
"""
|
|
This is a special function called by the oobhandler when an error
|
|
occurs already at the execution stage (such as the oob function
|
|
not being recognized or having the wrong args etc).
|
|
"""
|
|
session.msg(oob=("send", {"ERROR": errmsg}))
|
|
|
|
def list(oobhandler, session, mode, *args, **kwargs):
|
|
"""
|
|
List available properties. Mode is the type of information
|
|
desired:
|
|
"COMMANDS" Request an array of commands supported
|
|
by the server.
|
|
"LISTS" Request an array of lists supported
|
|
by the server.
|
|
"CONFIGURABLE_VARIABLES" Request an array of variables the client
|
|
can configure.
|
|
"REPORTABLE_VARIABLES" Request an array of variables the server
|
|
will report.
|
|
"REPORTED_VARIABLES" Request an array of variables currently
|
|
being reported.
|
|
"SENDABLE_VARIABLES" Request an array of variables the server
|
|
will send.
|
|
"""
|
|
mode = mode.upper()
|
|
# the first return argument is treated by the msdp protocol as the
|
|
# name of the msdp array to return
|
|
if mode == "COMMANDS":
|
|
session.msg(oob=("list", ("COMMANDS",
|
|
"LIST",
|
|
"REPORT",
|
|
"UNREPORT",
|
|
# "RESET",
|
|
"SEND")))
|
|
elif mode == "LISTS":
|
|
session.msg(oob=("list", ("LISTS",
|
|
"REPORTABLE_VARIABLES",
|
|
"REPORTED_VARIABLES",
|
|
# "CONFIGURABLE_VARIABLES",
|
|
"SENDABLE_VARIABLES")))
|
|
elif mode == "REPORTABLE_VARIABLES":
|
|
session.msg(oob=("list", ("REPORTABLE_VARIABLES",) +
|
|
tuple(key for key in OOB_REPORTABLE.keys())))
|
|
elif mode == "REPORTED_VARIABLES":
|
|
session.msg(oob=("list", ("REPORTED_VARIABLES",) +
|
|
tuple(oobhandler.get_all_tracked(session))))
|
|
elif mode == "SENDABLE_VARIABLES":
|
|
session.msg(oob=("list", ("SENDABLE_VARIABLES",) +
|
|
tuple(key for key in OOB_REPORTABLE.keys())))
|
|
#elif mode == "CONFIGURABLE_VARIABLES":
|
|
# pass
|
|
else:
|
|
session.msg(oob=("list", ("unsupported mode",)))
|
|
|
|
|
|
def send(oobhandler, session, *args, **kwargs):
|
|
"""
|
|
This function directly returns the value of the given variable to the
|
|
session. vartype can be one of
|
|
"""
|
|
obj = session.get_puppet_or_player()
|
|
ret = {}
|
|
if obj:
|
|
for name in (a.upper() for a in args if a):
|
|
try:
|
|
key, value = OOB_SENDABLE.get(name, _NA)(obj)
|
|
ret[name] = value
|
|
except Exception, e:
|
|
ret[name] = str(e)
|
|
# return result
|
|
session.msg(oob=("send", ret))
|
|
|
|
|
|
def report(oobhandler, session, *args, **kwargs):
|
|
"""
|
|
This creates a tracker instance to track the data given in *args.
|
|
vartype is one of "prop" (database fields) or "attr" (attributes)
|
|
"""
|
|
obj = session.get_puppet_or_player()
|
|
if obj:
|
|
for name in (a.upper() for a in args if a):
|
|
key, val = OOB_REPORTABLE.get(name, _NA)(obj)
|
|
if key:
|
|
if key.startswith("db_"):
|
|
oobhandler.track_field(obj, session.sessid,
|
|
key, ReportFieldTracker)
|
|
else: # assume attribute
|
|
oobhandler.track_attribute(obj, session.sessid,
|
|
key, ReportAttributeTracker)
|
|
|
|
|
|
def unreport(oobhandler, session, vartype="prop", *args, **kwargs):
|
|
"""
|
|
This removes tracking for the given data given in *args.
|
|
vartype is one of of "prop" or "attr".
|
|
"""
|
|
obj = session.get_puppet_or_player()
|
|
if obj:
|
|
for name in (a.upper() for a in args if a):
|
|
key, val = OOB_REPORTABLE.get(name, _NA)
|
|
if key:
|
|
if key.startswith("db_"):
|
|
oobhandler.untrack_field(obj, session.sessid, key)
|
|
else: # assume attribute
|
|
oobhandler.untrack_attribute(obj, session.sessid, key)
|
|
|