mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
Further cleanup of source; making class methods _private for clarity in the API.
This commit is contained in:
parent
fc156b5a54
commit
c8df141e89
18 changed files with 607 additions and 588 deletions
|
|
@ -4,7 +4,7 @@ Models for the comsystem.
|
|||
The comsystem's main component is the Message, which
|
||||
carries the actual information between two parties.
|
||||
Msgs are stored in the database and usually not
|
||||
deleted.
|
||||
deleted.
|
||||
A Msg always have one sender (a user), but can have
|
||||
any number targets, both users and channels.
|
||||
|
||||
|
|
@ -16,7 +16,7 @@ be able to delete connections on the fly).
|
|||
|
||||
from django.db import models
|
||||
from src.utils.idmapper.models import SharedMemoryModel
|
||||
from src.comms import managers
|
||||
from src.comms import managers
|
||||
from src.locks.lockhandler import LockHandler
|
||||
from src.utils import logger
|
||||
from src.utils.utils import is_iter, to_str
|
||||
|
|
@ -35,18 +35,18 @@ def obj_to_id(inp):
|
|||
"""
|
||||
dbref = is_dbref(inp)
|
||||
if dbref:
|
||||
return str(dbref)
|
||||
return str(dbref)
|
||||
if hasattr(inp, 'id'):
|
||||
return str(inp.id)
|
||||
if hasattr(inp, 'dbobj') and hasattr(inp.dbobj, 'id'):
|
||||
return str(inp.dbobj.id)
|
||||
return str(inp)
|
||||
return str(inp)
|
||||
|
||||
def id_to_obj(dbref, db_model='PlayerDB'):
|
||||
"""
|
||||
loads from dbref to object. Uses the db_model to search
|
||||
for the id.
|
||||
"""
|
||||
for the id.
|
||||
"""
|
||||
if db_model == 'PlayerDB':
|
||||
from src.players.models import PlayerDB as db_model
|
||||
else:
|
||||
|
|
@ -55,7 +55,7 @@ def id_to_obj(dbref, db_model='PlayerDB'):
|
|||
dbref = int(dbref.strip())
|
||||
return db_model.objects.get(id=dbref)
|
||||
except Exception:
|
||||
return None
|
||||
return None
|
||||
|
||||
#------------------------------------------------------------
|
||||
#
|
||||
|
|
@ -67,18 +67,18 @@ class Msg(SharedMemoryModel):
|
|||
"""
|
||||
A single message. This model describes all ooc messages
|
||||
sent in-game, both to channels and between players.
|
||||
|
||||
|
||||
The Msg class defines the following properties:
|
||||
sender - sender of message
|
||||
receivers - list of target objects for message
|
||||
channels - list of channels message was sent to
|
||||
message - the text being sent
|
||||
date_sent - time message was sent
|
||||
hide_from_sender - bool if message should be hidden from sender
|
||||
hide_from_sender - bool if message should be hidden from sender
|
||||
hide_from_receivers - list of receiver objects to hide message from
|
||||
hide_from_channels - list of channels objects to hide message from
|
||||
permissions - perm strings
|
||||
|
||||
|
||||
"""
|
||||
#
|
||||
# Msg database model setup
|
||||
|
|
@ -90,24 +90,24 @@ class Msg(SharedMemoryModel):
|
|||
# There must always be one sender of the message.
|
||||
db_sender = models.ForeignKey("players.PlayerDB", related_name='sender_set', null=True, verbose_name='sender')
|
||||
# in the case of external senders, no Player object might be available
|
||||
db_sender_external = models.CharField('external sender', max_length=255, null=True, blank=True,
|
||||
db_sender_external = models.CharField('external sender', max_length=255, null=True, blank=True,
|
||||
help_text="identifier for external sender, for example a sender over an IRC connection (i.e. someone who doesn't have an exixtence in-game).")
|
||||
# The destination objects of this message. Stored as a
|
||||
# comma-separated string of object dbrefs. Can be defined along
|
||||
# with channels below.
|
||||
db_receivers = models.CharField('receivers', max_length=255, null=True, blank=True,
|
||||
help_text='comma-separated list of object dbrefs this message is aimed at.')
|
||||
db_receivers = models.CharField('receivers', max_length=255, null=True, blank=True,
|
||||
help_text='comma-separated list of object dbrefs this message is aimed at.')
|
||||
# The channels this message was sent to. Stored as a
|
||||
# comma-separated string of channel dbrefs. A message can both
|
||||
# have channel targets and destination objects.
|
||||
db_channels = models.CharField('channels', max_length=255, null=True, blank=True,
|
||||
db_channels = models.CharField('channels', max_length=255, null=True, blank=True,
|
||||
help_text='comma-separated list of channel dbrefs this message is aimed at.')
|
||||
# The actual message and a timestamp. The message field
|
||||
# should itself handle eventual headers etc.
|
||||
# should itself handle eventual headers etc.
|
||||
db_message = models.TextField('messsage')
|
||||
db_date_sent = models.DateTimeField('date sent', editable=False, auto_now_add=True)
|
||||
# lock storage
|
||||
db_lock_storage = models.CharField('locks', max_length=512, blank=True,
|
||||
db_lock_storage = models.CharField('locks', max_length=512, blank=True,
|
||||
help_text='access locks on this message.')
|
||||
# These are settable by senders/receivers/channels respectively.
|
||||
# Stored as a comma-separated string of dbrefs. Can be used by the
|
||||
|
|
@ -117,9 +117,9 @@ class Msg(SharedMemoryModel):
|
|||
db_hide_from_receivers = models.CharField(max_length=255, null=True, blank=True)
|
||||
db_hide_from_channels = models.CharField(max_length=255, null=True, blank=True)
|
||||
# Storage of lock strings
|
||||
#db_lock_storage = models.TextField(null=True)
|
||||
|
||||
# Database manager
|
||||
#db_lock_storage = models.TextField(null=True)
|
||||
|
||||
# Database manager
|
||||
objects = managers.MsgManager()
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
|
@ -134,8 +134,8 @@ class Msg(SharedMemoryModel):
|
|||
# @property decorators that allows to access these fields using
|
||||
# normal python operations (without having to remember to save()
|
||||
# etc). So e.g. a property 'attr' has a get/set/del decorator
|
||||
# defined that allows the user to do self.attr = value,
|
||||
# value = self.attr and del self.attr respectively (where self
|
||||
# defined that allows the user to do self.attr = value,
|
||||
# value = self.attr and del self.attr respectively (where self
|
||||
# is the object in question).
|
||||
|
||||
# sender property (wraps db_sender)
|
||||
|
|
@ -183,7 +183,7 @@ class Msg(SharedMemoryModel):
|
|||
if is_iter(value):
|
||||
value = ",".join([obj_to_id(val) for val in value])
|
||||
self.db_receivers = obj_to_id(value)
|
||||
self.save()
|
||||
self.save()
|
||||
#@receivers.deleter
|
||||
def receivers_del(self):
|
||||
"Deleter. Allows for del self.receivers"
|
||||
|
|
@ -204,7 +204,7 @@ class Msg(SharedMemoryModel):
|
|||
if is_iter(value):
|
||||
value = ",".join([obj_to_id(val) for val in value])
|
||||
self.db_channels = obj_to_id(value)
|
||||
self.save()
|
||||
self.save()
|
||||
#@channels.deleter
|
||||
def channels_del(self):
|
||||
"Deleter. Allows for del self.channels"
|
||||
|
|
@ -253,7 +253,7 @@ class Msg(SharedMemoryModel):
|
|||
def hide_from_sender_set(self, value):
|
||||
"Setter. Allows for self.hide_from_senders = value."
|
||||
self.db_hide_from_sender = value
|
||||
self.save()
|
||||
self.save()
|
||||
#@hide_from_sender.deleter
|
||||
def hide_from_sender_del(self):
|
||||
"Deleter. Allows for del self.hide_from_senders"
|
||||
|
|
@ -274,7 +274,7 @@ class Msg(SharedMemoryModel):
|
|||
if is_iter(value):
|
||||
value = ",".join([obj_to_id(val) for val in value])
|
||||
self.db_hide_from_receivers = obj_to_id(value)
|
||||
self.save()
|
||||
self.save()
|
||||
#@hide_from_receivers.deleter
|
||||
def hide_from_receivers_del(self):
|
||||
"Deleter. Allows for del self.hide_from_receivers"
|
||||
|
|
@ -286,7 +286,7 @@ class Msg(SharedMemoryModel):
|
|||
#@property
|
||||
def hide_from_channels_get(self):
|
||||
"Getter. Allows for value = self.hide_from_channels. Returns a list of hide_from_channels."
|
||||
if self.db_hide_from_channels:
|
||||
if self.db_hide_from_channels:
|
||||
return [id_to_obj(dbref) for dbref in self.db_hide_from_channels.split(',')]
|
||||
return []
|
||||
#@hide_from_channels.setter
|
||||
|
|
@ -295,7 +295,7 @@ class Msg(SharedMemoryModel):
|
|||
if is_iter(value):
|
||||
value = ",".join([obj_to_id(val) for val in value])
|
||||
self.db_hide_from_channels = obj_to_id(value)
|
||||
self.save()
|
||||
self.save()
|
||||
#@hide_from_channels.deleter
|
||||
def hide_from_channels_del(self):
|
||||
"Deleter. Allows for del self.hide_from_channels"
|
||||
|
|
@ -304,7 +304,7 @@ class Msg(SharedMemoryModel):
|
|||
hide_from_channels = property(hide_from_channels_get, hide_from_channels_set, hide_from_channels_del)
|
||||
|
||||
# lock_storage property (wraps db_lock_storage)
|
||||
#@property
|
||||
#@property
|
||||
def lock_storage_get(self):
|
||||
"Getter. Allows for value = self.lock_storage"
|
||||
return self.db_lock_storage
|
||||
|
|
@ -321,15 +321,15 @@ class Msg(SharedMemoryModel):
|
|||
|
||||
db_model_name = "msg" # used by attributes to safely store objects
|
||||
|
||||
#
|
||||
#
|
||||
# Msg class methods
|
||||
#
|
||||
#
|
||||
|
||||
def __str__(self):
|
||||
"Print text"
|
||||
if self.channels:
|
||||
return "%s -> %s: %s" % (self.sender.key,
|
||||
", ".join([chan.key for chan in self.channels]),
|
||||
if self.channels:
|
||||
return "%s -> %s: %s" % (self.sender.key,
|
||||
", ".join([chan.key for chan in self.channels]),
|
||||
self.message)
|
||||
else:
|
||||
return "%s -> %s: %s" % (self.sender.key,
|
||||
|
|
@ -341,7 +341,7 @@ class Msg(SharedMemoryModel):
|
|||
accessing_obj - object trying to access this one
|
||||
access_type - type of access sought
|
||||
default - what to return if no lock of access_type was found
|
||||
"""
|
||||
"""
|
||||
return self.locks.check(accessing_obj, access_type=access_type, default=default)
|
||||
|
||||
|
||||
|
|
@ -353,61 +353,61 @@ class Msg(SharedMemoryModel):
|
|||
|
||||
class TempMsg(object):
|
||||
"""
|
||||
This is a non-persistent object for sending
|
||||
temporary messages that will not be stored.
|
||||
It mimics the "real" Msg object, but don't require
|
||||
sender to be given.
|
||||
|
||||
This is a non-persistent object for sending
|
||||
temporary messages that will not be stored.
|
||||
It mimics the "real" Msg object, but don't require
|
||||
sender to be given.
|
||||
|
||||
"""
|
||||
def __init__(self, sender=None, receivers=[], channels=[], message="", permissions=[]):
|
||||
self.sender = sender
|
||||
self.receivers = receivers
|
||||
self.receivers = receivers
|
||||
self.message = message
|
||||
self.permissions = permissions
|
||||
self.hide_from_sender = False
|
||||
self.hide_from_sender = receivers = False
|
||||
self.hide_from_channels = False
|
||||
self.hide_from_channels = False
|
||||
|
||||
#------------------------------------------------------------
|
||||
#
|
||||
# Channel
|
||||
#
|
||||
#------------------------------------------------------------
|
||||
|
||||
|
||||
class Channel(SharedMemoryModel):
|
||||
"""
|
||||
This is the basis of a comm channel, only implementing
|
||||
the very basics of distributing messages.
|
||||
|
||||
the very basics of distributing messages.
|
||||
|
||||
The Channel class defines the following properties:
|
||||
key - main name for channel
|
||||
desc - optional description of channel
|
||||
aliases - alternative names for the channel
|
||||
keep_log - bool if the channel should remember messages
|
||||
permissions - perm strings
|
||||
|
||||
|
||||
"""
|
||||
|
||||
|
||||
#
|
||||
# Channel database model setup
|
||||
#
|
||||
#
|
||||
# These databse fields are all set using their corresponding properties,
|
||||
# named same as the field, but withtout the db_* prefix.
|
||||
|
||||
|
||||
# unique identifier for this channel
|
||||
db_key = models.CharField('key', max_length=255, unique=True, db_index=True)
|
||||
# optional description of channel
|
||||
db_desc = models.CharField('description', max_length=80, blank=True, null=True)
|
||||
# optional description of channel
|
||||
db_desc = models.CharField('description', max_length=80, blank=True, null=True)
|
||||
# aliases for the channel. These are searched by cmdhandler
|
||||
# as well to determine if a command is the name of a channel.
|
||||
# Several aliases are separated by commas.
|
||||
db_aliases = models.CharField('aliases', max_length=255)
|
||||
# Several aliases are separated by commas.
|
||||
db_aliases = models.CharField('aliases', max_length=255)
|
||||
# Whether this channel should remember its past messages
|
||||
db_keep_log = models.BooleanField(default=True)
|
||||
# Storage of lock definitions
|
||||
db_lock_storage = models.CharField('locks', max_length=512, blank=True)
|
||||
|
||||
|
||||
# Database manager
|
||||
objects = managers.ChannelManager()
|
||||
|
||||
|
|
@ -416,15 +416,15 @@ class Channel(SharedMemoryModel):
|
|||
verbose_name = "Channel"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
SharedMemoryModel.__init__(self, *args, **kwargs)
|
||||
SharedMemoryModel.__init__(self, *args, **kwargs)
|
||||
self.locks = LockHandler(self)
|
||||
|
||||
|
||||
# Wrapper properties to easily set database fields. These are
|
||||
# @property decorators that allows to access these fields using
|
||||
# normal python operations (without having to remember to save()
|
||||
# etc). So e.g. a property 'attr' has a get/set/del decorator
|
||||
# defined that allows the user to do self.attr = value,
|
||||
# value = self.attr and del self.attr respectively (where self
|
||||
# defined that allows the user to do self.attr = value,
|
||||
# value = self.attr and del self.attr respectively (where self
|
||||
# is the object in question).
|
||||
|
||||
# key property (wraps db_key)
|
||||
|
|
@ -473,14 +473,14 @@ class Channel(SharedMemoryModel):
|
|||
if is_iter(value):
|
||||
value = ",".join([str(val).strip().lower() for val in value])
|
||||
self.db_aliases = value
|
||||
self.save()
|
||||
self.save()
|
||||
#@aliases_del.deleter
|
||||
def aliases_del(self):
|
||||
"Deleter. Allows for del self.aliases"
|
||||
self.db_aliases = ""
|
||||
self.save()
|
||||
aliases = property(aliases_get, aliases_set, aliases_del)
|
||||
|
||||
|
||||
# keep_log property (wraps db_keep_log)
|
||||
#@property
|
||||
def keep_log_get(self):
|
||||
|
|
@ -499,7 +499,7 @@ class Channel(SharedMemoryModel):
|
|||
keep_log = property(keep_log_get, keep_log_set, keep_log_del)
|
||||
|
||||
# lock_storage property (wraps db_lock_storage)
|
||||
#@property
|
||||
#@property
|
||||
def lock_storage_get(self):
|
||||
"Getter. Allows for value = self.lock_storage"
|
||||
return self.db_lock_storage
|
||||
|
|
@ -527,11 +527,11 @@ class Channel(SharedMemoryModel):
|
|||
|
||||
def __str__(self):
|
||||
return "Channel '%s' (%s)" % (self.key, self.desc)
|
||||
|
||||
|
||||
def has_connection(self, player):
|
||||
"""
|
||||
Checks so this player is actually listening
|
||||
to this channel.
|
||||
to this channel.
|
||||
"""
|
||||
return PlayerChannelConnection.objects.has_connection(player, self)
|
||||
|
||||
|
|
@ -539,54 +539,54 @@ class Channel(SharedMemoryModel):
|
|||
"""
|
||||
Send the given message to all players connected to channel. Note that
|
||||
no permission-checking is done here; it is assumed to have been
|
||||
done before calling this method.
|
||||
done before calling this method.
|
||||
|
||||
msgobj - a Msg instance or a message string. In the latter case a Msg will be created.
|
||||
msgobj - a Msg instance or a message string. In the latter case a Msg will be created.
|
||||
from_obj - if msgobj is not an Msg-instance, this is used to create
|
||||
a message on the fly. If from_obj is None, no Msg object will
|
||||
be created and the message will be sent without being logged.
|
||||
"""
|
||||
be created and the message will be sent without being logged.
|
||||
"""
|
||||
if isinstance(msgobj, basestring):
|
||||
# given msgobj is a string
|
||||
if from_obj:
|
||||
if isinstance(from_obj, basestring):
|
||||
msgobj = Msg(db_sender_external=from_obj, db_message=msgobj)
|
||||
else:
|
||||
msgobj = Msg(db_sender=from_obj, db_message=msgobj)
|
||||
# try to use
|
||||
msgobj = Msg(db_sender=from_obj, db_message=msgobj)
|
||||
# try to use
|
||||
msgobj.save()
|
||||
msgobj.channels = [self]
|
||||
msg = msgobj.message
|
||||
msgobj.channels = [self]
|
||||
msg = msgobj.message
|
||||
else:
|
||||
# this just sends a message, without any sender
|
||||
# this just sends a message, without any sender
|
||||
# (and without storing it in a persistent Msg object)
|
||||
msg = to_str(msgobj)
|
||||
else:
|
||||
msg = msgobj.message
|
||||
|
||||
# get all players connected to this channel and send to them
|
||||
for conn in Channel.objects.get_all_connections(self):
|
||||
for conn in Channel.objects.get_all_connections(self):
|
||||
try:
|
||||
conn.player.msg(msg, from_obj)
|
||||
except AttributeError:
|
||||
try:
|
||||
try:
|
||||
conn.to_external(msg, from_obj, from_channel=self)
|
||||
except Exception:
|
||||
logger.log_trace("Cannot send msg to connection '%s'" % conn)
|
||||
return True
|
||||
return True
|
||||
|
||||
def tempmsg(self, message):
|
||||
"""
|
||||
A wrapper for sending non-persistent messages. Nothing
|
||||
will be stored in the database.
|
||||
will be stored in the database.
|
||||
|
||||
message - a Msg object or a text string.
|
||||
message - a Msg object or a text string.
|
||||
"""
|
||||
if type(msgobj) == Msg:
|
||||
# extract only the string
|
||||
message = message.message
|
||||
if type(msgobj) == Msg:
|
||||
# extract only the string
|
||||
message = message.message
|
||||
return self.msg(message)
|
||||
|
||||
|
||||
def connect_to(self, player):
|
||||
"Connect the user to this channel"
|
||||
if not self.access(player, 'listen'):
|
||||
|
|
@ -594,7 +594,7 @@ class Channel(SharedMemoryModel):
|
|||
conn = PlayerChannelConnection.objects.create_connection(player, self)
|
||||
if conn:
|
||||
return True
|
||||
return False
|
||||
return False
|
||||
|
||||
def disconnect_from(self, player):
|
||||
"Disconnect user from this channel."
|
||||
|
|
@ -611,14 +611,14 @@ class Channel(SharedMemoryModel):
|
|||
accessing_obj - object trying to access this one
|
||||
access_type - type of access sought
|
||||
default - what to return if no lock of access_type was found
|
||||
"""
|
||||
"""
|
||||
return self.locks.check(accessing_obj, access_type=access_type, default=default)
|
||||
|
||||
class PlayerChannelConnection(SharedMemoryModel):
|
||||
"""
|
||||
This connects a player object to a particular comm channel.
|
||||
The advantage of making it like this is that one can easily
|
||||
break the connection just by deleting this object.
|
||||
break the connection just by deleting this object.
|
||||
"""
|
||||
|
||||
# Player connected to a channel
|
||||
|
|
@ -672,19 +672,19 @@ class PlayerChannelConnection(SharedMemoryModel):
|
|||
|
||||
class ExternalChannelConnection(SharedMemoryModel):
|
||||
"""
|
||||
This defines an external protocol connecting to
|
||||
This defines an external protocol connecting to
|
||||
a channel, while storing some critical info about
|
||||
that connection.
|
||||
that connection.
|
||||
"""
|
||||
# evennia channel connecting to
|
||||
db_channel = models.ForeignKey(Channel, verbose_name='channel',
|
||||
db_channel = models.ForeignKey(Channel, verbose_name='channel',
|
||||
help_text='which channel this connection is tied to.')
|
||||
# external connection identifier
|
||||
db_external_key = models.CharField('external key', max_length=128,
|
||||
db_external_key = models.CharField('external key', max_length=128,
|
||||
help_text='external connection identifier, used by calling protocol.')
|
||||
# eval-code to use when the channel tries to send a message
|
||||
# to the external protocol.
|
||||
db_external_send_code = models.TextField('executable code', blank=True,
|
||||
# to the external protocol.
|
||||
db_external_send_code = models.TextField('executable code', blank=True,
|
||||
help_text='this is a custom snippet of Python code to connect the external protocol to the in-game channel.')
|
||||
# custom config for the connection
|
||||
db_external_config = models.TextField('external config', blank=True,
|
||||
|
|
@ -693,10 +693,10 @@ class ExternalChannelConnection(SharedMemoryModel):
|
|||
db_is_enabled = models.BooleanField('is enabled', default=True, help_text='turn on/off the connection.')
|
||||
|
||||
objects = managers.ExternalChannelConnectionManager()
|
||||
|
||||
|
||||
class Meta:
|
||||
verbose_name = "External Channel Connection"
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return "%s <-> external %s" % (self.channel.key, self.db_external_key)
|
||||
|
||||
|
|
@ -778,7 +778,7 @@ class ExternalChannelConnection(SharedMemoryModel):
|
|||
self.save()
|
||||
#@is_enabled.deleter
|
||||
def is_enabled_del(self):
|
||||
"Deleter. Allows for del self.is_enabled. Deletes connection."
|
||||
"Deleter. Allows for del self.is_enabled. Deletes connection."
|
||||
self.delete()
|
||||
is_enabled = property(is_enabled_get, is_enabled_set, is_enabled_del)
|
||||
|
||||
|
|
@ -789,23 +789,22 @@ class ExternalChannelConnection(SharedMemoryModel):
|
|||
def to_channel(self, message, from_obj=None):
|
||||
"Send external -> channel"
|
||||
if not from_obj:
|
||||
from_obj = self.external_key
|
||||
from_obj = self.external_key
|
||||
self.channel.msg(message, from_obj=from_obj)
|
||||
|
||||
def to_external(self, message, from_obj=None, from_channel=None):
|
||||
"Send channel -> external"
|
||||
|
||||
# make sure we are not echoing back our own message to ourselves
|
||||
# (this would result in a nasty infinite loop)
|
||||
# make sure we are not echoing back our own message to ourselves
|
||||
# (this would result in a nasty infinite loop)
|
||||
if from_obj == self.external_key:
|
||||
return
|
||||
|
||||
return
|
||||
|
||||
try:
|
||||
# we execute the code snippet that should make it possible for the
|
||||
# we execute the code snippet that should make it possible for the
|
||||
# connection to contact the protocol correctly (as set by the protocol).
|
||||
# Note that the code block has access to the variables here, such
|
||||
# as message, from_obj and from_channel.
|
||||
# as message, from_obj and from_channel.
|
||||
exec(to_str(self.external_send_code))
|
||||
except Exception:
|
||||
logger.log_trace("Channel %s could not send to External %s" % (self.channel, self.external_key))
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ Custom manager for HelpEntry objects.
|
|||
"""
|
||||
from django.db import models
|
||||
from src.utils import logger, utils
|
||||
__all__ = ("HelpEntryManager",)
|
||||
|
||||
class HelpEntryManager(models.Manager):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ from src.help.manager import HelpEntryManager
|
|||
from src.utils import ansi
|
||||
from src.locks.lockhandler import LockHandler
|
||||
from src.utils.utils import is_iter
|
||||
__all__ = ("HelpEntry",)
|
||||
|
||||
#------------------------------------------------------------
|
||||
#
|
||||
|
|
@ -32,6 +33,9 @@ class HelpEntry(SharedMemoryModel):
|
|||
entrytext - the actual help text
|
||||
permissions - perm strings
|
||||
|
||||
Method:
|
||||
access
|
||||
|
||||
"""
|
||||
|
||||
#
|
||||
|
|
@ -78,88 +82,88 @@ class HelpEntry(SharedMemoryModel):
|
|||
|
||||
# key property (wraps db_key)
|
||||
#@property
|
||||
def key_get(self):
|
||||
def __key_get(self):
|
||||
"Getter. Allows for value = self.key"
|
||||
return self.db_key
|
||||
#@key.setter
|
||||
def key_set(self, value):
|
||||
def __key_set(self, value):
|
||||
"Setter. Allows for self.key = value"
|
||||
self.db_key = value
|
||||
self.save()
|
||||
#@key.deleter
|
||||
def key_del(self):
|
||||
def __key_del(self):
|
||||
"Deleter. Allows for del self.key. Deletes entry."
|
||||
self.delete()
|
||||
key = property(key_get, key_set, key_del)
|
||||
key = property(__key_get, __key_set, __key_del)
|
||||
|
||||
# help_category property (wraps db_help_category)
|
||||
#@property
|
||||
def help_category_get(self):
|
||||
def __help_category_get(self):
|
||||
"Getter. Allows for value = self.help_category"
|
||||
return self.db_help_category
|
||||
#@help_category.setter
|
||||
def help_category_set(self, value):
|
||||
def __help_category_set(self, value):
|
||||
"Setter. Allows for self.help_category = value"
|
||||
self.db_help_category = value
|
||||
self.save()
|
||||
#@help_category.deleter
|
||||
def help_category_del(self):
|
||||
def __help_category_del(self):
|
||||
"Deleter. Allows for del self.help_category"
|
||||
self.db_help_category = "General"
|
||||
self.save()
|
||||
help_category = property(help_category_get, help_category_set, help_category_del)
|
||||
help_category = property(__help_category_get, __help_category_set, __help_category_del)
|
||||
|
||||
# entrytext property (wraps db_entrytext)
|
||||
#@property
|
||||
def entrytext_get(self):
|
||||
def __entrytext_get(self):
|
||||
"Getter. Allows for value = self.entrytext"
|
||||
return self.db_entrytext
|
||||
#@entrytext.setter
|
||||
def entrytext_set(self, value):
|
||||
def __entrytext_set(self, value):
|
||||
"Setter. Allows for self.entrytext = value"
|
||||
self.db_entrytext = value
|
||||
self.save()
|
||||
#@entrytext.deleter
|
||||
def entrytext_del(self):
|
||||
def __entrytext_del(self):
|
||||
"Deleter. Allows for del self.entrytext"
|
||||
self.db_entrytext = ""
|
||||
self.save()
|
||||
entrytext = property(entrytext_get, entrytext_set, entrytext_del)
|
||||
entrytext = property(__entrytext_get, __entrytext_set, __entrytext_del)
|
||||
|
||||
# permissions property
|
||||
#@property
|
||||
def permissions_get(self):
|
||||
def __permissions_get(self):
|
||||
"Getter. Allows for value = self.permissions. Returns a list of permissions."
|
||||
return [perm.strip() for perm in self.db_permissions.split(',')]
|
||||
#@permissions.setter
|
||||
def permissions_set(self, value):
|
||||
def __permissions_set(self, value):
|
||||
"Setter. Allows for self.permissions = value. Stores as a comma-separated string."
|
||||
if is_iter(value):
|
||||
value = ",".join([str(val).strip().lower() for val in value])
|
||||
self.db_permissions = value
|
||||
self.save()
|
||||
#@permissions.deleter
|
||||
def permissions_del(self):
|
||||
def __permissions_del(self):
|
||||
"Deleter. Allows for del self.permissions"
|
||||
self.db_permissions = ""
|
||||
self.save()
|
||||
permissions = property(permissions_get, permissions_set, permissions_del)
|
||||
permissions = property(__permissions_get, __permissions_set, __permissions_del)
|
||||
|
||||
# lock_storage property (wraps db_lock_storage)
|
||||
#@property
|
||||
def lock_storage_get(self):
|
||||
def __lock_storage_get(self):
|
||||
"Getter. Allows for value = self.lock_storage"
|
||||
return self.db_lock_storage
|
||||
#@nick.setter
|
||||
def lock_storage_set(self, value):
|
||||
def __lock_storage_set(self, value):
|
||||
"""Saves the lock_storagetodate. This is usually not called directly, but through self.lock()"""
|
||||
self.db_lock_storage = value
|
||||
self.save()
|
||||
#@nick.deleter
|
||||
def lock_storage_del(self):
|
||||
def __lock_storage_del(self):
|
||||
"Deleter is disabled. Use the lockhandler.delete (self.lock.delete) instead"""
|
||||
logger.log_errmsg("Lock_Storage (on %s) cannot be deleted. Use obj.lock.delete() instead." % self)
|
||||
lock_storage = property(lock_storage_get, lock_storage_set, lock_storage_del)
|
||||
lock_storage = property(__lock_storage_get, __lock_storage_set, __lock_storage_del)
|
||||
|
||||
|
||||
#
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@ This module provides a set of permission lock functions for use
|
|||
with Evennia's permissions system.
|
||||
|
||||
To call these locks, make sure this module is included in the
|
||||
settings tuple PERMISSION_FUNC_MODULES then define a lock on the form
|
||||
'<access_type>:func(args)' and add it to the object's lockhandler.
|
||||
Run the access() method of the handler to execute the lock check.
|
||||
settings tuple PERMISSION_FUNC_MODULES then define a lock on the form
|
||||
'<access_type>:func(args)' and add it to the object's lockhandler.
|
||||
Run the access() method of the handler to execute the lock check.
|
||||
|
||||
Note that accessing_obj and accessed_obj can be any object type
|
||||
with a lock variable/field, so be careful to not expect
|
||||
a certain object type.
|
||||
a certain object type.
|
||||
|
||||
|
||||
Appendix: MUX locks
|
||||
|
|
@ -22,7 +22,7 @@ available like the MUX ones. So many of these are not available in
|
|||
basic Evennia, but could all be implemented easily if needed for the
|
||||
individual game.
|
||||
|
||||
MUX Name: Affects: Effect:
|
||||
MUX Name: Affects: Effect:
|
||||
-------------------------------------------------------------------------------
|
||||
DefaultLock: Exits: controls who may traverse the exit to
|
||||
its destination.
|
||||
|
|
@ -34,43 +34,43 @@ DefaultLock: Exits: controls who may traverse the exit to
|
|||
Players/Things: controls who may GET the object.
|
||||
Evennia: "get:<lockfunc()"
|
||||
EnterLock: Players/Things: controls who may ENTER the object
|
||||
Evennia:
|
||||
Evennia:
|
||||
GetFromLock: All but Exits: controls who may gets things from a given
|
||||
location.
|
||||
Evennia:
|
||||
Evennia:
|
||||
GiveLock: Players/Things: controls who may give the object.
|
||||
Evennia:
|
||||
Evennia:
|
||||
LeaveLock: Players/Things: controls who may LEAVE the object.
|
||||
Evennia:
|
||||
Evennia:
|
||||
LinkLock: All but Exits: controls who may link to the location if the
|
||||
location is LINK_OK (for linking exits or
|
||||
setting drop-tos) or ABODE (for setting
|
||||
homes)
|
||||
Evennia:
|
||||
Evennia:
|
||||
MailLock: Players: controls who may @mail the player.
|
||||
Evennia:
|
||||
Evennia:
|
||||
OpenLock: All but Exits: controls who may open an exit.
|
||||
Evennia:
|
||||
Evennia:
|
||||
PageLock: Players: controls who may page the player.
|
||||
Evennia: "send:<lockfunc()>"
|
||||
ParentLock: All: controls who may make @parent links to the
|
||||
object.
|
||||
Evennia: Typeclasses and "puppet:<lockstring()>"
|
||||
ReceiveLock: Players/Things: controls who may give things to the object.
|
||||
Evennia:
|
||||
Evennia:
|
||||
SpeechLock: All but Exits: controls who may speak in that location
|
||||
Evennia:
|
||||
Evennia:
|
||||
TeloutLock: All but Exits: controls who may teleport out of the
|
||||
location.
|
||||
Evennia:
|
||||
Evennia:
|
||||
TportLock: Rooms/Things: controls who may teleport there
|
||||
Evennia:
|
||||
Evennia:
|
||||
UseLock: All but Exits: controls who may USE the object, GIVE the
|
||||
object money and have the PAY attributes
|
||||
run, have their messages heard and possibly
|
||||
acted on by LISTEN and AxHEAR, and invoke
|
||||
$-commands stored on the object.
|
||||
Evennia: Commands and Cmdsets.
|
||||
Evennia: Commands and Cmdsets.
|
||||
DropLock: All but rooms: controls who may drop that object.
|
||||
Evennia:
|
||||
VisibleLock: All: Controls object visibility when the object
|
||||
|
|
@ -85,7 +85,7 @@ from django.conf import settings
|
|||
from src.utils import search
|
||||
from src.utils import utils
|
||||
|
||||
PERMISSION_HIERARCHY = [p.lower() for p in settings.PERMISSION_HIERARCHY]
|
||||
_PERMISSION_HIERARCHY = [p.lower() for p in settings.PERMISSION_HIERARCHY]
|
||||
|
||||
def _to_player(accessing_obj):
|
||||
"Helper function. Makes sure an accessing object is a player object"
|
||||
|
|
@ -95,85 +95,85 @@ def _to_player(accessing_obj):
|
|||
return accessing_obj
|
||||
|
||||
|
||||
# lock functions
|
||||
# lock functions
|
||||
|
||||
def true(*args, **kwargs):
|
||||
"Always returns True."
|
||||
return True
|
||||
def all(*args, **kwargs):
|
||||
return True
|
||||
return True
|
||||
def false(*args, **kwargs):
|
||||
"Always returns False"
|
||||
return False
|
||||
return False
|
||||
def none(*args, **kwargs):
|
||||
return False
|
||||
return False
|
||||
|
||||
def perm(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
"""
|
||||
The basic permission-checker. Ignores case.
|
||||
The basic permission-checker. Ignores case.
|
||||
|
||||
Usage:
|
||||
Usage:
|
||||
perm(<permission>)
|
||||
|
||||
|
||||
where <permission> is the permission accessing_obj must
|
||||
have in order to pass the lock. If the given permission
|
||||
is part of PERMISSION_HIERARCHY, permission is also granted
|
||||
to all ranks higher up in the hierarchy.
|
||||
have in order to pass the lock. If the given permission
|
||||
is part of _PERMISSION_HIERARCHY, permission is also granted
|
||||
to all ranks higher up in the hierarchy.
|
||||
"""
|
||||
try:
|
||||
perm = args[0].lower()
|
||||
permissions = [p.lower() for p in accessing_obj.permissions]
|
||||
except AttributeError, IndexError:
|
||||
return False
|
||||
|
||||
return False
|
||||
|
||||
if perm in permissions:
|
||||
# simplest case - we have a direct match
|
||||
return True
|
||||
if perm in PERMISSION_HIERARCHY:
|
||||
return True
|
||||
if perm in _PERMISSION_HIERARCHY:
|
||||
# check if we have a higher hierarchy position
|
||||
ppos = PERMISSION_HIERARCHY.index(perm)
|
||||
return any(1 for hpos, hperm in enumerate(PERMISSION_HIERARCHY)
|
||||
ppos = _PERMISSION_HIERARCHY.index(perm)
|
||||
return any(1 for hpos, hperm in enumerate(_PERMISSION_HIERARCHY)
|
||||
if hperm in permissions and hpos > ppos)
|
||||
return False
|
||||
return False
|
||||
|
||||
def perm_above(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
"""
|
||||
Only allow objects with a permission *higher* in the permission
|
||||
hierarchy than the one given. If there is no such higher rank,
|
||||
hierarchy than the one given. If there is no such higher rank,
|
||||
it's assumed we refer to superuser. If no hierarchy is defined,
|
||||
this function has no meaning and returns False.
|
||||
this function has no meaning and returns False.
|
||||
"""
|
||||
try:
|
||||
perm = args[0].lower()
|
||||
except IndexError:
|
||||
return False
|
||||
return False
|
||||
|
||||
if perm in PERMISSION_HIERARCHY:
|
||||
ppos = PERMISSION_HIERARCHY.index(perm)
|
||||
return any(1 for hpos, hperm in enumerate(PERMISSION_HIERARCHY)
|
||||
if perm in _PERMISSION_HIERARCHY:
|
||||
ppos = _PERMISSION_HIERARCHY.index(perm)
|
||||
return any(1 for hpos, hperm in enumerate(_PERMISSION_HIERARCHY)
|
||||
if hperm in [p.lower() for p in accessing_obj.permissions] and hpos > ppos)
|
||||
return False
|
||||
return False
|
||||
|
||||
def pperm(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
"""
|
||||
The basic permission-checker for Player objects. Ignores case.
|
||||
The basic permission-checker for Player objects. Ignores case.
|
||||
|
||||
Usage:
|
||||
Usage:
|
||||
pperm(<permission>)
|
||||
|
||||
|
||||
where <permission> is the permission accessing_obj must
|
||||
have in order to pass the lock. If the given permission
|
||||
is part of PERMISSION_HIERARCHY, permission is also granted
|
||||
to all ranks higher up in the hierarchy.
|
||||
have in order to pass the lock. If the given permission
|
||||
is part of _PERMISSION_HIERARCHY, permission is also granted
|
||||
to all ranks higher up in the hierarchy.
|
||||
"""
|
||||
return perm(_to_player(accessing_obj), accessed_obj, *args, **kwargs)
|
||||
|
||||
def pperm_above(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
"""
|
||||
Only allow Player objects with a permission *higher* in the permission
|
||||
hierarchy than the one given. If there is no such higher rank,
|
||||
hierarchy than the one given. If there is no such higher rank,
|
||||
it's assumed we refer to superuser. If no hierarchy is defined,
|
||||
this function has no meaning and returns False.
|
||||
this function has no meaning and returns False.
|
||||
"""
|
||||
return perm_above(_to_player(accessing_obj), accessed_obj, *args, **kwargs)
|
||||
|
||||
|
|
@ -181,7 +181,7 @@ def dbref(accessing_obj, accessed_obj, *args, **kwargs):
|
|||
"""
|
||||
Usage:
|
||||
dbref(3)
|
||||
|
||||
|
||||
This lock type checks if the checking object
|
||||
has a particular dbref. Note that this only
|
||||
works for checking objects that are stored
|
||||
|
|
@ -195,7 +195,7 @@ def dbref(accessing_obj, accessed_obj, *args, **kwargs):
|
|||
return False
|
||||
if hasattr(accessing_obj, 'id'):
|
||||
return dbref == accessing_obj.id
|
||||
return False
|
||||
return False
|
||||
|
||||
def pdbref(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
"""
|
||||
|
|
@ -212,7 +212,7 @@ def pid(accessing_obj, accessed_obj, *args, **kwargs):
|
|||
return dbref(_to_player(accessing_obj), accessed_obj, *args, **kwargs)
|
||||
|
||||
|
||||
# this is more efficient than multiple if ... elif statments
|
||||
# this is more efficient than multiple if ... elif statments
|
||||
CF_MAPPING = {'eq': lambda val1, val2: val1 == val2 or int(val1) == int(val2),
|
||||
'gt': lambda val1, val2: int(val1) > int(val2),
|
||||
'lt': lambda val1, val2: int(val1) < int(val2),
|
||||
|
|
@ -232,7 +232,7 @@ def attr(accessing_obj, accessed_obj, *args, **kwargs):
|
|||
how the value should be compared with one on accessing_obj (so
|
||||
compare=gt means the accessing_obj must have a value greater than
|
||||
the one given).
|
||||
|
||||
|
||||
Searches attributes *and* properties stored on the checking
|
||||
object. The first form works like a flag - if the
|
||||
attribute/property exists on the object, the value is checked for
|
||||
|
|
@ -240,17 +240,17 @@ def attr(accessing_obj, accessed_obj, *args, **kwargs):
|
|||
attribute/property matches. Note that all retrieved values will be
|
||||
converted to strings before doing the comparison.
|
||||
"""
|
||||
# deal with arguments
|
||||
# deal with arguments
|
||||
if not args:
|
||||
return False
|
||||
attrname = args[0].strip()
|
||||
value = None
|
||||
value = None
|
||||
if len(args) > 1:
|
||||
value = args[1].strip()
|
||||
compare = 'eq'
|
||||
if kwargs:
|
||||
compare = kwargs.get('compare', 'eq')
|
||||
|
||||
|
||||
def valcompare(val1, val2, typ='eq'):
|
||||
"compare based on type"
|
||||
try:
|
||||
|
|
@ -258,20 +258,20 @@ def attr(accessing_obj, accessed_obj, *args, **kwargs):
|
|||
except Exception, e:
|
||||
#print e
|
||||
# this might happen if we try to compare two things that cannot be compared
|
||||
return False
|
||||
return False
|
||||
|
||||
# first, look for normal properties on the object trying to gain access
|
||||
# first, look for normal properties on the object trying to gain access
|
||||
if hasattr(accessing_obj, attrname):
|
||||
if value:
|
||||
return valcompare(str(getattr(accessing_obj, attrname)), value, compare)
|
||||
return valcompare(str(getattr(accessing_obj, attrname)), value, compare)
|
||||
return bool(getattr(accessing_obj, attrname)) # will return Fail on False value etc
|
||||
# check attributes, if they exist
|
||||
# check attributes, if they exist
|
||||
if (hasattr(accessing_obj, 'has_attribute') and accessing_obj.has_attribute(attrname)):
|
||||
if value:
|
||||
return (hasattr(accessing_obj, 'get_attribute')
|
||||
return (hasattr(accessing_obj, 'get_attribute')
|
||||
and valcompare(accessing_obj.get_attribute(attrname), value, compare))
|
||||
return bool(accessing_obj.get_attribute(attrname)) # fails on False/None values
|
||||
return False
|
||||
return False
|
||||
|
||||
def objattr(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
"""
|
||||
|
|
@ -280,7 +280,7 @@ def objattr(accessing_obj, accessed_obj, *args, **kwargs):
|
|||
objattr(attrname, value)
|
||||
objattr(attrname, value, compare=type)
|
||||
|
||||
Works like attr, except it looks for an attribute on
|
||||
Works like attr, except it looks for an attribute on
|
||||
accessing_obj.obj, if such an entity exists. Suitable
|
||||
for commands.
|
||||
|
||||
|
|
@ -295,8 +295,8 @@ def locattr(accessing_obj, accessed_obj, *args, **kwargs):
|
|||
locattr(attrname, value)
|
||||
locattr(attrname, value, compare=type)
|
||||
|
||||
Works like attr, except it looks for an attribute on
|
||||
accessing_obj.location, if such an entity exists.
|
||||
Works like attr, except it looks for an attribute on
|
||||
accessing_obj.location, if such an entity exists.
|
||||
|
||||
"""
|
||||
if hasattr(accessing_obj, "location"):
|
||||
|
|
@ -305,14 +305,14 @@ def locattr(accessing_obj, accessed_obj, *args, **kwargs):
|
|||
|
||||
def attr_eq(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
"""
|
||||
Usage:
|
||||
Usage:
|
||||
attr_gt(attrname, 54)
|
||||
"""
|
||||
return attr(accessing_obj, accessed_obj, *args, **kwargs)
|
||||
|
||||
def attr_gt(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
"""
|
||||
Usage:
|
||||
Usage:
|
||||
attr_gt(attrname, 54)
|
||||
|
||||
Only true if access_obj's attribute > the value given.
|
||||
|
|
@ -320,7 +320,7 @@ def attr_gt(accessing_obj, accessed_obj, *args, **kwargs):
|
|||
return attr(accessing_obj, accessed_obj, *args, **{'compare':'gt'})
|
||||
def attr_ge(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
"""
|
||||
Usage:
|
||||
Usage:
|
||||
attr_gt(attrname, 54)
|
||||
|
||||
Only true if access_obj's attribute >= the value given.
|
||||
|
|
@ -328,7 +328,7 @@ def attr_ge(accessing_obj, accessed_obj, *args, **kwargs):
|
|||
return attr(accessing_obj, accessed_obj, *args, **{'compare':'ge'})
|
||||
def attr_lt(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
"""
|
||||
Usage:
|
||||
Usage:
|
||||
attr_gt(attrname, 54)
|
||||
|
||||
Only true if access_obj's attribute < the value given.
|
||||
|
|
@ -336,7 +336,7 @@ def attr_lt(accessing_obj, accessed_obj, *args, **kwargs):
|
|||
return attr(accessing_obj, accessed_obj, *args, **{'compare':'lt'})
|
||||
def attr_le(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
"""
|
||||
Usage:
|
||||
Usage:
|
||||
attr_gt(attrname, 54)
|
||||
|
||||
Only true if access_obj's attribute <= the value given.
|
||||
|
|
@ -344,7 +344,7 @@ def attr_le(accessing_obj, accessed_obj, *args, **kwargs):
|
|||
return attr(accessing_obj, accessed_obj, *args, **{'compare':'le'})
|
||||
def attr_ne(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
"""
|
||||
Usage:
|
||||
Usage:
|
||||
attr_gt(attrname, 54)
|
||||
|
||||
Only true if access_obj's attribute != the value given.
|
||||
|
|
@ -353,7 +353,7 @@ def attr_ne(accessing_obj, accessed_obj, *args, **kwargs):
|
|||
|
||||
def holds(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
"""
|
||||
Usage:
|
||||
Usage:
|
||||
holds() # checks if accessed_obj or accessed_obj.obj is held by accessing_obj
|
||||
holds(key/dbref) # checks if accessing_obj holds an object with given key/dbref
|
||||
|
||||
|
|
@ -364,50 +364,50 @@ def holds(accessing_obj, accessed_obj, *args, **kwargs):
|
|||
try:
|
||||
# commands and scripts don't have contents, so we are usually looking
|
||||
# for the contents of their .obj property instead (i.e. the object the
|
||||
# command/script is attached to).
|
||||
# command/script is attached to).
|
||||
contents = accessing_obj.contents
|
||||
except AttributeError:
|
||||
try:
|
||||
try:
|
||||
contents = accessing_obj.obj.contents
|
||||
except AttributeError:
|
||||
return False
|
||||
return False
|
||||
|
||||
def check_holds(objid):
|
||||
# helper function. Compares both dbrefs and keys/aliases.
|
||||
objid = str(objid)
|
||||
dbref = utils.dbref(objid)
|
||||
dbref = utils.dbref(objid)
|
||||
if dbref and any((True for obj in contents if obj.id == dbref)):
|
||||
return True
|
||||
return True
|
||||
objid = objid.lower()
|
||||
return any((True for obj in contents
|
||||
return any((True for obj in contents
|
||||
if obj.key.lower() == objid or objid in [al.lower() for al in obj.aliases]))
|
||||
|
||||
if args and args[0]:
|
||||
return check_holds(args[0])
|
||||
else:
|
||||
else:
|
||||
try:
|
||||
if check_holds(accessed_obj.id):
|
||||
#print "holds: accessed_obj.id - True"
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
pass
|
||||
#print "holds: accessed_obj.obj.id -", hasattr(accessed_obj, "obj") and check_holds(accessed_obj.obj.id)
|
||||
return hasattr(accessed_obj, "obj") and check_holds(accessed_obj.obj.id)
|
||||
|
||||
|
||||
def superuser(*args, **kwargs):
|
||||
"""
|
||||
Only accepts an accesing_obj that is superuser (e.g. user #1)
|
||||
|
||||
|
||||
Since a superuser would not ever reach this check (superusers
|
||||
bypass the lock entirely), any user who gets this far cannot be a
|
||||
superuser, hence we just return False. :)
|
||||
"""
|
||||
return False
|
||||
|
||||
return False
|
||||
|
||||
def serversetting(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
"""
|
||||
Only returns true if the Evennia settings exists, alternatively has a certain value.
|
||||
|
||||
|
||||
Usage:
|
||||
serversetting(IRC_ENABLED)
|
||||
serversetting(BASE_SCRIPT_PATH, [game.gamesrc.scripts])
|
||||
|
|
@ -418,16 +418,16 @@ def serversetting(accessing_obj, accessed_obj, *args, **kwargs):
|
|||
return False
|
||||
if len(args) < 2:
|
||||
setting = args[0]
|
||||
val = "True"
|
||||
val = "True"
|
||||
else:
|
||||
setting, val = args[0], args[1]
|
||||
# convert
|
||||
if val == 'True':
|
||||
val = True
|
||||
elif val == 'False':
|
||||
val = False
|
||||
val = False
|
||||
elif val.isdigit():
|
||||
val = int(val)
|
||||
if setting in settings._wrapped.__dict__:
|
||||
return settings._wrapped.__dict__[setting] == val
|
||||
return False
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -5,15 +5,15 @@ A lock defines access to a particular subsystem or property of
|
|||
Evennia. For example, the "owner" property can be impmemented as a
|
||||
lock. Or the disability to lift an object or to ban users.
|
||||
|
||||
A lock consists of three parts:
|
||||
A lock consists of three parts:
|
||||
|
||||
- access_type - this defines what kind of access this lock regulates. This
|
||||
just a string.
|
||||
just a string.
|
||||
- function call - this is one or many calls to functions that will determine
|
||||
if the lock is passed or not.
|
||||
- lock function(s). These are regular python functions with a special
|
||||
- lock function(s). These are regular python functions with a special
|
||||
set of allowed arguments. They should always return a boolean depending
|
||||
on if they allow access or not.
|
||||
on if they allow access or not.
|
||||
|
||||
# Lock function
|
||||
|
||||
|
|
@ -26,26 +26,26 @@ take four arguments looking like this:
|
|||
|
||||
The accessing object is the object wanting to gain access.
|
||||
The accessed object is the object this lock resides on
|
||||
args and kwargs will hold optional arguments and/or keyword arguments
|
||||
args and kwargs will hold optional arguments and/or keyword arguments
|
||||
to the function as a list and a dictionary respectively.
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
perm(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
"Checking if the object has a particular, desired permission"
|
||||
if args:
|
||||
desired_perm = args[0]
|
||||
desired_perm = args[0]
|
||||
return desired_perm in accessing_obj.permissions
|
||||
return False
|
||||
return False
|
||||
|
||||
Lock functions should most often be pretty general and ideally possible to
|
||||
re-use and combine in various ways to build clever locks.
|
||||
re-use and combine in various ways to build clever locks.
|
||||
|
||||
|
||||
# Lock definition ("Lock string")
|
||||
|
||||
A lock definition is a string with a special syntax. It is added to
|
||||
each object's lockhandler, making that lock available from then on.
|
||||
A lock definition is a string with a special syntax. It is added to
|
||||
each object's lockhandler, making that lock available from then on.
|
||||
|
||||
The lock definition looks like this:
|
||||
|
||||
|
|
@ -54,15 +54,15 @@ The lock definition looks like this:
|
|||
That is, the access_type, a colon followed by calls to lock functions
|
||||
combined with AND or OR. NOT negates the result of the following call.
|
||||
|
||||
Example:
|
||||
|
||||
We want to limit who may edit a particular object (let's call this access_type
|
||||
Example:
|
||||
|
||||
We want to limit who may edit a particular object (let's call this access_type
|
||||
for 'edit', it depends on what the command is looking for). We want this to
|
||||
only work for those with the Permission 'Builders'. So we use our lock
|
||||
function above and define it like this:
|
||||
|
||||
'edit:perm(Builders)'
|
||||
|
||||
|
||||
Here, the lock-function perm() will be called with the string
|
||||
'Builders' (accessing_obj and accessed_obj are added automatically,
|
||||
you only need to add the args/kwargs, if any).
|
||||
|
|
@ -73,17 +73,17 @@ could use AND:
|
|||
'edit:perm(Builders) AND perm(GoodGuy)'
|
||||
|
||||
To allow EITHER Builders and GoodGuys, we replace AND with OR. perm() is just one example,
|
||||
the lock function can do anything and compare any properties of the calling object to
|
||||
the lock function can do anything and compare any properties of the calling object to
|
||||
decide if the lock is passed or not.
|
||||
|
||||
'lift:attrib(very_strong) AND NOT attrib(bad_back)'
|
||||
|
||||
To make these work, add the string to the lockhandler of the object you want
|
||||
to apply the lock to:
|
||||
to apply the lock to:
|
||||
|
||||
obj.lockhandler.add('edit:perm(Builders)')
|
||||
|
||||
From then on, a command that wants to check for 'edit' access on this
|
||||
From then on, a command that wants to check for 'edit' access on this
|
||||
object would do something like this:
|
||||
|
||||
if not target_obj.lockhandler.has_perm(caller, 'edit'):
|
||||
|
|
@ -93,8 +93,8 @@ All objects also has a shortcut called 'access' that is recommended to use inste
|
|||
|
||||
if not target_obj.access(caller, 'edit'):
|
||||
caller.msg("Sorry, you cannot edit that.")
|
||||
|
||||
# Permissions
|
||||
|
||||
# Permissions
|
||||
|
||||
Permissions are just text strings stored in a comma-separated list on
|
||||
typeclassed objects. The default perm() lock function uses them,
|
||||
|
|
@ -106,8 +106,8 @@ to any other identifier you can use.
|
|||
|
||||
import re, inspect
|
||||
from django.conf import settings
|
||||
from src.utils import logger, utils
|
||||
|
||||
from src.utils import logger, utils
|
||||
__all__ = ("LockHandler", )
|
||||
#
|
||||
# Exception class
|
||||
#
|
||||
|
|
@ -120,17 +120,17 @@ class LockException(Exception):
|
|||
# Cached lock functions
|
||||
#
|
||||
|
||||
LOCKFUNCS = {}
|
||||
def cache_lockfuncs():
|
||||
_LOCKFUNCS = {}
|
||||
def _cache_lockfuncs():
|
||||
"Updates the cache."
|
||||
global LOCKFUNCS
|
||||
LOCKFUNCS = {}
|
||||
global _LOCKFUNCS
|
||||
_LOCKFUNCS = {}
|
||||
for modulepath in settings.LOCK_FUNC_MODULES:
|
||||
modulepath = utils.pypath_to_realpath(modulepath)
|
||||
mod = utils.mod_import(modulepath)
|
||||
if mod:
|
||||
if mod:
|
||||
for tup in (tup for tup in inspect.getmembers(mod) if callable(tup[1])):
|
||||
LOCKFUNCS[tup[0]] = tup[1]
|
||||
_LOCKFUNCS[tup[0]] = tup[1]
|
||||
else:
|
||||
logger.log_errmsg("Couldn't load %s from PERMISSION_FUNC_MODULES." % modulepath)
|
||||
|
||||
|
|
@ -138,21 +138,21 @@ def cache_lockfuncs():
|
|||
# pre-compiled regular expressions
|
||||
#
|
||||
|
||||
RE_FUNCS = re.compile(r"\w+\([^)]*\)")
|
||||
RE_SEPS = re.compile(r"(?<=[ )])AND(?=\s)|(?<=[ )])OR(?=\s)|(?<=[ )])NOT(?=\s)")
|
||||
RE_OK = re.compile(r"%s|and|or|not")
|
||||
_RE_FUNCS = re.compile(r"\w+\([^)]*\)")
|
||||
_RE_SEPS = re.compile(r"(?<=[ )])AND(?=\s)|(?<=[ )])OR(?=\s)|(?<=[ )])NOT(?=\s)")
|
||||
_RE_OK = re.compile(r"%s|and|or|not")
|
||||
|
||||
|
||||
#
|
||||
#
|
||||
# Lock handler
|
||||
# Lock handler
|
||||
#
|
||||
#
|
||||
|
||||
class LockHandler(object):
|
||||
"""
|
||||
This handler should be attached to all objects implementing
|
||||
permission checks, under the property 'lockhandler'.
|
||||
This handler should be attached to all objects implementing
|
||||
permission checks, under the property 'lockhandler'.
|
||||
"""
|
||||
|
||||
def __init__(self, obj):
|
||||
|
|
@ -160,13 +160,13 @@ class LockHandler(object):
|
|||
Loads and pre-caches all relevant locks and their
|
||||
functions.
|
||||
"""
|
||||
if not LOCKFUNCS:
|
||||
cache_lockfuncs()
|
||||
if not _LOCKFUNCS:
|
||||
_cache_lockfuncs()
|
||||
self.obj = obj
|
||||
self.locks = {}
|
||||
self.log_obj = None
|
||||
self.locks = {}
|
||||
self.log_obj = None
|
||||
self.no_errors = True
|
||||
self.reset_flag = False
|
||||
self.reset_flag = False
|
||||
|
||||
self._cache_locks(self.obj.lock_storage)
|
||||
|
||||
|
|
@ -177,7 +177,7 @@ class LockHandler(object):
|
|||
def _log_error(self, message):
|
||||
"Try to log errors back to object"
|
||||
if self.log_obj and hasattr(self.log_obj, 'msg'):
|
||||
self.log_obj.msg(message)
|
||||
self.log_obj.msg(message)
|
||||
elif hasattr(self.obj, 'msg'):
|
||||
self.obj.msg(message)
|
||||
else:
|
||||
|
|
@ -192,12 +192,12 @@ class LockHandler(object):
|
|||
"""
|
||||
locks = {}
|
||||
if not storage_lockstring:
|
||||
return locks
|
||||
return locks
|
||||
nlocks = storage_lockstring.count(';') + 1
|
||||
duplicates = 0
|
||||
elist = [] # errors
|
||||
elist = [] # errors
|
||||
wlist = [] # warnings
|
||||
for raw_lockstring in storage_lockstring.split(';'):
|
||||
for raw_lockstring in storage_lockstring.split(';'):
|
||||
lock_funcs = []
|
||||
try:
|
||||
access_type, rhs = (part.strip() for part in raw_lockstring.split(':', 1))
|
||||
|
|
@ -206,42 +206,42 @@ class LockHandler(object):
|
|||
return locks
|
||||
|
||||
# parse the lock functions and separators
|
||||
funclist = RE_FUNCS.findall(rhs)
|
||||
funclist = _RE_FUNCS.findall(rhs)
|
||||
evalstring = rhs.replace('AND','and').replace('OR','or').replace('NOT','not')
|
||||
nfuncs = len(funclist)
|
||||
for funcstring in funclist:
|
||||
for funcstring in funclist:
|
||||
funcname, rest = (part.strip().strip(')') for part in funcstring.split('(', 1))
|
||||
func = LOCKFUNCS.get(funcname, None)
|
||||
func = _LOCKFUNCS.get(funcname, None)
|
||||
if not callable(func):
|
||||
elist.append("Lock: function '%s' is not available." % funcstring)
|
||||
continue
|
||||
continue
|
||||
args = list(arg.strip() for arg in rest.split(',') if not '=' in arg)
|
||||
kwargs = dict([arg.split('=', 1) for arg in rest.split(',') if '=' in arg])
|
||||
kwargs = dict([arg.split('=', 1) for arg in rest.split(',') if '=' in arg])
|
||||
lock_funcs.append((func, args, kwargs))
|
||||
evalstring = evalstring.replace(funcstring, '%s')
|
||||
if len(lock_funcs) < nfuncs:
|
||||
evalstring = evalstring.replace(funcstring, '%s')
|
||||
if len(lock_funcs) < nfuncs:
|
||||
continue
|
||||
try:
|
||||
# purge the eval string of any superfluos items, then test it
|
||||
evalstring = " ".join(RE_OK.findall(evalstring))
|
||||
evalstring = " ".join(_RE_OK.findall(evalstring))
|
||||
eval(evalstring % tuple(True for func in funclist))
|
||||
except Exception:
|
||||
elist.append("Lock: definition '%s' has syntax errors." % raw_lockstring)
|
||||
continue
|
||||
if access_type in locks:
|
||||
if access_type in locks:
|
||||
duplicates += 1
|
||||
wlist.append("Lock: access type '%s' changed from '%s' to '%s' " % \
|
||||
(access_type, locks[access_type][2], raw_lockstring))
|
||||
locks[access_type] = (evalstring, tuple(lock_funcs), raw_lockstring)
|
||||
locks[access_type] = (evalstring, tuple(lock_funcs), raw_lockstring)
|
||||
if wlist and self.log_obj:
|
||||
# a warning text was set, it's not an error, so only report if log_obj is available.
|
||||
self._log_error("\n".join(wlist))
|
||||
if elist:
|
||||
# an error text was set, raise exception.
|
||||
raise LockException("\n".join(elist))
|
||||
# an error text was set, raise exception.
|
||||
raise LockException("\n".join(elist))
|
||||
self.no_errors = False
|
||||
# return the gathered locks in an easily executable form
|
||||
return locks
|
||||
# return the gathered locks in an easily executable form
|
||||
return locks
|
||||
|
||||
def _cache_locks(self, storage_lockstring):
|
||||
"""Store data"""
|
||||
|
|
@ -253,29 +253,29 @@ class LockHandler(object):
|
|||
|
||||
def add(self, lockstring, log_obj=None):
|
||||
"""
|
||||
Add a new lockstring on the form '<access_type>:<functions>'. Multiple
|
||||
access types should be separated by semicolon (;).
|
||||
Add a new lockstring on the form '<access_type>:<functions>'. Multiple
|
||||
access types should be separated by semicolon (;).
|
||||
|
||||
If log_obj is given, it will be fed error information.
|
||||
"""
|
||||
if log_obj:
|
||||
self.log_obj = log_obj
|
||||
self.no_errors = True
|
||||
# sanity checks
|
||||
for lockdef in lockstring.split(';'):
|
||||
self.no_errors = True
|
||||
# sanity checks
|
||||
for lockdef in lockstring.split(';'):
|
||||
if not ':' in lockstring:
|
||||
self._log_error("Lock: '%s' contains no colon (:)." % lockdef)
|
||||
return False
|
||||
return False
|
||||
access_type, rhs = [part.strip() for part in lockdef.split(':', 1)]
|
||||
if not access_type:
|
||||
self._log_error("Lock: '%s' has no access_type (left-side of colon is empty)." % lockdef)
|
||||
return False
|
||||
return False
|
||||
if rhs.count('(') != rhs.count(')'):
|
||||
self._log_error("Lock: '%s' has mismatched parentheses." % lockdef)
|
||||
return False
|
||||
if not RE_FUNCS.findall(rhs):
|
||||
return False
|
||||
if not _RE_FUNCS.findall(rhs):
|
||||
self._log_error("Lock: '%s' has no valid lock functions." % lockdef)
|
||||
return False
|
||||
return False
|
||||
# get the lock string
|
||||
storage_lockstring = self.obj.lock_storage
|
||||
if storage_lockstring:
|
||||
|
|
@ -285,8 +285,8 @@ class LockHandler(object):
|
|||
# cache the locks will get rid of eventual doublets
|
||||
self._cache_locks(storage_lockstring)
|
||||
self._save_locks()
|
||||
self.log_obj = None
|
||||
return self.no_errors
|
||||
self.log_obj = None
|
||||
return self.no_errors
|
||||
|
||||
def get(self, access_type):
|
||||
"get the lockstring of a particular type"
|
||||
|
|
@ -297,8 +297,8 @@ class LockHandler(object):
|
|||
if access_type in self.locks:
|
||||
del self.locks[access_type]
|
||||
self._save_locks()
|
||||
return True
|
||||
return False
|
||||
return True
|
||||
return False
|
||||
|
||||
def clear(self):
|
||||
"Remove all locks"
|
||||
|
|
@ -307,10 +307,10 @@ class LockHandler(object):
|
|||
def reset(self):
|
||||
"""
|
||||
Set the reset flag, so the the lock will be re-cached at next checking.
|
||||
This is usually set by @reload.
|
||||
This is usually set by @reload.
|
||||
"""
|
||||
self.reset_flag = True
|
||||
|
||||
|
||||
def check(self, accessing_obj, access_type, default=False, no_superuser_bypass=False):
|
||||
"""
|
||||
Checks a lock of the correct type by passing execution
|
||||
|
|
@ -322,8 +322,8 @@ class LockHandler(object):
|
|||
no_superuser_bypass - don't use this unless you really, really need to,
|
||||
it makes supersusers susceptible to the lock check.
|
||||
|
||||
A lock is executed in the follwoing way:
|
||||
|
||||
A lock is executed in the follwoing way:
|
||||
|
||||
Parsing the lockstring, we (during cache) extract the valid
|
||||
lock functions and store their function objects in the right
|
||||
order along with their args/kwargs. These are now executed in
|
||||
|
|
@ -340,11 +340,11 @@ class LockHandler(object):
|
|||
|
||||
"""
|
||||
if self.reset_flag:
|
||||
# rebuild cache
|
||||
# rebuild cache
|
||||
self._cache_locks(self.obj.lock_storage)
|
||||
self.reset_flag = False
|
||||
self.reset_flag = False
|
||||
|
||||
if (not no_superuser_bypass
|
||||
if (not no_superuser_bypass
|
||||
and ((hasattr(accessing_obj, 'is_superuser') and accessing_obj.is_superuser)
|
||||
or (hasattr(accessing_obj, 'player') and hasattr(accessing_obj.player, 'is_superuser') and accessing_obj.player.is_superuser)
|
||||
or (hasattr(accessing_obj, 'get_player') and (accessing_obj.get_player()==None or accessing_obj.get_player().is_superuser)))):
|
||||
|
|
@ -353,11 +353,11 @@ class LockHandler(object):
|
|||
return True
|
||||
|
||||
if access_type in self.locks:
|
||||
# we have a lock, test it.
|
||||
evalstring, func_tup, raw_string = self.locks[access_type]
|
||||
# we have a lock, test it.
|
||||
evalstring, func_tup, raw_string = self.locks[access_type]
|
||||
# execute all lock funcs in the correct order, producing a tuple of True/False results.
|
||||
true_false = tuple(bool(tup[0](accessing_obj, self.obj, *tup[1], **tup[2])) for tup in func_tup)
|
||||
# the True/False tuple goes into evalstring, which combines them
|
||||
# the True/False tuple goes into evalstring, which combines them
|
||||
# with AND/OR/NOT in order to get the final result.
|
||||
return eval(evalstring % true_false)
|
||||
else:
|
||||
|
|
@ -367,22 +367,22 @@ class LockHandler(object):
|
|||
"""
|
||||
Do a direct check against a lockstring ('atype:func()..'), without any
|
||||
intermediary storage on the accessed object (this can be left
|
||||
to None if the lock functions called don't access it). atype can also be
|
||||
to None if the lock functions called don't access it). atype can also be
|
||||
put to a dummy value since no lock selection is made.
|
||||
"""
|
||||
if ((hasattr(accessing_obj, 'is_superuser') and accessing_obj.is_superuser)
|
||||
or (hasattr(accessing_obj, 'player') and hasattr(accessing_obj.player, 'is_superuser') and accessing_obj.player.is_superuser)
|
||||
or (hasattr(accessing_obj, 'get_player') and (accessing_obj.get_player()==None or accessing_obj.get_player().is_superuser))):
|
||||
return True
|
||||
|
||||
return True
|
||||
|
||||
locks = self. _parse_lockstring(lockstring)
|
||||
for access_type in locks:
|
||||
evalstring, func_tup, raw_string = locks[access_type]
|
||||
true_false = tuple(tup[0](accessing_obj, self.obj, *tup[1], **tup[2]) for tup in func_tup)
|
||||
return eval(evalstring % true_false)
|
||||
|
||||
def test():
|
||||
# testing
|
||||
|
||||
def _test():
|
||||
# testing
|
||||
|
||||
class TestObj(object):
|
||||
pass
|
||||
|
|
@ -390,9 +390,9 @@ def test():
|
|||
import pdb
|
||||
obj1 = TestObj()
|
||||
obj2 = TestObj()
|
||||
|
||||
|
||||
#obj1.lock_storage = "owner:dbref(#4);edit:dbref(#5) or perm(Wizards);examine:perm(Builders);delete:perm(Wizards);get:all()"
|
||||
#obj1.lock_storage = "cmd:all();admin:id(1);listen:all();send:all()"
|
||||
#obj1.lock_storage = "cmd:all();admin:id(1);listen:all();send:all()"
|
||||
obj1.lock_storage = "listen:perm(Immortals)"
|
||||
|
||||
pdb.set_trace()
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
"""
|
||||
This is part of Evennia's unittest framework, for testing
|
||||
the stability and integrrity of the codebase during updates.
|
||||
the stability and integrrity of the codebase during updates.
|
||||
|
||||
This module tests the lock functionality of Evennia.
|
||||
|
||||
|
|
@ -49,7 +49,7 @@ class TestLockfuncs(LockTest):
|
|||
def testrun(self):
|
||||
self.obj2.permissions = ['Wizards']
|
||||
self.assertEquals(True, lockfuncs.true(self.obj2, self.obj1))
|
||||
self.assertEquals(False, lockfuncs.false(self.obj2, self.obj1))
|
||||
self.assertEquals(False, lockfuncs.false(self.obj2, self.obj1))
|
||||
self.assertEquals(True, lockfuncs.perm(self.obj2, self.obj1, 'Wizards'))
|
||||
self.assertEquals(True, lockfuncs.perm_above(self.obj2, self.obj1, 'Builders'))
|
||||
dbref = self.obj2.dbref
|
||||
|
|
|
|||
|
|
@ -10,9 +10,11 @@ from src.utils import utils
|
|||
from src.utils.utils import to_unicode
|
||||
from src.utils import logger
|
||||
|
||||
__all__ = ("ObjectManager",)
|
||||
|
||||
# Try to use a custom way to parse id-tagged multimatches.
|
||||
|
||||
AT_MULTIMATCH_INPUT = utils.mod_import(*settings.SEARCH_AT_MULTIMATCH_INPUT.rsplit('.', 1))
|
||||
_AT_MULTIMATCH_INPUT = utils.mod_import(*settings.SEARCH_AT_MULTIMATCH_INPUT.rsplit('.', 1))
|
||||
|
||||
class ObjectManager(TypedObjectManager):
|
||||
"""
|
||||
|
|
@ -288,7 +290,7 @@ class ObjectManager(TypedObjectManager):
|
|||
matches = local_and_global_search(ostring, exact=True)
|
||||
if not matches:
|
||||
# if we have no match, check if we are dealing with an "N-keyword" query - if so, strip it.
|
||||
match_number, ostring = AT_MULTIMATCH_INPUT(ostring)
|
||||
match_number, ostring = _AT_MULTIMATCH_INPUT(ostring)
|
||||
if match_number != None and ostring:
|
||||
# Run search again, without match number:
|
||||
matches = local_and_global_search(ostring, exact=True)
|
||||
|
|
|
|||
|
|
@ -32,7 +32,8 @@ from src.scripts.scripthandler import ScriptHandler
|
|||
from src.utils import logger
|
||||
from src.utils.utils import make_iter, to_unicode, to_str, mod_import
|
||||
|
||||
#PlayerDB = ContentType.objects.get(app_label="players", model="playerdb").model_class()
|
||||
#__all__ = ("ObjAttribute", "Alias", "ObjectNick", "ObjectDB")
|
||||
|
||||
|
||||
_AT_SEARCH_RESULT = mod_import(*settings.SEARCH_AT_RESULT.rsplit('.', 1))
|
||||
|
||||
|
|
@ -211,7 +212,7 @@ class ObjectDB(TypedObject):
|
|||
|
||||
# aliases property (wraps (db_aliases)
|
||||
#@property
|
||||
def aliases_get(self):
|
||||
def __aliases_get(self):
|
||||
"Getter. Allows for value = self.aliases"
|
||||
try:
|
||||
return _GA(self, "_cached_aliases")
|
||||
|
|
@ -220,23 +221,23 @@ class ObjectDB(TypedObject):
|
|||
_SA(self, "_cached_aliases", aliases)
|
||||
return aliases
|
||||
#@aliases.setter
|
||||
def aliases_set(self, aliases):
|
||||
def __aliases_set(self, aliases):
|
||||
"Setter. Allows for self.aliases = value"
|
||||
for alias in make_iter(aliases):
|
||||
new_alias = Alias(db_key=alias, db_obj=self)
|
||||
new_alias.save()
|
||||
_SA(self, "_cached_aliases", aliases)
|
||||
#@aliases.deleter
|
||||
def aliases_del(self):
|
||||
def __aliases_del(self):
|
||||
"Deleter. Allows for del self.aliases"
|
||||
for alias in Alias.objects.filter(db_obj=self):
|
||||
alias.delete()
|
||||
_DA(self, "_cached_aliases")
|
||||
aliases = property(aliases_get, aliases_set, aliases_del)
|
||||
aliases = property(__aliases_get, __aliases_set, __aliases_del)
|
||||
|
||||
# player property (wraps db_player)
|
||||
#@property
|
||||
def player_get(self):
|
||||
def __player_get(self):
|
||||
"""
|
||||
Getter. Allows for value = self.player.
|
||||
We have to be careful here since Player is also
|
||||
|
|
@ -244,29 +245,29 @@ class ObjectDB(TypedObject):
|
|||
"""
|
||||
return _get_cache(self, "player")
|
||||
#@player.setter
|
||||
def player_set(self, player):
|
||||
def __player_set(self, player):
|
||||
"Setter. Allows for self.player = value"
|
||||
if isinstance(player, TypeClass):
|
||||
player = player.dbobj
|
||||
_set_cache(self, "player", player)
|
||||
#@player.deleter
|
||||
def player_del(self):
|
||||
def __player_del(self):
|
||||
"Deleter. Allows for del self.player"
|
||||
self.db_player = None
|
||||
self.save()
|
||||
_del_cache(self, "player")
|
||||
player = property(player_get, player_set, player_del)
|
||||
player = property(__player_get, __player_set, __player_del)
|
||||
|
||||
# location property (wraps db_location)
|
||||
#@property
|
||||
def location_get(self):
|
||||
def __location_get(self):
|
||||
"Getter. Allows for value = self.location."
|
||||
loc = _get_cache(self, "location")
|
||||
if loc:
|
||||
return loc.typeclass
|
||||
return None
|
||||
#@location.setter
|
||||
def location_set(self, location):
|
||||
def __location_set(self, location):
|
||||
"Setter. Allows for self.location = location"
|
||||
try:
|
||||
if location == None or type(location) == ObjectDB:
|
||||
|
|
@ -289,23 +290,23 @@ class ObjectDB(TypedObject):
|
|||
logger.log_trace(string)
|
||||
raise
|
||||
#@location.deleter
|
||||
def location_del(self):
|
||||
def __location_del(self):
|
||||
"Deleter. Allows for del self.location"
|
||||
self.db_location = None
|
||||
self.save()
|
||||
_del_cache(self, "location")
|
||||
location = property(location_get, location_set, location_del)
|
||||
location = property(__location_get, __location_set, __location_del)
|
||||
|
||||
# home property (wraps db_home)
|
||||
#@property
|
||||
def home_get(self):
|
||||
def __home_get(self):
|
||||
"Getter. Allows for value = self.home"
|
||||
home = _get_cache(self, "home")
|
||||
if home:
|
||||
return home.typeclass
|
||||
return None
|
||||
#@home.setter
|
||||
def home_set(self, home):
|
||||
def __home_set(self, home):
|
||||
"Setter. Allows for self.home = value"
|
||||
try:
|
||||
if home == None or type(home) == ObjectDB:
|
||||
|
|
@ -326,23 +327,23 @@ class ObjectDB(TypedObject):
|
|||
logger.log_trace(string)
|
||||
#raise
|
||||
#@home.deleter
|
||||
def home_del(self):
|
||||
def __home_del(self):
|
||||
"Deleter. Allows for del self.home."
|
||||
self.db_home = None
|
||||
self.save()
|
||||
_del_cache(self, "home")
|
||||
home = property(home_get, home_set, home_del)
|
||||
home = property(__home_get, __home_set, __home_del)
|
||||
|
||||
# destination property (wraps db_destination)
|
||||
#@property
|
||||
def destination_get(self):
|
||||
def __destination_get(self):
|
||||
"Getter. Allows for value = self.destination."
|
||||
dest = _get_cache(self, "destination")
|
||||
if dest:
|
||||
return dest.typeclass
|
||||
return None
|
||||
#@destination.setter
|
||||
def destination_set(self, destination):
|
||||
def __destination_set(self, destination):
|
||||
"Setter. Allows for self.destination = destination"
|
||||
try:
|
||||
if destination == None or type(destination) == ObjectDB:
|
||||
|
|
@ -365,33 +366,33 @@ class ObjectDB(TypedObject):
|
|||
logger.log_trace(string)
|
||||
raise
|
||||
#@destination.deleter
|
||||
def destination_del(self):
|
||||
def __destination_del(self):
|
||||
"Deleter. Allows for del self.destination"
|
||||
self.db_destination = None
|
||||
self.save()
|
||||
_del_cache(self, "destination")
|
||||
destination = property(destination_get, destination_set, destination_del)
|
||||
destination = property(__destination_get, __destination_set, __destination_del)
|
||||
|
||||
# cmdset_storage property.
|
||||
# This seems very sensitive to caching, so leaving it be for now. /Griatch
|
||||
#@property
|
||||
def cmdset_storage_get(self):
|
||||
def __cmdset_storage_get(self):
|
||||
"Getter. Allows for value = self.name. Returns a list of cmdset_storage."
|
||||
if self.db_cmdset_storage:
|
||||
return [path.strip() for path in self.db_cmdset_storage.split(',')]
|
||||
return []
|
||||
#@cmdset_storage.setter
|
||||
def cmdset_storage_set(self, value):
|
||||
def __cmdset_storage_set(self, value):
|
||||
"Setter. Allows for self.name = value. Stores as a comma-separated string."
|
||||
value = ",".join(str(val).strip() for val in make_iter(value))
|
||||
self.db_cmdset_storage = value
|
||||
self.save()
|
||||
#@cmdset_storage.deleter
|
||||
def cmdset_storage_del(self):
|
||||
def __cmdset_storage_del(self):
|
||||
"Deleter. Allows for del self.name"
|
||||
self.db_cmdset_storage = ""
|
||||
self.save()
|
||||
cmdset_storage = property(cmdset_storage_get, cmdset_storage_set, cmdset_storage_del)
|
||||
cmdset_storage = property(__cmdset_storage_get, __cmdset_storage_set, __cmdset_storage_del)
|
||||
|
||||
class Meta:
|
||||
"Define Django meta options"
|
||||
|
|
@ -409,7 +410,7 @@ class ObjectDB(TypedObject):
|
|||
_default_typeclass_path = settings.BASE_OBJECT_TYPECLASS or "src.objects.objects.Object"
|
||||
|
||||
#@property
|
||||
def sessions_get(self):
|
||||
def __sessions_get(self):
|
||||
"""
|
||||
Retrieve sessions connected to this object.
|
||||
"""
|
||||
|
|
@ -417,42 +418,43 @@ class ObjectDB(TypedObject):
|
|||
if self.player:
|
||||
return self.player.sessions
|
||||
return []
|
||||
sessions = property(sessions_get)
|
||||
sessions = property(__sessions_get)
|
||||
|
||||
#@property
|
||||
def has_player_get(self):
|
||||
def __has_player_get(self):
|
||||
"""
|
||||
Convenience function for checking if an active player is
|
||||
currently connected to this object
|
||||
"""
|
||||
return any(self.sessions)
|
||||
has_player = property(has_player_get)
|
||||
is_player = property(has_player_get)
|
||||
has_player = property(__has_player_get)
|
||||
is_player = property(__has_player_get)
|
||||
|
||||
#@property
|
||||
def is_superuser_get(self):
|
||||
def __is_superuser_get(self):
|
||||
"Check if user has a player, and if so, if it is a superuser."
|
||||
return any(self.sessions) and self.player.is_superuser
|
||||
is_superuser = property(is_superuser_get)
|
||||
is_superuser = property(__is_superuser_get)
|
||||
|
||||
#@property
|
||||
def contents_get(self, exclude=None):
|
||||
"""
|
||||
Returns the contents of this object, i.e. all
|
||||
objects that has this object set as its location.
|
||||
This should be publically available.
|
||||
"""
|
||||
return ObjectDB.objects.get_contents(self, excludeobj=exclude)
|
||||
contents = property(contents_get)
|
||||
|
||||
#@property
|
||||
def exits_get(self):
|
||||
def __exits_get(self):
|
||||
"""
|
||||
Returns all exits from this object, i.e. all objects
|
||||
at this location having the property destination != None.
|
||||
"""
|
||||
return [exi for exi in self.contents
|
||||
if exi.destination]
|
||||
exits = property(exits_get)
|
||||
exits = property(__exits_get)
|
||||
|
||||
|
||||
#
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ they control by simply linking to a new object's user property.
|
|||
|
||||
from src.typeclasses.typeclass import TypeClass
|
||||
from src.commands import cmdset, command
|
||||
__all__ = ("Object", "Character", "Room", "Exit")
|
||||
|
||||
#
|
||||
# Base class to inherit from.
|
||||
|
|
@ -24,11 +25,10 @@ from src.commands import cmdset, command
|
|||
|
||||
class Object(TypeClass):
|
||||
"""
|
||||
This is the base class for all in-game objects.
|
||||
Inherit from this to create different types of
|
||||
objects in the game.
|
||||
This is the base class for all in-game objects. Inherit from this
|
||||
to create different types of objects in the game.
|
||||
"""
|
||||
|
||||
# __init__ is only defined here in order to present docstring to API.
|
||||
def __init__(self, dbobj):
|
||||
"""
|
||||
This is the root typeclass object, implementing an in-game Evennia
|
||||
|
|
|
|||
|
|
@ -2,12 +2,13 @@
|
|||
The managers for the custom Player object and permissions.
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import datetime
|
||||
from functools import update_wrapper
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
from src.typeclasses.managers import returns_typeclass_list, returns_typeclass, TypedObjectManager
|
||||
from src.utils import logger
|
||||
__all__ = ("PlayerManager",)
|
||||
|
||||
#
|
||||
# Player Manager
|
||||
|
|
@ -35,16 +36,16 @@ def returns_player_list(method):
|
|||
players.append(user.get_profile())
|
||||
except Exception:
|
||||
# there is something wrong with get_profile. But
|
||||
# there is a 1-1 relation between Users-Players, so we
|
||||
# there is a 1-1 relation between Users-Players, so we
|
||||
# try to go the other way instead.
|
||||
from src.players.models import PlayerDB
|
||||
match = PlayerDB.objects.filter(user=user)
|
||||
if match:
|
||||
players.append(match[0])
|
||||
else:
|
||||
logger.log_trace("No connection User<->Player, maybe database was partially reset?")
|
||||
logger.log_trace("No connection User<->Player, maybe database was partially reset?")
|
||||
return players
|
||||
return update_wrapper(func, method)
|
||||
return update_wrapper(func, method)
|
||||
|
||||
def returns_player(method):
|
||||
"""
|
||||
|
|
@ -57,18 +58,18 @@ def returns_player(method):
|
|||
if match:
|
||||
return match[0]
|
||||
else:
|
||||
return None
|
||||
return None
|
||||
return update_wrapper(func, method)
|
||||
|
||||
class PlayerManager(TypedObjectManager):
|
||||
"""
|
||||
This PlayerManager implements methods for searching
|
||||
This PlayerManager implements methods for searching
|
||||
and manipulating Players directly from the database.
|
||||
|
||||
Evennia-specific search methods (will return Characters if
|
||||
possible or a Typeclass/list of Typeclassed objects, whereas
|
||||
Django-general methods will return Querysets or database objects):
|
||||
|
||||
|
||||
dbref (converter)
|
||||
dbref_search
|
||||
get_dbref_range
|
||||
|
|
@ -83,14 +84,14 @@ class PlayerManager(TypedObjectManager):
|
|||
get_player_from_name
|
||||
player_search (equivalent to ev.search_player)
|
||||
swap_character
|
||||
|
||||
|
||||
"""
|
||||
def num_total_players(self):
|
||||
"""
|
||||
Returns the total number of registered users/players.
|
||||
"""
|
||||
return self.count()
|
||||
|
||||
|
||||
@returns_typeclass_list
|
||||
def get_connected_players(self):
|
||||
"""
|
||||
|
|
@ -99,7 +100,7 @@ class PlayerManager(TypedObjectManager):
|
|||
return [player for player in self.all() if player.sessions]
|
||||
|
||||
@returns_typeclass_list
|
||||
@returns_player_list
|
||||
@returns_player_list
|
||||
def get_recently_created_players(self, days=7):
|
||||
"""
|
||||
Returns a QuerySet containing the player User accounts that have been
|
||||
|
|
@ -145,8 +146,8 @@ class PlayerManager(TypedObjectManager):
|
|||
players = self.filter(user__username=uname)
|
||||
if players:
|
||||
return players[0]
|
||||
return None
|
||||
|
||||
return None
|
||||
|
||||
# @returns_typeclass_list
|
||||
# def get_players_with_perm(self, permstring):
|
||||
# """
|
||||
|
|
@ -159,7 +160,7 @@ class PlayerManager(TypedObjectManager):
|
|||
# @returns_typeclass_list
|
||||
# def get_players_with_group(self, groupstring):
|
||||
# """
|
||||
# Returns all players belonging to the given group.
|
||||
# Returns all players belonging to the given group.
|
||||
# """
|
||||
# return [player.user for player in self.all()
|
||||
# if player.has_group(groupstring)]
|
||||
|
|
@ -167,9 +168,9 @@ class PlayerManager(TypedObjectManager):
|
|||
@returns_typeclass_list
|
||||
def player_search(self, ostring):
|
||||
"""
|
||||
Searches for a particular player by name or
|
||||
Searches for a particular player by name or
|
||||
database id.
|
||||
|
||||
|
||||
ostring = a string or database id.
|
||||
"""
|
||||
ostring = ostring.lstrip("*")
|
||||
|
|
@ -178,7 +179,7 @@ class PlayerManager(TypedObjectManager):
|
|||
matches = self.filter(id=dbref)
|
||||
if matches:
|
||||
return matches
|
||||
return self.filter(user__username__iexact=ostring)
|
||||
return self.filter(user__username__iexact=ostring)
|
||||
|
||||
def swap_character(self, player, new_character, delete_old_character=False):
|
||||
"""
|
||||
|
|
@ -192,7 +193,7 @@ class PlayerManager(TypedObjectManager):
|
|||
return False
|
||||
|
||||
# do the swap
|
||||
old_character = player.character
|
||||
old_character = player.character
|
||||
if old_character:
|
||||
old_character.player = None
|
||||
try:
|
||||
|
|
@ -202,8 +203,7 @@ class PlayerManager(TypedObjectManager):
|
|||
# recover old setup
|
||||
old_character.player = player
|
||||
player.character = old_character
|
||||
return False
|
||||
return False
|
||||
if delete_old_character:
|
||||
old_character.delete()
|
||||
return True
|
||||
|
||||
return True
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ from src.utils import logger, utils
|
|||
from src.commands.cmdsethandler import CmdSetHandler
|
||||
from src.commands import cmdhandler
|
||||
|
||||
__all__ = ("PlayerAttribute", "PlayerNick", "PlayerDB")
|
||||
|
||||
_AT_SEARCH_RESULT = utils.mod_import(*settings.SEARCH_AT_RESULT.rsplit('.', 1))
|
||||
|
||||
#------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
"""
|
||||
Typeclass for Player objects
|
||||
|
||||
Note that this object is primarily intended to
|
||||
store OOC information, not game info! This
|
||||
Note that this object is primarily intended to
|
||||
store OOC information, not game info! This
|
||||
object represents the actual user (not their
|
||||
character) and has NO actual precence in the
|
||||
character) and has NO actual precence in the
|
||||
game world (this is handled by the associated
|
||||
character object, so you should customize that
|
||||
instead for most things).
|
||||
|
|
@ -12,12 +12,12 @@ instead for most things).
|
|||
"""
|
||||
from django.conf import settings
|
||||
from src.typeclasses.typeclass import TypeClass
|
||||
|
||||
__all__ = ("Player",)
|
||||
CMDSET_OOC = settings.CMDSET_OOC
|
||||
|
||||
class Player(TypeClass):
|
||||
"""
|
||||
Base typeclass for all Players.
|
||||
Base typeclass for all Players.
|
||||
"""
|
||||
def __init__(self, dbobj):
|
||||
"""
|
||||
|
|
@ -27,7 +27,7 @@ class Player(TypeClass):
|
|||
can connect to a Character Object in order to "enter" the
|
||||
game.
|
||||
|
||||
Player Typeclass API:
|
||||
Player Typeclass API:
|
||||
|
||||
* Available properties (only available on initiated typeclass objects)
|
||||
|
||||
|
|
@ -38,22 +38,22 @@ class Player(TypeClass):
|
|||
dbobj (Player, read-only) - link to database model. dbobj.typeclass points back to this class
|
||||
typeclass (Player, read-only) - this links back to this class as an identified only. Use self.swap_typeclass() to switch.
|
||||
date_created (string) - time stamp of object creation
|
||||
permissions (list of strings) - list of permission strings
|
||||
permissions (list of strings) - list of permission strings
|
||||
|
||||
user (User, read-only) - django User authorization object
|
||||
obj (Object) - game object controlled by player. 'character' can also be used.
|
||||
sessions (list of Sessions) - sessions connected to this player
|
||||
is_superuser (bool, read-only) - if the connected user is a superuser
|
||||
|
||||
* Handlers
|
||||
* Handlers
|
||||
|
||||
locks - lock-handler: use locks.add() to add new lock strings
|
||||
db - attribute-handler: store/retrieve database attributes on this self.db.myattr=val, val=self.db.myattr
|
||||
ndb - non-persistent attribute handler: same as db but does not create a database entry when storing data
|
||||
ndb - non-persistent attribute handler: same as db but does not create a database entry when storing data
|
||||
scripts - script-handler. Add new scripts to object with scripts.add()
|
||||
cmdset - cmdset-handler. Use cmdset.add() to add new cmdsets to object
|
||||
nicks - nick-handler. New nicks with nicks.add().
|
||||
|
||||
|
||||
* Helper methods
|
||||
|
||||
msg(outgoing_string, from_obj=None, data=None)
|
||||
|
|
@ -68,12 +68,12 @@ class Player(TypeClass):
|
|||
* Hook methods
|
||||
|
||||
basetype_setup()
|
||||
at_player_creation()
|
||||
at_player_creation()
|
||||
|
||||
- note that the following hooks are also found on Objects and are
|
||||
usually handled on the character level:
|
||||
|
||||
at_init()
|
||||
at_init()
|
||||
at_cmdset_get()
|
||||
at_first_login()
|
||||
at_post_login()
|
||||
|
|
@ -86,11 +86,11 @@ class Player(TypeClass):
|
|||
"""
|
||||
super(Player, self).__init__(dbobj)
|
||||
|
||||
## methods inherited from database model
|
||||
## methods inherited from database model
|
||||
|
||||
def msg(self, outgoing_string, from_obj=None, data=None):
|
||||
"""
|
||||
Evennia -> User
|
||||
"""
|
||||
Evennia -> User
|
||||
This is the main route for sending data back to the user from the server.
|
||||
|
||||
outgoing_string (string) - text data to send
|
||||
|
|
@ -106,7 +106,7 @@ class Player(TypeClass):
|
|||
|
||||
new_character (Object) - character/object to swap to
|
||||
delete_old_character (bool) - delete the old character when swapping
|
||||
|
||||
|
||||
Returns: True/False depending on if swap suceeded or not.
|
||||
"""
|
||||
return self.dbobj.swap_character(new_character, delete_old_character=delete_old_character)
|
||||
|
|
@ -117,44 +117,44 @@ class Player(TypeClass):
|
|||
lets its typeclass execute the command. Evennia also calls
|
||||
this method whenever the player sends a command on the command line.
|
||||
|
||||
Argument:
|
||||
Argument:
|
||||
raw_string (string) - raw command input
|
||||
|
||||
Returns Deferred - this is an asynchronous Twisted object that will
|
||||
not fire until the command has actually finished executing. To overload
|
||||
this one needs to attach callback functions to it, with addCallback(function).
|
||||
this one needs to attach callback functions to it, with addCallback(function).
|
||||
This function will be called with an eventual return value from the command
|
||||
execution.
|
||||
execution.
|
||||
|
||||
This return is not used at all by Evennia by default, but might be useful
|
||||
for coders intending to implement some sort of nested command structure.
|
||||
for coders intending to implement some sort of nested command structure.
|
||||
"""
|
||||
self.dbobj.execute_cmd(raw_string)
|
||||
|
||||
def search(self, ostring, global_search=False, attribute_name=None, use_nicks=False,
|
||||
def search(self, ostring, global_search=False, attribute_name=None, use_nicks=False,
|
||||
location=None, ignore_errors=False, player=False):
|
||||
"""
|
||||
This method mimicks object.search if self.character is set. Otherwise only
|
||||
other Players can be searched with this method.
|
||||
other Players can be searched with this method.
|
||||
"""
|
||||
self.dbobj.search(ostring, global_search=global_search, attribute_name=attribute_name, use_nicks=use_nicks,
|
||||
self.dbobj.search(ostring, global_search=global_search, attribute_name=attribute_name, use_nicks=use_nicks,
|
||||
location=location, ignore_errors=ignore_errors, player=player)
|
||||
|
||||
|
||||
def is_typeclass(self, typeclass, exact=False):
|
||||
"""
|
||||
Returns true if this object has this type
|
||||
OR has a typeclass which is an subclass of
|
||||
the given typeclass.
|
||||
|
||||
|
||||
typeclass - can be a class object or the
|
||||
python path to such an object to match against.
|
||||
|
||||
python path to such an object to match against.
|
||||
|
||||
exact - returns true only if the object's
|
||||
type is exactly this typeclass, ignoring
|
||||
parents.
|
||||
|
||||
Returns: Boolean
|
||||
"""
|
||||
Returns: Boolean
|
||||
"""
|
||||
return self.dbobj.is_typeclass(typeclass, exact=exact)
|
||||
|
||||
def swap_typeclass(self, new_typeclass, clean_attributes=False, no_default=True):
|
||||
|
|
@ -162,18 +162,18 @@ class Player(TypeClass):
|
|||
This performs an in-situ swap of the typeclass. This means
|
||||
that in-game, this object will suddenly be something else.
|
||||
Player will not be affected. To 'move' a player to a different
|
||||
object entirely (while retaining this object's type), use
|
||||
object entirely (while retaining this object's type), use
|
||||
self.player.swap_object().
|
||||
|
||||
Note that this might be an error prone operation if the
|
||||
Note that this might be an error prone operation if the
|
||||
old/new typeclass was heavily customized - your code
|
||||
might expect one and not the other, so be careful to
|
||||
might expect one and not the other, so be careful to
|
||||
bug test your code if using this feature! Often its easiest
|
||||
to create a new object and just swap the player over to
|
||||
that one instead.
|
||||
that one instead.
|
||||
|
||||
Arguments:
|
||||
new_typeclass (path/classobj) - type to switch to
|
||||
Arguments:
|
||||
new_typeclass (path/classobj) - type to switch to
|
||||
clean_attributes (bool/list) - will delete all attributes
|
||||
stored on this object (but not any
|
||||
of the database fields such as name or
|
||||
|
|
@ -185,10 +185,10 @@ class Player(TypeClass):
|
|||
no_default - if this is active, the swapper will not allow for
|
||||
swapping to a default typeclass in case the given
|
||||
one fails for some reason. Instead the old one
|
||||
will be preserved.
|
||||
Returns:
|
||||
will be preserved.
|
||||
Returns:
|
||||
boolean True/False depending on if the swap worked or not.
|
||||
|
||||
|
||||
|
||||
"""
|
||||
self.dbobj.swap_typeclass(new_typeclass, clean_attributes=clean_attributes, no_default=no_default)
|
||||
|
|
@ -200,52 +200,52 @@ class Player(TypeClass):
|
|||
accessing_obj (Object)- object trying to access this one
|
||||
access_type (string) - type of access sought
|
||||
default (bool) - what to return if no lock of access_type was found
|
||||
"""
|
||||
"""
|
||||
return self.dbobj.access(accessing_obj, access_type=access_type, default=default)
|
||||
|
||||
def check_permstring(self, permstring):
|
||||
"""
|
||||
This explicitly checks the given string against this object's
|
||||
'permissions' property without involving any locks.
|
||||
|
||||
|
||||
permstring (string) - permission string that need to match a permission on the object.
|
||||
(example: 'Builders')
|
||||
"""
|
||||
return self.dbobj.check_permstring(permstring)
|
||||
|
||||
## player hooks
|
||||
|
||||
|
||||
def basetype_setup(self):
|
||||
"""
|
||||
This sets up the basic properties for a player.
|
||||
This sets up the basic properties for a player.
|
||||
Overload this with at_player_creation rather than
|
||||
changing this method.
|
||||
|
||||
changing this method.
|
||||
|
||||
"""
|
||||
# the text encoding to use.
|
||||
self.db.encoding = "utf-8"
|
||||
|
||||
|
||||
# A basic security setup
|
||||
self.locks.add("examine:perm(Wizards)")
|
||||
self.locks.add("edit:perm(Wizards)")
|
||||
self.locks.add("delete:perm(Wizards)")
|
||||
self.locks.add("boot:perm(Wizards)")
|
||||
self.locks.add("boot:perm(Wizards)")
|
||||
self.locks.add("msg:all()")
|
||||
|
||||
# The ooc player cmdset
|
||||
# The ooc player cmdset
|
||||
self.cmdset.add_default(CMDSET_OOC, permanent=True)
|
||||
self.cmdset.outside_access = False
|
||||
|
||||
self.cmdset.outside_access = False
|
||||
|
||||
def at_player_creation(self):
|
||||
"""
|
||||
This is called once, the very first time
|
||||
the player is created (i.e. first time they
|
||||
register with the game). It's a good place
|
||||
to store attributes all players should have,
|
||||
like configuration values etc.
|
||||
"""
|
||||
like configuration values etc.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
|
||||
def at_init(self):
|
||||
"""
|
||||
|
|
@ -257,7 +257,7 @@ class Player(TypeClass):
|
|||
happens the moment the player logs in or reconnects after a
|
||||
reload.
|
||||
"""
|
||||
pass
|
||||
pass
|
||||
|
||||
# Note that the hooks below also exist in the character object's
|
||||
# typeclass. You can often ignore these and rely on the character
|
||||
|
|
@ -292,7 +292,7 @@ class Player(TypeClass):
|
|||
"""
|
||||
Called at the end of the login
|
||||
process, just before letting
|
||||
them loose.
|
||||
them loose.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
|
@ -307,29 +307,29 @@ class Player(TypeClass):
|
|||
"""
|
||||
Called when any text is emitted to this
|
||||
object. If it returns False, no text
|
||||
will be sent automatically.
|
||||
will be sent automatically.
|
||||
"""
|
||||
return True
|
||||
return True
|
||||
|
||||
def at_message_send(self, message, to_object):
|
||||
"""
|
||||
Called whenever this object tries to send text
|
||||
to another object. Only called if the object supplied
|
||||
itself as a sender in the msg() call.
|
||||
itself as a sender in the msg() call.
|
||||
"""
|
||||
pass
|
||||
pass
|
||||
|
||||
def at_server_reload(self):
|
||||
"""
|
||||
This hook is called whenever the server is shutting down for restart/reboot.
|
||||
This hook is called whenever the server is shutting down for restart/reboot.
|
||||
If you want to, for example, save non-persistent properties across a restart,
|
||||
this is the place to do it.
|
||||
this is the place to do it.
|
||||
"""
|
||||
pass
|
||||
|
||||
def at_server_shutdown(self):
|
||||
"""
|
||||
This hook is called whenever the server is shutting down fully (i.e. not for
|
||||
a restart).
|
||||
This hook is called whenever the server is shutting down fully (i.e. not for
|
||||
a restart).
|
||||
"""
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -4,18 +4,19 @@ The custom manager for Scripts.
|
|||
|
||||
from src.typeclasses.managers import TypedObjectManager
|
||||
from src.typeclasses.managers import returns_typeclass_list
|
||||
__all__ = ("ScriptManager",)
|
||||
|
||||
VALIDATE_ITERATION = 0
|
||||
|
||||
class ScriptManager(TypedObjectManager):
|
||||
"""
|
||||
This Scriptmanager implements methods for searching
|
||||
This Scriptmanager implements methods for searching
|
||||
and manipulating Scripts directly from the database.
|
||||
|
||||
Evennia-specific search methods (will return Typeclasses or
|
||||
lists of Typeclasses, whereas Django-general methods will return
|
||||
Querysets or database objects).
|
||||
|
||||
Querysets or database objects).
|
||||
|
||||
dbref (converter)
|
||||
dbref_search
|
||||
get_dbref_range
|
||||
|
|
@ -38,15 +39,15 @@ class ScriptManager(TypedObjectManager):
|
|||
if not obj:
|
||||
return []
|
||||
scripts = self.filter(db_obj=obj)
|
||||
if key:
|
||||
if key:
|
||||
return scripts.filter(db_key=key)
|
||||
return scripts
|
||||
return scripts
|
||||
|
||||
@returns_typeclass_list
|
||||
def get_all_scripts(self, key=None):
|
||||
"""
|
||||
Return all scripts, alternative only
|
||||
scripts with a certain key/dbref or path.
|
||||
scripts with a certain key/dbref or path.
|
||||
"""
|
||||
if key:
|
||||
dbref = self.dbref(key)
|
||||
|
|
@ -66,7 +67,7 @@ class ScriptManager(TypedObjectManager):
|
|||
This stops and deletes a specific script directly
|
||||
from the script database. This might be
|
||||
needed for global scripts not tied to
|
||||
a specific game object.
|
||||
a specific game object.
|
||||
"""
|
||||
scripts = self.get_id(dbref)
|
||||
for script in scripts:
|
||||
|
|
@ -76,7 +77,7 @@ class ScriptManager(TypedObjectManager):
|
|||
"""
|
||||
This cleans up the script database of all non-persistent
|
||||
scripts, or only those on obj. It is called every time the server restarts
|
||||
and
|
||||
and
|
||||
"""
|
||||
if obj:
|
||||
to_stop = self.filter(db_persistent=False, db_obj=obj)
|
||||
|
|
@ -86,70 +87,70 @@ class ScriptManager(TypedObjectManager):
|
|||
for script in to_stop.filter(db_is_active=True):
|
||||
script.stop()
|
||||
for script in to_stop.filter(db_is_active=False):
|
||||
script.delete()
|
||||
script.delete()
|
||||
return nr_deleted
|
||||
|
||||
def validate(self, scripts=None, obj=None, key=None, dbref=None,
|
||||
def validate(self, scripts=None, obj=None, key=None, dbref=None,
|
||||
init_mode=False):
|
||||
"""
|
||||
This will step through the script database and make sure
|
||||
all objects run scripts that are still valid in the context
|
||||
they are in. This is called by the game engine at regular
|
||||
intervals but can also be initiated by player scripts.
|
||||
intervals but can also be initiated by player scripts.
|
||||
If key and/or obj is given, only update the related
|
||||
script/object.
|
||||
|
||||
Only one of the arguments are supposed to be supplied
|
||||
at a time, since they are exclusive to each other.
|
||||
|
||||
|
||||
scripts = a list of scripts objects obtained somewhere.
|
||||
obj = validate only scripts defined on a special object.
|
||||
key = validate only scripts with a particular key
|
||||
dbref = validate only the single script with this particular id.
|
||||
dbref = validate only the single script with this particular id.
|
||||
|
||||
init_mode - This is used during server upstart and can have
|
||||
three values:
|
||||
three values:
|
||||
False (no init mode). Called during run.
|
||||
"reset" - server reboot. Kill non-persistent scripts
|
||||
"reload" - server reload. Keep non-persistent scripts.
|
||||
|
||||
|
||||
This method also makes sure start any scripts it validates,
|
||||
this should be harmless, since already-active scripts
|
||||
have the property 'is_running' set and will be skipped.
|
||||
have the property 'is_running' set and will be skipped.
|
||||
"""
|
||||
|
||||
# we store a variable that tracks if we are calling a
|
||||
# validation from within another validation (avoids
|
||||
# loops).
|
||||
# we store a variable that tracks if we are calling a
|
||||
# validation from within another validation (avoids
|
||||
# loops).
|
||||
|
||||
global VALIDATE_ITERATION
|
||||
global VALIDATE_ITERATION
|
||||
if VALIDATE_ITERATION > 0:
|
||||
# we are in a nested validation. Exit.
|
||||
VALIDATE_ITERATION -= 1
|
||||
return None, None
|
||||
return None, None
|
||||
VALIDATE_ITERATION += 1
|
||||
|
||||
# not in a validation - loop. Validate as normal.
|
||||
|
||||
|
||||
nr_started = 0
|
||||
nr_stopped = 0
|
||||
nr_stopped = 0
|
||||
|
||||
if init_mode:
|
||||
if init_mode == 'reset':
|
||||
# special mode when server starts or object logs in.
|
||||
# This deletes all non-persistent scripts from database
|
||||
# special mode when server starts or object logs in.
|
||||
# This deletes all non-persistent scripts from database
|
||||
nr_stopped += self.remove_non_persistent(obj=obj)
|
||||
# turn off the activity flag for all remaining scripts
|
||||
scripts = self.get_all_scripts()
|
||||
for script in scripts:
|
||||
script.dbobj.is_active = False
|
||||
|
||||
script.dbobj.is_active = False
|
||||
|
||||
elif not scripts:
|
||||
# normal operation
|
||||
# normal operation
|
||||
if dbref and self.dbref(dbref):
|
||||
scripts = self.get_id(dbref)
|
||||
elif obj:
|
||||
scripts = self.get_all_scripts_on_obj(obj, key=key)
|
||||
scripts = self.get_all_scripts_on_obj(obj, key=key)
|
||||
else:
|
||||
scripts = self.get_all_scripts(key=key) #self.model.get_all_cached_instances()
|
||||
|
||||
|
|
@ -158,46 +159,46 @@ class ScriptManager(TypedObjectManager):
|
|||
VALIDATE_ITERATION -= 1
|
||||
return None, None
|
||||
|
||||
#print "scripts to validate: [%s]" % (", ".join(script.key for script in scripts))
|
||||
#print "scripts to validate: [%s]" % (", ".join(script.key for script in scripts))
|
||||
for script in scripts:
|
||||
#print "validating %s (%i) (init_mode=%s)" % (script.key, id(script.dbobj), init_mode)
|
||||
if script.is_valid():
|
||||
nr_started += script.start(force_restart=init_mode)
|
||||
#print "validating %s (%i) (init_mode=%s)" % (script.key, id(script.dbobj), init_mode)
|
||||
if script.is_valid():
|
||||
nr_started += script.start(force_restart=init_mode)
|
||||
#print "back from start. nr_started=", nr_started
|
||||
else:
|
||||
script.stop()
|
||||
nr_stopped += 1
|
||||
VALIDATE_ITERATION -= 1
|
||||
return nr_started, nr_stopped
|
||||
|
||||
|
||||
@returns_typeclass_list
|
||||
def script_search(self, ostring, obj=None, only_timed=False):
|
||||
"""
|
||||
Search for a particular script.
|
||||
|
||||
|
||||
ostring - search criterion - a script ID or key
|
||||
obj - limit search to scripts defined on this object
|
||||
only_timed - limit search only to scripts that run
|
||||
on a timer.
|
||||
on a timer.
|
||||
"""
|
||||
|
||||
ostring = ostring.strip()
|
||||
|
||||
|
||||
dbref = self.dbref(ostring)
|
||||
if dbref:
|
||||
# this is a dbref, try to find the script directly
|
||||
dbref_match = self.dbref_search(dbref)
|
||||
if dbref_match:
|
||||
ok = True
|
||||
ok = True
|
||||
if obj and obj != dbref_match.obj:
|
||||
ok = False
|
||||
if only_timed and dbref_match.interval:
|
||||
ok = False
|
||||
ok = False
|
||||
if ok:
|
||||
return [dbref_match]
|
||||
# not a dbref; normal search
|
||||
scripts = self.filter(db_key__iexact=ostring)
|
||||
|
||||
|
||||
if obj:
|
||||
scripts = scripts.exclude(db_obj=None).filter(db_obj__db_key__iexact=ostring)
|
||||
if only_timed:
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ from src.typeclasses.models import Attribute, TypedObject
|
|||
from django.contrib.contenttypes.models import ContentType
|
||||
from src.scripts.manager import ScriptManager
|
||||
|
||||
__all__ = ("ScriptAttribute", "ScriptDB")
|
||||
|
||||
#------------------------------------------------------------
|
||||
#
|
||||
# ScriptAttribute
|
||||
|
|
@ -121,122 +123,122 @@ class ScriptDB(TypedObject):
|
|||
|
||||
# desc property (wraps db_desc)
|
||||
#@property
|
||||
def desc_get(self):
|
||||
def __desc_get(self):
|
||||
"Getter. Allows for value = self.desc"
|
||||
return self.db_desc
|
||||
#@desc.setter
|
||||
def desc_set(self, value):
|
||||
def __desc_set(self, value):
|
||||
"Setter. Allows for self.desc = value"
|
||||
self.db_desc = value
|
||||
self.save()
|
||||
#@desc.deleter
|
||||
def desc_del(self):
|
||||
def __desc_del(self):
|
||||
"Deleter. Allows for del self.desc"
|
||||
self.db_desc = ""
|
||||
self.save()
|
||||
desc = property(desc_get, desc_set, desc_del)
|
||||
desc = property(__desc_get, __desc_set, __desc_del)
|
||||
|
||||
# obj property (wraps db_obj)
|
||||
#@property
|
||||
def obj_get(self):
|
||||
def __obj_get(self):
|
||||
"Getter. Allows for value = self.obj"
|
||||
return self.db_obj
|
||||
#@obj.setter
|
||||
def obj_set(self, value):
|
||||
def __obj_set(self, value):
|
||||
"Setter. Allows for self.obj = value"
|
||||
self.db_obj = value
|
||||
self.save()
|
||||
#@obj.deleter
|
||||
def obj_del(self):
|
||||
def __obj_del(self):
|
||||
"Deleter. Allows for del self.obj"
|
||||
self.db_obj = None
|
||||
self.save()
|
||||
obj = property(obj_get, obj_set, obj_del)
|
||||
obj = property(__obj_get, __obj_set, __obj_del)
|
||||
|
||||
# interval property (wraps db_interval)
|
||||
#@property
|
||||
def interval_get(self):
|
||||
def __interval_get(self):
|
||||
"Getter. Allows for value = self.interval"
|
||||
return self.db_interval
|
||||
#@interval.setter
|
||||
def interval_set(self, value):
|
||||
def __interval_set(self, value):
|
||||
"Setter. Allows for self.interval = value"
|
||||
self.db_interval = int(value)
|
||||
self.save()
|
||||
#@interval.deleter
|
||||
def interval_del(self):
|
||||
def __interval_del(self):
|
||||
"Deleter. Allows for del self.interval"
|
||||
self.db_interval = 0
|
||||
self.save()
|
||||
interval = property(interval_get, interval_set, interval_del)
|
||||
interval = property(__interval_get, __interval_set, __interval_del)
|
||||
|
||||
# start_delay property (wraps db_start_delay)
|
||||
#@property
|
||||
def start_delay_get(self):
|
||||
def __start_delay_get(self):
|
||||
"Getter. Allows for value = self.start_delay"
|
||||
return self.db_start_delay
|
||||
#@start_delay.setter
|
||||
def start_delay_set(self, value):
|
||||
def __start_delay_set(self, value):
|
||||
"Setter. Allows for self.start_delay = value"
|
||||
self.db_start_delay = value
|
||||
self.save()
|
||||
#@start_delay.deleter
|
||||
def start_delay_del(self):
|
||||
def __start_delay_del(self):
|
||||
"Deleter. Allows for del self.start_delay"
|
||||
self.db_start_delay = False
|
||||
self.save()
|
||||
start_delay = property(start_delay_get, start_delay_set, start_delay_del)
|
||||
start_delay = property(__start_delay_get, __start_delay_set, __start_delay_del)
|
||||
|
||||
# repeats property (wraps db_repeats)
|
||||
#@property
|
||||
def repeats_get(self):
|
||||
def __repeats_get(self):
|
||||
"Getter. Allows for value = self.repeats"
|
||||
return self.db_repeats
|
||||
#@repeats.setter
|
||||
def repeats_set(self, value):
|
||||
def __repeats_set(self, value):
|
||||
"Setter. Allows for self.repeats = value"
|
||||
self.db_repeats = int(value)
|
||||
self.save()
|
||||
#@repeats.deleter
|
||||
def repeats_del(self):
|
||||
def __repeats_del(self):
|
||||
"Deleter. Allows for del self.repeats"
|
||||
self.db_repeats = 0
|
||||
self.save()
|
||||
repeats = property(repeats_get, repeats_set, repeats_del)
|
||||
repeats = property(__repeats_get, __repeats_set, __repeats_del)
|
||||
|
||||
# persistent property (wraps db_persistent)
|
||||
#@property
|
||||
def persistent_get(self):
|
||||
def __persistent_get(self):
|
||||
"Getter. Allows for value = self.persistent"
|
||||
return self.db_persistent
|
||||
#@persistent.setter
|
||||
def persistent_set(self, value):
|
||||
def __persistent_set(self, value):
|
||||
"Setter. Allows for self.persistent = value"
|
||||
self.db_persistent = value
|
||||
self.save()
|
||||
#@persistent.deleter
|
||||
def persistent_del(self):
|
||||
def __persistent_del(self):
|
||||
"Deleter. Allows for del self.persistent"
|
||||
self.db_persistent = False
|
||||
self.save()
|
||||
persistent = property(persistent_get, persistent_set, persistent_del)
|
||||
persistent = property(__persistent_get, __persistent_set, __persistent_del)
|
||||
|
||||
# is_active property (wraps db_is_active)
|
||||
#@property
|
||||
def is_active_get(self):
|
||||
def __is_active_get(self):
|
||||
"Getter. Allows for value = self.is_active"
|
||||
return self.db_is_active
|
||||
#@is_active.setter
|
||||
def is_active_set(self, value):
|
||||
def __is_active_set(self, value):
|
||||
"Setter. Allows for self.is_active = value"
|
||||
self.db_is_active = value
|
||||
self.save()
|
||||
#@is_active.deleter
|
||||
def is_active_del(self):
|
||||
def __is_active_del(self):
|
||||
"Deleter. Allows for del self.is_active"
|
||||
self.db_is_active = False
|
||||
self.save()
|
||||
is_active = property(is_active_get, is_active_set, is_active_del)
|
||||
is_active = property(__is_active_get, __is_active_set, __is_active_del)
|
||||
|
||||
#
|
||||
#
|
||||
|
|
|
|||
|
|
@ -2,27 +2,29 @@
|
|||
This module contains the base Script class that all
|
||||
scripts are inheriting from.
|
||||
|
||||
It also defines a few common scripts.
|
||||
It also defines a few common scripts.
|
||||
"""
|
||||
|
||||
from time import time
|
||||
from time import time
|
||||
from twisted.internet.defer import maybeDeferred
|
||||
from twisted.internet.task import LoopingCall
|
||||
from twisted.internet import task
|
||||
from twisted.internet import task
|
||||
from src.server.sessionhandler import SESSIONS
|
||||
from src.typeclasses.typeclass import TypeClass
|
||||
from src.scripts.models import ScriptDB
|
||||
from src.comms import channelhandler
|
||||
from src.comms import channelhandler
|
||||
from src.utils import logger
|
||||
|
||||
__all__ = ("Script", "DoNothing", "CheckSessions", "ValidateScripts", "ValidateChannelHandler", "AddCmdSet")
|
||||
|
||||
#
|
||||
# Base script, inherit from Script below instead.
|
||||
# Base script, inherit from Script below instead.
|
||||
#
|
||||
class ScriptClass(TypeClass):
|
||||
"""
|
||||
Base class for scripts. Don't inherit from this, inherit from Script instead.
|
||||
"""
|
||||
# private methods
|
||||
# private methods
|
||||
|
||||
def __eq__(self, other):
|
||||
"""
|
||||
|
|
@ -32,7 +34,7 @@ class ScriptClass(TypeClass):
|
|||
try:
|
||||
return other.id == self.id
|
||||
except Exception:
|
||||
return False
|
||||
return False
|
||||
|
||||
def _start_task(self, start_now=True):
|
||||
"start task runner"
|
||||
|
|
@ -43,17 +45,17 @@ class ScriptClass(TypeClass):
|
|||
#print " start with paused time:", self.key, self.ndb._paused_time
|
||||
self.ndb.twisted_task.start(self.ndb._paused_time, now=False)
|
||||
else:
|
||||
# starting script anew.
|
||||
# starting script anew.
|
||||
#print "_start_task: self.interval:", self.key, self.dbobj.interval
|
||||
self.ndb.twisted_task.start(self.dbobj.interval, now=start_now and not self.start_delay)
|
||||
self.ndb.time_last_called = int(time())
|
||||
|
||||
self.ndb.time_last_called = int(time())
|
||||
|
||||
def _stop_task(self):
|
||||
"stop task runner"
|
||||
try:
|
||||
#print "stopping twisted task:", id(self.ndb.twisted_task), self.obj
|
||||
if self.ndb.twisted_task and self.ndb.twisted_task.running:
|
||||
self.ndb.twisted_task.stop()
|
||||
self.ndb.twisted_task.stop()
|
||||
except Exception:
|
||||
logger.log_trace()
|
||||
def _step_err_callback(self, e):
|
||||
|
|
@ -69,14 +71,14 @@ class ScriptClass(TypeClass):
|
|||
"step task runner. No try..except needed due to defer wrap."
|
||||
if not self.is_valid():
|
||||
self.stop()
|
||||
return
|
||||
return
|
||||
self.at_repeat()
|
||||
repeats = self.dbobj.db_repeats
|
||||
if repeats <= 0:
|
||||
pass # infinite repeat
|
||||
elif repeats == 1:
|
||||
self.stop()
|
||||
return
|
||||
return
|
||||
else:
|
||||
self.dbobj.db_repeats -= 1
|
||||
self.ndb.time_last_called = int(time())
|
||||
|
|
@ -84,7 +86,7 @@ class ScriptClass(TypeClass):
|
|||
|
||||
if self.ndb._paused_time:
|
||||
# this means we were running an unpaused script, for the time remaining
|
||||
# after the pause. Now we start a normal-running timer again.
|
||||
# after the pause. Now we start a normal-running timer again.
|
||||
#print "switching to normal run:", self.key
|
||||
del self.ndb._paused_time
|
||||
self._stop_task()
|
||||
|
|
@ -93,23 +95,23 @@ class ScriptClass(TypeClass):
|
|||
|
||||
def _step_task(self):
|
||||
"step task"
|
||||
try:
|
||||
try:
|
||||
d = maybeDeferred(self._step_succ_callback)
|
||||
d.addErrback(self._step_err_callback)
|
||||
d.addErrback(self._step_err_callback)
|
||||
return d
|
||||
except Exception:
|
||||
logger.log_trace()
|
||||
logger.log_trace()
|
||||
|
||||
|
||||
# Public methods
|
||||
|
||||
# Public methods
|
||||
|
||||
def time_until_next_repeat(self):
|
||||
"""
|
||||
Returns the time in seconds until the script will be
|
||||
run again. If this is not a stepping script, returns None.
|
||||
run again. If this is not a stepping script, returns None.
|
||||
This is not used in any way by the script's stepping
|
||||
system; it's only here for the user to be able to
|
||||
check in on their scripts and when they will next be run.
|
||||
check in on their scripts and when they will next be run.
|
||||
"""
|
||||
try:
|
||||
if self.ndb._paused_time:
|
||||
|
|
@ -117,7 +119,7 @@ class ScriptClass(TypeClass):
|
|||
else:
|
||||
return max(0, (self.ndb.time_last_called + self.dbobj.db_interval) - int(time()))
|
||||
except Exception:
|
||||
return None
|
||||
return None
|
||||
|
||||
def start(self, force_restart=False):
|
||||
"""
|
||||
|
|
@ -125,33 +127,33 @@ class ScriptClass(TypeClass):
|
|||
persistent scripts, this is usually once every server start)
|
||||
|
||||
force_restart - if True, will always restart the script, regardless
|
||||
of if it has started before.
|
||||
|
||||
returns 0 or 1 to indicated the script has been started or not. Used in counting.
|
||||
of if it has started before.
|
||||
|
||||
returns 0 or 1 to indicated the script has been started or not. Used in counting.
|
||||
"""
|
||||
#print "Script %s (%s) start (active:%s, force:%s) ..." % (self.key, id(self.dbobj),
|
||||
# self.is_active, force_restart)
|
||||
#print "Script %s (%s) start (active:%s, force:%s) ..." % (self.key, id(self.dbobj),
|
||||
# self.is_active, force_restart)
|
||||
|
||||
if self.dbobj.is_active and not force_restart:
|
||||
# script already runs and should not be restarted.
|
||||
return 0
|
||||
return 0
|
||||
|
||||
obj = self.obj
|
||||
if obj:
|
||||
# check so the scripted object is valid and initalized
|
||||
if obj:
|
||||
# check so the scripted object is valid and initalized
|
||||
try:
|
||||
dummy = object.__getattribute__(obj, 'cmdset')
|
||||
dummy = object.__getattribute__(obj, 'cmdset')
|
||||
except AttributeError:
|
||||
# this means the object is not initialized.
|
||||
self.dbobj.is_active = False
|
||||
return 0
|
||||
return 0
|
||||
|
||||
# try to restart a paused script
|
||||
# try to restart a paused script
|
||||
if self.unpause():
|
||||
return 1
|
||||
|
||||
# try to start the script from scratch
|
||||
try:
|
||||
# try to start the script from scratch
|
||||
try:
|
||||
self.dbobj.is_active = True
|
||||
self.at_start()
|
||||
if self.dbobj.db_interval > 0:
|
||||
|
|
@ -159,15 +161,15 @@ class ScriptClass(TypeClass):
|
|||
return 1
|
||||
except Exception:
|
||||
logger.log_trace()
|
||||
self.dbobj.is_active = False
|
||||
self.dbobj.is_active = False
|
||||
return 0
|
||||
|
||||
def stop(self, kill=False):
|
||||
"""
|
||||
Called to stop the script from running.
|
||||
This also deletes the script.
|
||||
This also deletes the script.
|
||||
|
||||
kill - don't call finishing hooks.
|
||||
kill - don't call finishing hooks.
|
||||
"""
|
||||
#print "stopping script %s" % self.key
|
||||
#import pdb
|
||||
|
|
@ -197,29 +199,29 @@ class ScriptClass(TypeClass):
|
|||
#print "pausing", self.key, self.time_until_next_repeat()
|
||||
dt = self.time_until_next_repeat()
|
||||
if dt == None:
|
||||
return
|
||||
self.db._paused_time = dt
|
||||
return
|
||||
self.db._paused_time = dt
|
||||
self._stop_task()
|
||||
|
||||
|
||||
def unpause(self):
|
||||
"""
|
||||
Restart a paused script. This WILL call at_start().
|
||||
Restart a paused script. This WILL call at_start().
|
||||
"""
|
||||
#print "unpausing", self.key, self.db._paused_time
|
||||
dt = self.db._paused_time
|
||||
dt = self.db._paused_time
|
||||
if dt == None:
|
||||
return False
|
||||
try:
|
||||
self.dbobj.is_active = True
|
||||
self.at_start()
|
||||
self.ndb._paused_time = dt
|
||||
self.ndb._paused_time = dt
|
||||
self._start_task(start_now=False)
|
||||
del self.db._paused_time
|
||||
except Exception, e:
|
||||
logger.log_trace()
|
||||
self.dbobj.is_active = False
|
||||
return False
|
||||
return True
|
||||
return True
|
||||
|
||||
# hooks
|
||||
def at_script_creation(self):
|
||||
|
|
@ -230,7 +232,7 @@ class ScriptClass(TypeClass):
|
|||
pass
|
||||
def at_start(self):
|
||||
"placeholder."
|
||||
pass
|
||||
pass
|
||||
def at_stop(self):
|
||||
"placeholder"
|
||||
pass
|
||||
|
|
@ -240,7 +242,7 @@ class ScriptClass(TypeClass):
|
|||
def at_init(self):
|
||||
"called when typeclass re-caches. Usually not used for scripts."
|
||||
pass
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Base Script - inherit from this
|
||||
|
|
@ -255,36 +257,36 @@ class Script(ScriptClass):
|
|||
def __init__(self, dbobj):
|
||||
"""
|
||||
This is the base TypeClass for all Scripts. Scripts describe events, timers and states in game,
|
||||
they can have a time component or describe a state that changes under certain conditions.
|
||||
they can have a time component or describe a state that changes under certain conditions.
|
||||
|
||||
Script API:
|
||||
|
||||
* Available properties (only available on initiated Typeclass objects)
|
||||
|
||||
key (string) - name of object
|
||||
key (string) - name of object
|
||||
name (string)- same as key
|
||||
aliases (list of strings) - aliases to the object. Will be saved to database as AliasDB entries but returned as strings.
|
||||
dbref (int, read-only) - unique #id-number. Also "id" can be used.
|
||||
dbobj (Object, read-only) - link to database model. dbobj.typeclass points back to this class
|
||||
typeclass (Object, read-only) - this links back to this class as an identified only. Use self.swap_typeclass() to switch.
|
||||
date_created (string) - time stamp of object creation
|
||||
permissions (list of strings) - list of permission strings
|
||||
permissions (list of strings) - list of permission strings
|
||||
|
||||
desc (string) - optional description of script, shown in listings
|
||||
obj (Object) - optional object that this script is connected to and acts on (set automatically by obj.scripts.add())
|
||||
interval (int) - how often script should run, in seconds. <0 turns off ticker
|
||||
obj (Object) - optional object that this script is connected to and acts on (set automatically by obj.scripts.add())
|
||||
interval (int) - how often script should run, in seconds. <=0 turns off ticker
|
||||
start_delay (bool) - if the script should start repeating right away or wait self.interval seconds
|
||||
repeats (int) - how many times the script should repeat before stopping. 0 means infinite repeats
|
||||
repeats (int) - how many times the script should repeat before stopping. <=0 means infinite repeats
|
||||
persistent (bool) - if script should survive a server shutdown or not
|
||||
is_active (bool) - if script is currently running
|
||||
is_active (bool) - if script is currently running
|
||||
|
||||
* Handlers
|
||||
|
||||
locks - lock-handler: use locks.add() to add new lock strings
|
||||
db - attribute-handler: store/retrieve database attributes on this self.db.myattr=val, val=self.db.myattr
|
||||
ndb - non-persistent attribute handler: same as db but does not create a database entry when storing data
|
||||
ndb - non-persistent attribute handler: same as db but does not create a database entry when storing data
|
||||
|
||||
* Helper methods
|
||||
* Helper methods
|
||||
|
||||
start() - start script (this usually happens automatically at creation and obj.script.add() etc)
|
||||
stop() - stop script, and delete it
|
||||
|
|
@ -292,48 +294,48 @@ class Script(ScriptClass):
|
|||
unpause() - restart a previously paused script. The script will continue as if it was never paused.
|
||||
time_until_next_repeat() - if a timed script (interval>0), returns time until next tick
|
||||
|
||||
* Hook methods
|
||||
* Hook methods
|
||||
|
||||
at_script_creation() - called only once, when an object of this
|
||||
class is first created.
|
||||
is_valid() - is called to check if the script is valid to be running
|
||||
at the current time. If is_valid() returns False, the running
|
||||
script is stopped and removed from the game. You can use this
|
||||
to check state changes (i.e. an script tracking some combat
|
||||
stats at regular intervals is only valid to run while there is
|
||||
actual combat going on).
|
||||
to check state changes (i.e. an script tracking some combat
|
||||
stats at regular intervals is only valid to run while there is
|
||||
actual combat going on).
|
||||
at_start() - Called every time the script is started, which for persistent
|
||||
scripts is at least once every server start. Note that this is
|
||||
unaffected by self.delay_start, which only delays the first call
|
||||
to at_repeat().
|
||||
to at_repeat().
|
||||
at_repeat() - Called every self.interval seconds. It will be called immediately
|
||||
upon launch unless self.delay_start is True, which will delay
|
||||
the first call of this method by self.interval seconds. If
|
||||
self.interval==0, this method will never be called.
|
||||
the first call of this method by self.interval seconds. If
|
||||
self.interval<=0, this method will never be called.
|
||||
at_stop() - Called as the script object is stopped and is about to be removed from
|
||||
the game, e.g. because is_valid() returned False.
|
||||
at_server_reload() - Called when server reloads. Can be used to save temporary
|
||||
at_server_reload() - Called when server reloads. Can be used to save temporary
|
||||
variables you want should survive a reload.
|
||||
at_server_shutdown() - called at a full server shutdown.
|
||||
at_server_shutdown() - called at a full server shutdown.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
"""
|
||||
super(Script, self).__init__(dbobj)
|
||||
|
||||
def at_script_creation(self):
|
||||
"""
|
||||
Only called once, by the create function.
|
||||
"""
|
||||
self.key = "<unnamed>"
|
||||
self.key = "<unnamed>"
|
||||
self.desc = ""
|
||||
self.interval = 0 # infinite
|
||||
self.start_delay = False
|
||||
self.repeats = 0 # infinite
|
||||
self.persistent = False
|
||||
|
||||
self.persistent = False
|
||||
|
||||
def is_valid(self):
|
||||
"""
|
||||
Is called to check if the script is valid to run at this time.
|
||||
Is called to check if the script is valid to run at this time.
|
||||
Should return a boolean. The method is assumed to collect all needed
|
||||
information from its related self.obj.
|
||||
"""
|
||||
|
|
@ -342,7 +344,7 @@ class Script(ScriptClass):
|
|||
def at_start(self):
|
||||
"""
|
||||
Called whenever the script is started, which for persistent
|
||||
scripts is at least once every server start. It will also be called
|
||||
scripts is at least once every server start. It will also be called
|
||||
when starting again after a pause (such as after a server reload)
|
||||
"""
|
||||
pass
|
||||
|
|
@ -350,10 +352,10 @@ class Script(ScriptClass):
|
|||
def at_repeat(self):
|
||||
"""
|
||||
Called repeatedly if this Script is set to repeat
|
||||
regularly.
|
||||
regularly.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def at_stop(self):
|
||||
"""
|
||||
Called whenever when it's time for this script to stop
|
||||
|
|
@ -363,38 +365,38 @@ class Script(ScriptClass):
|
|||
|
||||
def at_server_reload(self):
|
||||
"""
|
||||
This hook is called whenever the server is shutting down for restart/reboot.
|
||||
This hook is called whenever the server is shutting down for restart/reboot.
|
||||
If you want to, for example, save non-persistent properties across a restart,
|
||||
this is the place to do it.
|
||||
this is the place to do it.
|
||||
"""
|
||||
pass
|
||||
|
||||
def at_server_shutdown(self):
|
||||
"""
|
||||
This hook is called whenever the server is shutting down fully (i.e. not for
|
||||
a restart).
|
||||
This hook is called whenever the server is shutting down fully (i.e. not for
|
||||
a restart).
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
|
||||
# Some useful default Script types used by Evennia.
|
||||
# Some useful default Script types used by Evennia.
|
||||
|
||||
class DoNothing(Script):
|
||||
"An script that does nothing. Used as default fallback."
|
||||
def at_script_creation(self):
|
||||
"An script that does nothing. Used as default fallback."
|
||||
def at_script_creation(self):
|
||||
"Setup the script"
|
||||
self.key = "sys_do_nothing"
|
||||
self.desc = "This is a placeholder script."
|
||||
|
||||
self.desc = "This is a placeholder script."
|
||||
|
||||
class CheckSessions(Script):
|
||||
"Check sessions regularly."
|
||||
def at_script_creation(self):
|
||||
"Setup the script"
|
||||
self.key = "sys_session_check"
|
||||
self.desc = "Checks sessions so they are live."
|
||||
self.interval = 60 # repeat every 60 seconds
|
||||
self.persistent = True
|
||||
self.desc = "Checks sessions so they are live."
|
||||
self.interval = 60 # repeat every 60 seconds
|
||||
self.persistent = True
|
||||
|
||||
def at_repeat(self):
|
||||
"called every 60 seconds"
|
||||
|
|
@ -403,7 +405,7 @@ class CheckSessions(Script):
|
|||
SESSIONS.validate_sessions()
|
||||
|
||||
class ValidateScripts(Script):
|
||||
"Check script validation regularly"
|
||||
"Check script validation regularly"
|
||||
def at_script_creation(self):
|
||||
"Setup the script"
|
||||
self.key = "sys_scripts_validate"
|
||||
|
|
@ -412,31 +414,31 @@ class ValidateScripts(Script):
|
|||
self.persistent = True
|
||||
|
||||
def at_repeat(self):
|
||||
"called every hour"
|
||||
"called every hour"
|
||||
#print "ValidateScripts run."
|
||||
ScriptDB.objects.validate()
|
||||
|
||||
class ValidateChannelHandler(Script):
|
||||
"Update the channelhandler to make sure it's in sync."
|
||||
"Update the channelhandler to make sure it's in sync."
|
||||
|
||||
def at_script_creation(self):
|
||||
"Setup the script"
|
||||
self.key = "sys_channels_validate"
|
||||
self.desc = "Updates the channel handler"
|
||||
self.desc = "Updates the channel handler"
|
||||
self.interval = 3700 # validate a little later than ValidateScripts
|
||||
self.persistent = True
|
||||
|
||||
|
||||
def at_repeat(self):
|
||||
"called every hour+"
|
||||
#print "ValidateChannelHandler run."
|
||||
channelhandler.CHANNELHANDLER.update()
|
||||
|
||||
|
||||
class AddCmdSet(Script):
|
||||
"""
|
||||
This script permanently assigns a command set
|
||||
to an object whenever it is started. This is not
|
||||
used by the core system anymore, it's here mostly
|
||||
as an example.
|
||||
as an example.
|
||||
"""
|
||||
def at_script_creation(self):
|
||||
"Setup the script"
|
||||
|
|
@ -444,12 +446,12 @@ class AddCmdSet(Script):
|
|||
self.key = "add_cmdset"
|
||||
if not self.desc:
|
||||
self.desc = "Adds a cmdset to an object."
|
||||
self.persistent = True
|
||||
self.persistent = True
|
||||
|
||||
# this needs to be assigned to upon creation.
|
||||
# It should be a string pointing to the right
|
||||
# cmdset module and cmdset class name, e.g.
|
||||
# 'examples.cmdset_redbutton.RedButtonCmdSet'
|
||||
# cmdset module and cmdset class name, e.g.
|
||||
# 'examples.cmdset_redbutton.RedButtonCmdSet'
|
||||
# self.db.cmdset = <cmdset_path>
|
||||
# self.db.add_default = <bool>
|
||||
|
||||
|
|
@ -461,12 +463,12 @@ class AddCmdSet(Script):
|
|||
self.obj.cmdset.add_default(cmdset)
|
||||
else:
|
||||
self.obj.cmdset.add(cmdset)
|
||||
|
||||
|
||||
def at_stop(self):
|
||||
"""
|
||||
This removes the cmdset when the script stops
|
||||
"""
|
||||
cmdset = self.db.cmdset
|
||||
cmdset = self.db.cmdset
|
||||
if cmdset:
|
||||
if self.db.add_default:
|
||||
self.obj.cmdset.delete_default()
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from functools import update_wrapper
|
|||
from django.db import models
|
||||
from src.utils import idmapper
|
||||
from src.utils.utils import make_iter
|
||||
#from src.typeclasses import idmap
|
||||
__all__ = ("AttributeManager", "TypedObjectManager")
|
||||
|
||||
# Managers
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,8 @@ from src.locks.lockhandler import LockHandler
|
|||
from src.utils import logger, utils
|
||||
from src.utils.utils import make_iter, is_iter, has_parent, to_unicode, to_str
|
||||
|
||||
__all__ = ("Attribute", "TypeNick", "TypedObject")
|
||||
|
||||
_PERMISSION_HIERARCHY = [p.lower() for p in settings.PERMISSION_HIERARCHY]
|
||||
|
||||
_CTYPEGET = ContentType.objects.get
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ used by the typesystem or django itself.
|
|||
from src.utils.logger import log_trace, log_errmsg
|
||||
from django.conf import settings
|
||||
|
||||
__all__ = ("TypeClass",)
|
||||
|
||||
# these are called so many times it's worth to avoid lookup calls
|
||||
_GA = object.__getattribute__
|
||||
_SA = object.__setattr__
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue