Further cleanup of source; making class methods _private for clarity in the API.

This commit is contained in:
Griatch 2012-03-31 15:09:22 +02:00
parent fc156b5a54
commit c8df141e89
18 changed files with 607 additions and 588 deletions

View file

@ -4,7 +4,7 @@ Models for the comsystem.
The comsystem's main component is the Message, which The comsystem's main component is the Message, which
carries the actual information between two parties. carries the actual information between two parties.
Msgs are stored in the database and usually not Msgs are stored in the database and usually not
deleted. deleted.
A Msg always have one sender (a user), but can have A Msg always have one sender (a user), but can have
any number targets, both users and channels. 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 django.db import models
from src.utils.idmapper.models import SharedMemoryModel 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.locks.lockhandler import LockHandler
from src.utils import logger from src.utils import logger
from src.utils.utils import is_iter, to_str from src.utils.utils import is_iter, to_str
@ -35,18 +35,18 @@ def obj_to_id(inp):
""" """
dbref = is_dbref(inp) dbref = is_dbref(inp)
if dbref: if dbref:
return str(dbref) return str(dbref)
if hasattr(inp, 'id'): if hasattr(inp, 'id'):
return str(inp.id) return str(inp.id)
if hasattr(inp, 'dbobj') and hasattr(inp.dbobj, 'id'): if hasattr(inp, 'dbobj') and hasattr(inp.dbobj, 'id'):
return str(inp.dbobj.id) return str(inp.dbobj.id)
return str(inp) return str(inp)
def id_to_obj(dbref, db_model='PlayerDB'): def id_to_obj(dbref, db_model='PlayerDB'):
""" """
loads from dbref to object. Uses the db_model to search loads from dbref to object. Uses the db_model to search
for the id. for the id.
""" """
if db_model == 'PlayerDB': if db_model == 'PlayerDB':
from src.players.models import PlayerDB as db_model from src.players.models import PlayerDB as db_model
else: else:
@ -55,7 +55,7 @@ def id_to_obj(dbref, db_model='PlayerDB'):
dbref = int(dbref.strip()) dbref = int(dbref.strip())
return db_model.objects.get(id=dbref) return db_model.objects.get(id=dbref)
except Exception: except Exception:
return None return None
#------------------------------------------------------------ #------------------------------------------------------------
# #
@ -67,18 +67,18 @@ class Msg(SharedMemoryModel):
""" """
A single message. This model describes all ooc messages A single message. This model describes all ooc messages
sent in-game, both to channels and between players. sent in-game, both to channels and between players.
The Msg class defines the following properties: The Msg class defines the following properties:
sender - sender of message sender - sender of message
receivers - list of target objects for message receivers - list of target objects for message
channels - list of channels message was sent to channels - list of channels message was sent to
message - the text being sent message - the text being sent
date_sent - time message was 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_receivers - list of receiver objects to hide message from
hide_from_channels - list of channels objects to hide message from hide_from_channels - list of channels objects to hide message from
permissions - perm strings permissions - perm strings
""" """
# #
# Msg database model setup # Msg database model setup
@ -90,24 +90,24 @@ class Msg(SharedMemoryModel):
# There must always be one sender of the message. # There must always be one sender of the message.
db_sender = models.ForeignKey("players.PlayerDB", related_name='sender_set', null=True, verbose_name='sender') 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 # 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).") 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 # The destination objects of this message. Stored as a
# comma-separated string of object dbrefs. Can be defined along # comma-separated string of object dbrefs. Can be defined along
# with channels below. # with channels below.
db_receivers = models.CharField('receivers', max_length=255, null=True, blank=True, 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.') help_text='comma-separated list of object dbrefs this message is aimed at.')
# The channels this message was sent to. Stored as a # The channels this message was sent to. Stored as a
# comma-separated string of channel dbrefs. A message can both # comma-separated string of channel dbrefs. A message can both
# have channel targets and destination objects. # 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.') help_text='comma-separated list of channel dbrefs this message is aimed at.')
# The actual message and a timestamp. The message field # 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_message = models.TextField('messsage')
db_date_sent = models.DateTimeField('date sent', editable=False, auto_now_add=True) db_date_sent = models.DateTimeField('date sent', editable=False, auto_now_add=True)
# lock storage # 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.') help_text='access locks on this message.')
# These are settable by senders/receivers/channels respectively. # These are settable by senders/receivers/channels respectively.
# Stored as a comma-separated string of dbrefs. Can be used by the # 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_receivers = models.CharField(max_length=255, null=True, blank=True)
db_hide_from_channels = 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 # Storage of lock strings
#db_lock_storage = models.TextField(null=True) #db_lock_storage = models.TextField(null=True)
# Database manager # Database manager
objects = managers.MsgManager() objects = managers.MsgManager()
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -134,8 +134,8 @@ class Msg(SharedMemoryModel):
# @property decorators that allows to access these fields using # @property decorators that allows to access these fields using
# normal python operations (without having to remember to save() # normal python operations (without having to remember to save()
# etc). So e.g. a property 'attr' has a get/set/del decorator # etc). So e.g. a property 'attr' has a get/set/del decorator
# defined that allows the user to do self.attr = value, # defined that allows the user to do self.attr = value,
# value = self.attr and del self.attr respectively (where self # value = self.attr and del self.attr respectively (where self
# is the object in question). # is the object in question).
# sender property (wraps db_sender) # sender property (wraps db_sender)
@ -183,7 +183,7 @@ class Msg(SharedMemoryModel):
if is_iter(value): if is_iter(value):
value = ",".join([obj_to_id(val) for val in value]) value = ",".join([obj_to_id(val) for val in value])
self.db_receivers = obj_to_id(value) self.db_receivers = obj_to_id(value)
self.save() self.save()
#@receivers.deleter #@receivers.deleter
def receivers_del(self): def receivers_del(self):
"Deleter. Allows for del self.receivers" "Deleter. Allows for del self.receivers"
@ -204,7 +204,7 @@ class Msg(SharedMemoryModel):
if is_iter(value): if is_iter(value):
value = ",".join([obj_to_id(val) for val in value]) value = ",".join([obj_to_id(val) for val in value])
self.db_channels = obj_to_id(value) self.db_channels = obj_to_id(value)
self.save() self.save()
#@channels.deleter #@channels.deleter
def channels_del(self): def channels_del(self):
"Deleter. Allows for del self.channels" "Deleter. Allows for del self.channels"
@ -253,7 +253,7 @@ class Msg(SharedMemoryModel):
def hide_from_sender_set(self, value): def hide_from_sender_set(self, value):
"Setter. Allows for self.hide_from_senders = value." "Setter. Allows for self.hide_from_senders = value."
self.db_hide_from_sender = value self.db_hide_from_sender = value
self.save() self.save()
#@hide_from_sender.deleter #@hide_from_sender.deleter
def hide_from_sender_del(self): def hide_from_sender_del(self):
"Deleter. Allows for del self.hide_from_senders" "Deleter. Allows for del self.hide_from_senders"
@ -274,7 +274,7 @@ class Msg(SharedMemoryModel):
if is_iter(value): if is_iter(value):
value = ",".join([obj_to_id(val) for val in value]) value = ",".join([obj_to_id(val) for val in value])
self.db_hide_from_receivers = obj_to_id(value) self.db_hide_from_receivers = obj_to_id(value)
self.save() self.save()
#@hide_from_receivers.deleter #@hide_from_receivers.deleter
def hide_from_receivers_del(self): def hide_from_receivers_del(self):
"Deleter. Allows for del self.hide_from_receivers" "Deleter. Allows for del self.hide_from_receivers"
@ -286,7 +286,7 @@ class Msg(SharedMemoryModel):
#@property #@property
def hide_from_channels_get(self): def hide_from_channels_get(self):
"Getter. Allows for value = self.hide_from_channels. Returns a list of hide_from_channels." "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 [id_to_obj(dbref) for dbref in self.db_hide_from_channels.split(',')]
return [] return []
#@hide_from_channels.setter #@hide_from_channels.setter
@ -295,7 +295,7 @@ class Msg(SharedMemoryModel):
if is_iter(value): if is_iter(value):
value = ",".join([obj_to_id(val) for val in value]) value = ",".join([obj_to_id(val) for val in value])
self.db_hide_from_channels = obj_to_id(value) self.db_hide_from_channels = obj_to_id(value)
self.save() self.save()
#@hide_from_channels.deleter #@hide_from_channels.deleter
def hide_from_channels_del(self): def hide_from_channels_del(self):
"Deleter. Allows for del self.hide_from_channels" "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) hide_from_channels = property(hide_from_channels_get, hide_from_channels_set, hide_from_channels_del)
# lock_storage property (wraps db_lock_storage) # lock_storage property (wraps db_lock_storage)
#@property #@property
def lock_storage_get(self): def lock_storage_get(self):
"Getter. Allows for value = self.lock_storage" "Getter. Allows for value = self.lock_storage"
return self.db_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 db_model_name = "msg" # used by attributes to safely store objects
# #
# Msg class methods # Msg class methods
# #
def __str__(self): def __str__(self):
"Print text" "Print text"
if self.channels: if self.channels:
return "%s -> %s: %s" % (self.sender.key, return "%s -> %s: %s" % (self.sender.key,
", ".join([chan.key for chan in self.channels]), ", ".join([chan.key for chan in self.channels]),
self.message) self.message)
else: else:
return "%s -> %s: %s" % (self.sender.key, return "%s -> %s: %s" % (self.sender.key,
@ -341,7 +341,7 @@ class Msg(SharedMemoryModel):
accessing_obj - object trying to access this one accessing_obj - object trying to access this one
access_type - type of access sought access_type - type of access sought
default - what to return if no lock of access_type was found default - what to return if no lock of access_type was found
""" """
return self.locks.check(accessing_obj, access_type=access_type, default=default) return self.locks.check(accessing_obj, access_type=access_type, default=default)
@ -353,61 +353,61 @@ class Msg(SharedMemoryModel):
class TempMsg(object): class TempMsg(object):
""" """
This is a non-persistent object for sending This is a non-persistent object for sending
temporary messages that will not be stored. temporary messages that will not be stored.
It mimics the "real" Msg object, but don't require It mimics the "real" Msg object, but don't require
sender to be given. sender to be given.
""" """
def __init__(self, sender=None, receivers=[], channels=[], message="", permissions=[]): def __init__(self, sender=None, receivers=[], channels=[], message="", permissions=[]):
self.sender = sender self.sender = sender
self.receivers = receivers self.receivers = receivers
self.message = message self.message = message
self.permissions = permissions self.permissions = permissions
self.hide_from_sender = False self.hide_from_sender = False
self.hide_from_sender = receivers = False self.hide_from_sender = receivers = False
self.hide_from_channels = False self.hide_from_channels = False
#------------------------------------------------------------ #------------------------------------------------------------
# #
# Channel # Channel
# #
#------------------------------------------------------------ #------------------------------------------------------------
class Channel(SharedMemoryModel): class Channel(SharedMemoryModel):
""" """
This is the basis of a comm channel, only implementing 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: The Channel class defines the following properties:
key - main name for channel key - main name for channel
desc - optional description of channel desc - optional description of channel
aliases - alternative names for the channel aliases - alternative names for the channel
keep_log - bool if the channel should remember messages keep_log - bool if the channel should remember messages
permissions - perm strings permissions - perm strings
""" """
# #
# Channel database model setup # Channel database model setup
# #
# #
# These databse fields are all set using their corresponding properties, # These databse fields are all set using their corresponding properties,
# named same as the field, but withtout the db_* prefix. # named same as the field, but withtout the db_* prefix.
# unique identifier for this channel # unique identifier for this channel
db_key = models.CharField('key', max_length=255, unique=True, db_index=True) db_key = models.CharField('key', max_length=255, unique=True, db_index=True)
# optional description of channel # optional description of channel
db_desc = models.CharField('description', max_length=80, blank=True, null=True) db_desc = models.CharField('description', max_length=80, blank=True, null=True)
# aliases for the channel. These are searched by cmdhandler # aliases for the channel. These are searched by cmdhandler
# as well to determine if a command is the name of a channel. # as well to determine if a command is the name of a channel.
# Several aliases are separated by commas. # Several aliases are separated by commas.
db_aliases = models.CharField('aliases', max_length=255) db_aliases = models.CharField('aliases', max_length=255)
# Whether this channel should remember its past messages # Whether this channel should remember its past messages
db_keep_log = models.BooleanField(default=True) db_keep_log = models.BooleanField(default=True)
# Storage of lock definitions # Storage of lock definitions
db_lock_storage = models.CharField('locks', max_length=512, blank=True) db_lock_storage = models.CharField('locks', max_length=512, blank=True)
# Database manager # Database manager
objects = managers.ChannelManager() objects = managers.ChannelManager()
@ -416,15 +416,15 @@ class Channel(SharedMemoryModel):
verbose_name = "Channel" verbose_name = "Channel"
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
SharedMemoryModel.__init__(self, *args, **kwargs) SharedMemoryModel.__init__(self, *args, **kwargs)
self.locks = LockHandler(self) self.locks = LockHandler(self)
# Wrapper properties to easily set database fields. These are # Wrapper properties to easily set database fields. These are
# @property decorators that allows to access these fields using # @property decorators that allows to access these fields using
# normal python operations (without having to remember to save() # normal python operations (without having to remember to save()
# etc). So e.g. a property 'attr' has a get/set/del decorator # etc). So e.g. a property 'attr' has a get/set/del decorator
# defined that allows the user to do self.attr = value, # defined that allows the user to do self.attr = value,
# value = self.attr and del self.attr respectively (where self # value = self.attr and del self.attr respectively (where self
# is the object in question). # is the object in question).
# key property (wraps db_key) # key property (wraps db_key)
@ -473,14 +473,14 @@ class Channel(SharedMemoryModel):
if is_iter(value): if is_iter(value):
value = ",".join([str(val).strip().lower() for val in value]) value = ",".join([str(val).strip().lower() for val in value])
self.db_aliases = value self.db_aliases = value
self.save() self.save()
#@aliases_del.deleter #@aliases_del.deleter
def aliases_del(self): def aliases_del(self):
"Deleter. Allows for del self.aliases" "Deleter. Allows for del self.aliases"
self.db_aliases = "" self.db_aliases = ""
self.save() self.save()
aliases = property(aliases_get, aliases_set, aliases_del) aliases = property(aliases_get, aliases_set, aliases_del)
# keep_log property (wraps db_keep_log) # keep_log property (wraps db_keep_log)
#@property #@property
def keep_log_get(self): def keep_log_get(self):
@ -499,7 +499,7 @@ class Channel(SharedMemoryModel):
keep_log = property(keep_log_get, keep_log_set, keep_log_del) keep_log = property(keep_log_get, keep_log_set, keep_log_del)
# lock_storage property (wraps db_lock_storage) # lock_storage property (wraps db_lock_storage)
#@property #@property
def lock_storage_get(self): def lock_storage_get(self):
"Getter. Allows for value = self.lock_storage" "Getter. Allows for value = self.lock_storage"
return self.db_lock_storage return self.db_lock_storage
@ -527,11 +527,11 @@ class Channel(SharedMemoryModel):
def __str__(self): def __str__(self):
return "Channel '%s' (%s)" % (self.key, self.desc) return "Channel '%s' (%s)" % (self.key, self.desc)
def has_connection(self, player): def has_connection(self, player):
""" """
Checks so this player is actually listening Checks so this player is actually listening
to this channel. to this channel.
""" """
return PlayerChannelConnection.objects.has_connection(player, self) 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 Send the given message to all players connected to channel. Note that
no permission-checking is done here; it is assumed to have been 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 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 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): if isinstance(msgobj, basestring):
# given msgobj is a string # given msgobj is a string
if from_obj: if from_obj:
if isinstance(from_obj, basestring): if isinstance(from_obj, basestring):
msgobj = Msg(db_sender_external=from_obj, db_message=msgobj) msgobj = Msg(db_sender_external=from_obj, db_message=msgobj)
else: else:
msgobj = Msg(db_sender=from_obj, db_message=msgobj) msgobj = Msg(db_sender=from_obj, db_message=msgobj)
# try to use # try to use
msgobj.save() msgobj.save()
msgobj.channels = [self] msgobj.channels = [self]
msg = msgobj.message msg = msgobj.message
else: 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) # (and without storing it in a persistent Msg object)
msg = to_str(msgobj) msg = to_str(msgobj)
else: else:
msg = msgobj.message msg = msgobj.message
# get all players connected to this channel and send to them # 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: try:
conn.player.msg(msg, from_obj) conn.player.msg(msg, from_obj)
except AttributeError: except AttributeError:
try: try:
conn.to_external(msg, from_obj, from_channel=self) conn.to_external(msg, from_obj, from_channel=self)
except Exception: except Exception:
logger.log_trace("Cannot send msg to connection '%s'" % conn) logger.log_trace("Cannot send msg to connection '%s'" % conn)
return True return True
def tempmsg(self, message): def tempmsg(self, message):
""" """
A wrapper for sending non-persistent messages. Nothing 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: if type(msgobj) == Msg:
# extract only the string # extract only the string
message = message.message message = message.message
return self.msg(message) return self.msg(message)
def connect_to(self, player): def connect_to(self, player):
"Connect the user to this channel" "Connect the user to this channel"
if not self.access(player, 'listen'): if not self.access(player, 'listen'):
@ -594,7 +594,7 @@ class Channel(SharedMemoryModel):
conn = PlayerChannelConnection.objects.create_connection(player, self) conn = PlayerChannelConnection.objects.create_connection(player, self)
if conn: if conn:
return True return True
return False return False
def disconnect_from(self, player): def disconnect_from(self, player):
"Disconnect user from this channel." "Disconnect user from this channel."
@ -611,14 +611,14 @@ class Channel(SharedMemoryModel):
accessing_obj - object trying to access this one accessing_obj - object trying to access this one
access_type - type of access sought access_type - type of access sought
default - what to return if no lock of access_type was found default - what to return if no lock of access_type was found
""" """
return self.locks.check(accessing_obj, access_type=access_type, default=default) return self.locks.check(accessing_obj, access_type=access_type, default=default)
class PlayerChannelConnection(SharedMemoryModel): class PlayerChannelConnection(SharedMemoryModel):
""" """
This connects a player object to a particular comm channel. This connects a player object to a particular comm channel.
The advantage of making it like this is that one can easily 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 # Player connected to a channel
@ -672,19 +672,19 @@ class PlayerChannelConnection(SharedMemoryModel):
class ExternalChannelConnection(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 a channel, while storing some critical info about
that connection. that connection.
""" """
# evennia channel connecting to # 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.') help_text='which channel this connection is tied to.')
# external connection identifier # 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.') help_text='external connection identifier, used by calling protocol.')
# eval-code to use when the channel tries to send a message # eval-code to use when the channel tries to send a message
# to the external protocol. # to the external protocol.
db_external_send_code = models.TextField('executable code', blank=True, 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.') 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 # custom config for the connection
db_external_config = models.TextField('external config', blank=True, 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.') db_is_enabled = models.BooleanField('is enabled', default=True, help_text='turn on/off the connection.')
objects = managers.ExternalChannelConnectionManager() objects = managers.ExternalChannelConnectionManager()
class Meta: class Meta:
verbose_name = "External Channel Connection" verbose_name = "External Channel Connection"
def __str__(self): def __str__(self):
return "%s <-> external %s" % (self.channel.key, self.db_external_key) return "%s <-> external %s" % (self.channel.key, self.db_external_key)
@ -778,7 +778,7 @@ class ExternalChannelConnection(SharedMemoryModel):
self.save() self.save()
#@is_enabled.deleter #@is_enabled.deleter
def is_enabled_del(self): 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() self.delete()
is_enabled = property(is_enabled_get, is_enabled_set, is_enabled_del) 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): def to_channel(self, message, from_obj=None):
"Send external -> channel" "Send external -> channel"
if not from_obj: if not from_obj:
from_obj = self.external_key from_obj = self.external_key
self.channel.msg(message, from_obj=from_obj) self.channel.msg(message, from_obj=from_obj)
def to_external(self, message, from_obj=None, from_channel=None): def to_external(self, message, from_obj=None, from_channel=None):
"Send channel -> external" "Send channel -> external"
# make sure we are not echoing back our own message to ourselves # make sure we are not echoing back our own message to ourselves
# (this would result in a nasty infinite loop) # (this would result in a nasty infinite loop)
if from_obj == self.external_key: if from_obj == self.external_key:
return return
try: 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). # connection to contact the protocol correctly (as set by the protocol).
# Note that the code block has access to the variables here, such # 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)) exec(to_str(self.external_send_code))
except Exception: except Exception:
logger.log_trace("Channel %s could not send to External %s" % (self.channel, self.external_key)) logger.log_trace("Channel %s could not send to External %s" % (self.channel, self.external_key))

View file

@ -3,6 +3,7 @@ Custom manager for HelpEntry objects.
""" """
from django.db import models from django.db import models
from src.utils import logger, utils from src.utils import logger, utils
__all__ = ("HelpEntryManager",)
class HelpEntryManager(models.Manager): class HelpEntryManager(models.Manager):
""" """

View file

@ -15,6 +15,7 @@ from src.help.manager import HelpEntryManager
from src.utils import ansi from src.utils import ansi
from src.locks.lockhandler import LockHandler from src.locks.lockhandler import LockHandler
from src.utils.utils import is_iter from src.utils.utils import is_iter
__all__ = ("HelpEntry",)
#------------------------------------------------------------ #------------------------------------------------------------
# #
@ -32,6 +33,9 @@ class HelpEntry(SharedMemoryModel):
entrytext - the actual help text entrytext - the actual help text
permissions - perm strings permissions - perm strings
Method:
access
""" """
# #
@ -78,88 +82,88 @@ class HelpEntry(SharedMemoryModel):
# key property (wraps db_key) # key property (wraps db_key)
#@property #@property
def key_get(self): def __key_get(self):
"Getter. Allows for value = self.key" "Getter. Allows for value = self.key"
return self.db_key return self.db_key
#@key.setter #@key.setter
def key_set(self, value): def __key_set(self, value):
"Setter. Allows for self.key = value" "Setter. Allows for self.key = value"
self.db_key = value self.db_key = value
self.save() self.save()
#@key.deleter #@key.deleter
def key_del(self): def __key_del(self):
"Deleter. Allows for del self.key. Deletes entry." "Deleter. Allows for del self.key. Deletes entry."
self.delete() 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) # help_category property (wraps db_help_category)
#@property #@property
def help_category_get(self): def __help_category_get(self):
"Getter. Allows for value = self.help_category" "Getter. Allows for value = self.help_category"
return self.db_help_category return self.db_help_category
#@help_category.setter #@help_category.setter
def help_category_set(self, value): def __help_category_set(self, value):
"Setter. Allows for self.help_category = value" "Setter. Allows for self.help_category = value"
self.db_help_category = value self.db_help_category = value
self.save() self.save()
#@help_category.deleter #@help_category.deleter
def help_category_del(self): def __help_category_del(self):
"Deleter. Allows for del self.help_category" "Deleter. Allows for del self.help_category"
self.db_help_category = "General" self.db_help_category = "General"
self.save() 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) # entrytext property (wraps db_entrytext)
#@property #@property
def entrytext_get(self): def __entrytext_get(self):
"Getter. Allows for value = self.entrytext" "Getter. Allows for value = self.entrytext"
return self.db_entrytext return self.db_entrytext
#@entrytext.setter #@entrytext.setter
def entrytext_set(self, value): def __entrytext_set(self, value):
"Setter. Allows for self.entrytext = value" "Setter. Allows for self.entrytext = value"
self.db_entrytext = value self.db_entrytext = value
self.save() self.save()
#@entrytext.deleter #@entrytext.deleter
def entrytext_del(self): def __entrytext_del(self):
"Deleter. Allows for del self.entrytext" "Deleter. Allows for del self.entrytext"
self.db_entrytext = "" self.db_entrytext = ""
self.save() self.save()
entrytext = property(entrytext_get, entrytext_set, entrytext_del) entrytext = property(__entrytext_get, __entrytext_set, __entrytext_del)
# permissions property # permissions property
#@property #@property
def permissions_get(self): def __permissions_get(self):
"Getter. Allows for value = self.permissions. Returns a list of permissions." "Getter. Allows for value = self.permissions. Returns a list of permissions."
return [perm.strip() for perm in self.db_permissions.split(',')] return [perm.strip() for perm in self.db_permissions.split(',')]
#@permissions.setter #@permissions.setter
def permissions_set(self, value): def __permissions_set(self, value):
"Setter. Allows for self.permissions = value. Stores as a comma-separated string." "Setter. Allows for self.permissions = value. Stores as a comma-separated string."
if is_iter(value): if is_iter(value):
value = ",".join([str(val).strip().lower() for val in value]) value = ",".join([str(val).strip().lower() for val in value])
self.db_permissions = value self.db_permissions = value
self.save() self.save()
#@permissions.deleter #@permissions.deleter
def permissions_del(self): def __permissions_del(self):
"Deleter. Allows for del self.permissions" "Deleter. Allows for del self.permissions"
self.db_permissions = "" self.db_permissions = ""
self.save() 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) # lock_storage property (wraps db_lock_storage)
#@property #@property
def lock_storage_get(self): def __lock_storage_get(self):
"Getter. Allows for value = self.lock_storage" "Getter. Allows for value = self.lock_storage"
return self.db_lock_storage return self.db_lock_storage
#@nick.setter #@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()""" """Saves the lock_storagetodate. This is usually not called directly, but through self.lock()"""
self.db_lock_storage = value self.db_lock_storage = value
self.save() self.save()
#@nick.deleter #@nick.deleter
def lock_storage_del(self): def __lock_storage_del(self):
"Deleter is disabled. Use the lockhandler.delete (self.lock.delete) instead""" "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) 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)
# #

View file

@ -3,13 +3,13 @@ This module provides a set of permission lock functions for use
with Evennia's permissions system. with Evennia's permissions system.
To call these locks, make sure this module is included in the To call these locks, make sure this module is included in the
settings tuple PERMISSION_FUNC_MODULES then define a lock on the form settings tuple PERMISSION_FUNC_MODULES then define a lock on the form
'<access_type>:func(args)' and add it to the object's lockhandler. '<access_type>:func(args)' and add it to the object's lockhandler.
Run the access() method of the handler to execute the lock check. Run the access() method of the handler to execute the lock check.
Note that accessing_obj and accessed_obj can be any object type Note that accessing_obj and accessed_obj can be any object type
with a lock variable/field, so be careful to not expect with a lock variable/field, so be careful to not expect
a certain object type. a certain object type.
Appendix: MUX locks 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 basic Evennia, but could all be implemented easily if needed for the
individual game. individual game.
MUX Name: Affects: Effect: MUX Name: Affects: Effect:
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
DefaultLock: Exits: controls who may traverse the exit to DefaultLock: Exits: controls who may traverse the exit to
its destination. its destination.
@ -34,43 +34,43 @@ DefaultLock: Exits: controls who may traverse the exit to
Players/Things: controls who may GET the object. Players/Things: controls who may GET the object.
Evennia: "get:<lockfunc()" Evennia: "get:<lockfunc()"
EnterLock: Players/Things: controls who may ENTER the object EnterLock: Players/Things: controls who may ENTER the object
Evennia: Evennia:
GetFromLock: All but Exits: controls who may gets things from a given GetFromLock: All but Exits: controls who may gets things from a given
location. location.
Evennia: Evennia:
GiveLock: Players/Things: controls who may give the object. GiveLock: Players/Things: controls who may give the object.
Evennia: Evennia:
LeaveLock: Players/Things: controls who may LEAVE the object. LeaveLock: Players/Things: controls who may LEAVE the object.
Evennia: Evennia:
LinkLock: All but Exits: controls who may link to the location if the LinkLock: All but Exits: controls who may link to the location if the
location is LINK_OK (for linking exits or location is LINK_OK (for linking exits or
setting drop-tos) or ABODE (for setting setting drop-tos) or ABODE (for setting
homes) homes)
Evennia: Evennia:
MailLock: Players: controls who may @mail the player. MailLock: Players: controls who may @mail the player.
Evennia: Evennia:
OpenLock: All but Exits: controls who may open an exit. OpenLock: All but Exits: controls who may open an exit.
Evennia: Evennia:
PageLock: Players: controls who may page the player. PageLock: Players: controls who may page the player.
Evennia: "send:<lockfunc()>" Evennia: "send:<lockfunc()>"
ParentLock: All: controls who may make @parent links to the ParentLock: All: controls who may make @parent links to the
object. object.
Evennia: Typeclasses and "puppet:<lockstring()>" Evennia: Typeclasses and "puppet:<lockstring()>"
ReceiveLock: Players/Things: controls who may give things to the object. ReceiveLock: Players/Things: controls who may give things to the object.
Evennia: Evennia:
SpeechLock: All but Exits: controls who may speak in that location SpeechLock: All but Exits: controls who may speak in that location
Evennia: Evennia:
TeloutLock: All but Exits: controls who may teleport out of the TeloutLock: All but Exits: controls who may teleport out of the
location. location.
Evennia: Evennia:
TportLock: Rooms/Things: controls who may teleport there TportLock: Rooms/Things: controls who may teleport there
Evennia: Evennia:
UseLock: All but Exits: controls who may USE the object, GIVE the UseLock: All but Exits: controls who may USE the object, GIVE the
object money and have the PAY attributes object money and have the PAY attributes
run, have their messages heard and possibly run, have their messages heard and possibly
acted on by LISTEN and AxHEAR, and invoke acted on by LISTEN and AxHEAR, and invoke
$-commands stored on the object. $-commands stored on the object.
Evennia: Commands and Cmdsets. Evennia: Commands and Cmdsets.
DropLock: All but rooms: controls who may drop that object. DropLock: All but rooms: controls who may drop that object.
Evennia: Evennia:
VisibleLock: All: Controls object visibility when the object 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 search
from src.utils import utils 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): def _to_player(accessing_obj):
"Helper function. Makes sure an accessing object is a player object" "Helper function. Makes sure an accessing object is a player object"
@ -95,85 +95,85 @@ def _to_player(accessing_obj):
return accessing_obj return accessing_obj
# lock functions # lock functions
def true(*args, **kwargs): def true(*args, **kwargs):
"Always returns True." "Always returns True."
return True return True
def all(*args, **kwargs): def all(*args, **kwargs):
return True return True
def false(*args, **kwargs): def false(*args, **kwargs):
"Always returns False" "Always returns False"
return False return False
def none(*args, **kwargs): def none(*args, **kwargs):
return False return False
def perm(accessing_obj, accessed_obj, *args, **kwargs): def perm(accessing_obj, accessed_obj, *args, **kwargs):
""" """
The basic permission-checker. Ignores case. The basic permission-checker. Ignores case.
Usage: Usage:
perm(<permission>) perm(<permission>)
where <permission> is the permission accessing_obj must where <permission> is the permission accessing_obj must
have in order to pass the lock. If the given permission have in order to pass the lock. If the given permission
is part of PERMISSION_HIERARCHY, permission is also granted is part of _PERMISSION_HIERARCHY, permission is also granted
to all ranks higher up in the hierarchy. to all ranks higher up in the hierarchy.
""" """
try: try:
perm = args[0].lower() perm = args[0].lower()
permissions = [p.lower() for p in accessing_obj.permissions] permissions = [p.lower() for p in accessing_obj.permissions]
except AttributeError, IndexError: except AttributeError, IndexError:
return False return False
if perm in permissions: if perm in permissions:
# simplest case - we have a direct match # simplest case - we have a direct match
return True return True
if perm in PERMISSION_HIERARCHY: if perm in _PERMISSION_HIERARCHY:
# check if we have a higher hierarchy position # check if we have a higher hierarchy position
ppos = PERMISSION_HIERARCHY.index(perm) ppos = _PERMISSION_HIERARCHY.index(perm)
return any(1 for hpos, hperm in enumerate(PERMISSION_HIERARCHY) return any(1 for hpos, hperm in enumerate(_PERMISSION_HIERARCHY)
if hperm in permissions and hpos > ppos) if hperm in permissions and hpos > ppos)
return False return False
def perm_above(accessing_obj, accessed_obj, *args, **kwargs): def perm_above(accessing_obj, accessed_obj, *args, **kwargs):
""" """
Only allow objects with a permission *higher* in the permission 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, 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: try:
perm = args[0].lower() perm = args[0].lower()
except IndexError: except IndexError:
return False return False
if perm in PERMISSION_HIERARCHY: if perm in _PERMISSION_HIERARCHY:
ppos = PERMISSION_HIERARCHY.index(perm) ppos = _PERMISSION_HIERARCHY.index(perm)
return any(1 for hpos, hperm in enumerate(PERMISSION_HIERARCHY) return any(1 for hpos, hperm in enumerate(_PERMISSION_HIERARCHY)
if hperm in [p.lower() for p in accessing_obj.permissions] and hpos > ppos) 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): 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>) pperm(<permission>)
where <permission> is the permission accessing_obj must where <permission> is the permission accessing_obj must
have in order to pass the lock. If the given permission have in order to pass the lock. If the given permission
is part of PERMISSION_HIERARCHY, permission is also granted is part of _PERMISSION_HIERARCHY, permission is also granted
to all ranks higher up in the hierarchy. to all ranks higher up in the hierarchy.
""" """
return perm(_to_player(accessing_obj), accessed_obj, *args, **kwargs) return perm(_to_player(accessing_obj), accessed_obj, *args, **kwargs)
def pperm_above(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 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, 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) return perm_above(_to_player(accessing_obj), accessed_obj, *args, **kwargs)
@ -181,7 +181,7 @@ def dbref(accessing_obj, accessed_obj, *args, **kwargs):
""" """
Usage: Usage:
dbref(3) dbref(3)
This lock type checks if the checking object This lock type checks if the checking object
has a particular dbref. Note that this only has a particular dbref. Note that this only
works for checking objects that are stored works for checking objects that are stored
@ -195,7 +195,7 @@ def dbref(accessing_obj, accessed_obj, *args, **kwargs):
return False return False
if hasattr(accessing_obj, 'id'): if hasattr(accessing_obj, 'id'):
return dbref == accessing_obj.id return dbref == accessing_obj.id
return False return False
def pdbref(accessing_obj, accessed_obj, *args, **kwargs): 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) 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), CF_MAPPING = {'eq': lambda val1, val2: val1 == val2 or int(val1) == int(val2),
'gt': lambda val1, val2: int(val1) > int(val2), 'gt': lambda val1, val2: int(val1) > int(val2),
'lt': 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 how the value should be compared with one on accessing_obj (so
compare=gt means the accessing_obj must have a value greater than compare=gt means the accessing_obj must have a value greater than
the one given). the one given).
Searches attributes *and* properties stored on the checking Searches attributes *and* properties stored on the checking
object. The first form works like a flag - if the object. The first form works like a flag - if the
attribute/property exists on the object, the value is checked for 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 attribute/property matches. Note that all retrieved values will be
converted to strings before doing the comparison. converted to strings before doing the comparison.
""" """
# deal with arguments # deal with arguments
if not args: if not args:
return False return False
attrname = args[0].strip() attrname = args[0].strip()
value = None value = None
if len(args) > 1: if len(args) > 1:
value = args[1].strip() value = args[1].strip()
compare = 'eq' compare = 'eq'
if kwargs: if kwargs:
compare = kwargs.get('compare', 'eq') compare = kwargs.get('compare', 'eq')
def valcompare(val1, val2, typ='eq'): def valcompare(val1, val2, typ='eq'):
"compare based on type" "compare based on type"
try: try:
@ -258,20 +258,20 @@ def attr(accessing_obj, accessed_obj, *args, **kwargs):
except Exception, e: except Exception, e:
#print e #print e
# this might happen if we try to compare two things that cannot be compared # 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 hasattr(accessing_obj, attrname):
if value: 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 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 (hasattr(accessing_obj, 'has_attribute') and accessing_obj.has_attribute(attrname)):
if value: if value:
return (hasattr(accessing_obj, 'get_attribute') return (hasattr(accessing_obj, 'get_attribute')
and valcompare(accessing_obj.get_attribute(attrname), value, compare)) and valcompare(accessing_obj.get_attribute(attrname), value, compare))
return bool(accessing_obj.get_attribute(attrname)) # fails on False/None values return bool(accessing_obj.get_attribute(attrname)) # fails on False/None values
return False return False
def objattr(accessing_obj, accessed_obj, *args, **kwargs): 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)
objattr(attrname, value, compare=type) 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 accessing_obj.obj, if such an entity exists. Suitable
for commands. for commands.
@ -295,8 +295,8 @@ def locattr(accessing_obj, accessed_obj, *args, **kwargs):
locattr(attrname, value) locattr(attrname, value)
locattr(attrname, value, compare=type) locattr(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.location, if such an entity exists. accessing_obj.location, if such an entity exists.
""" """
if hasattr(accessing_obj, "location"): 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): def attr_eq(accessing_obj, accessed_obj, *args, **kwargs):
""" """
Usage: Usage:
attr_gt(attrname, 54) attr_gt(attrname, 54)
""" """
return attr(accessing_obj, accessed_obj, *args, **kwargs) return attr(accessing_obj, accessed_obj, *args, **kwargs)
def attr_gt(accessing_obj, accessed_obj, *args, **kwargs): def attr_gt(accessing_obj, accessed_obj, *args, **kwargs):
""" """
Usage: Usage:
attr_gt(attrname, 54) attr_gt(attrname, 54)
Only true if access_obj's attribute > the value given. 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'}) return attr(accessing_obj, accessed_obj, *args, **{'compare':'gt'})
def attr_ge(accessing_obj, accessed_obj, *args, **kwargs): def attr_ge(accessing_obj, accessed_obj, *args, **kwargs):
""" """
Usage: Usage:
attr_gt(attrname, 54) attr_gt(attrname, 54)
Only true if access_obj's attribute >= the value given. 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'}) return attr(accessing_obj, accessed_obj, *args, **{'compare':'ge'})
def attr_lt(accessing_obj, accessed_obj, *args, **kwargs): def attr_lt(accessing_obj, accessed_obj, *args, **kwargs):
""" """
Usage: Usage:
attr_gt(attrname, 54) attr_gt(attrname, 54)
Only true if access_obj's attribute < the value given. 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'}) return attr(accessing_obj, accessed_obj, *args, **{'compare':'lt'})
def attr_le(accessing_obj, accessed_obj, *args, **kwargs): def attr_le(accessing_obj, accessed_obj, *args, **kwargs):
""" """
Usage: Usage:
attr_gt(attrname, 54) attr_gt(attrname, 54)
Only true if access_obj's attribute <= the value given. 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'}) return attr(accessing_obj, accessed_obj, *args, **{'compare':'le'})
def attr_ne(accessing_obj, accessed_obj, *args, **kwargs): def attr_ne(accessing_obj, accessed_obj, *args, **kwargs):
""" """
Usage: Usage:
attr_gt(attrname, 54) attr_gt(attrname, 54)
Only true if access_obj's attribute != the value given. 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): 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() # 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 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: try:
# commands and scripts don't have contents, so we are usually looking # 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 # 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 contents = accessing_obj.contents
except AttributeError: except AttributeError:
try: try:
contents = accessing_obj.obj.contents contents = accessing_obj.obj.contents
except AttributeError: except AttributeError:
return False return False
def check_holds(objid): def check_holds(objid):
# helper function. Compares both dbrefs and keys/aliases. # helper function. Compares both dbrefs and keys/aliases.
objid = str(objid) objid = str(objid)
dbref = utils.dbref(objid) dbref = utils.dbref(objid)
if dbref and any((True for obj in contents if obj.id == dbref)): if dbref and any((True for obj in contents if obj.id == dbref)):
return True return True
objid = objid.lower() 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 obj.key.lower() == objid or objid in [al.lower() for al in obj.aliases]))
if args and args[0]: if args and args[0]:
return check_holds(args[0]) return check_holds(args[0])
else: else:
try: try:
if check_holds(accessed_obj.id): if check_holds(accessed_obj.id):
#print "holds: accessed_obj.id - True" #print "holds: accessed_obj.id - True"
return True return True
except Exception: except Exception:
pass pass
#print "holds: accessed_obj.obj.id -", hasattr(accessed_obj, "obj") and check_holds(accessed_obj.obj.id) #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) return hasattr(accessed_obj, "obj") and check_holds(accessed_obj.obj.id)
def superuser(*args, **kwargs): def superuser(*args, **kwargs):
""" """
Only accepts an accesing_obj that is superuser (e.g. user #1) Only accepts an accesing_obj that is superuser (e.g. user #1)
Since a superuser would not ever reach this check (superusers Since a superuser would not ever reach this check (superusers
bypass the lock entirely), any user who gets this far cannot be a bypass the lock entirely), any user who gets this far cannot be a
superuser, hence we just return False. :) superuser, hence we just return False. :)
""" """
return False return False
def serversetting(accessing_obj, accessed_obj, *args, **kwargs): def serversetting(accessing_obj, accessed_obj, *args, **kwargs):
""" """
Only returns true if the Evennia settings exists, alternatively has a certain value. Only returns true if the Evennia settings exists, alternatively has a certain value.
Usage: Usage:
serversetting(IRC_ENABLED) serversetting(IRC_ENABLED)
serversetting(BASE_SCRIPT_PATH, [game.gamesrc.scripts]) serversetting(BASE_SCRIPT_PATH, [game.gamesrc.scripts])
@ -418,16 +418,16 @@ def serversetting(accessing_obj, accessed_obj, *args, **kwargs):
return False return False
if len(args) < 2: if len(args) < 2:
setting = args[0] setting = args[0]
val = "True" val = "True"
else: else:
setting, val = args[0], args[1] setting, val = args[0], args[1]
# convert # convert
if val == 'True': if val == 'True':
val = True val = True
elif val == 'False': elif val == 'False':
val = False val = False
elif val.isdigit(): elif val.isdigit():
val = int(val) val = int(val)
if setting in settings._wrapped.__dict__: if setting in settings._wrapped.__dict__:
return settings._wrapped.__dict__[setting] == val return settings._wrapped.__dict__[setting] == val
return False return False

View file

@ -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 Evennia. For example, the "owner" property can be impmemented as a
lock. Or the disability to lift an object or to ban users. 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 - 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 - function call - this is one or many calls to functions that will determine
if the lock is passed or not. 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 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 # Lock function
@ -26,26 +26,26 @@ take four arguments looking like this:
The accessing object is the object wanting to gain access. The accessing object is the object wanting to gain access.
The accessed object is the object this lock resides on 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. to the function as a list and a dictionary respectively.
Example: Example:
perm(accessing_obj, accessed_obj, *args, **kwargs): perm(accessing_obj, accessed_obj, *args, **kwargs):
"Checking if the object has a particular, desired permission" "Checking if the object has a particular, desired permission"
if args: if args:
desired_perm = args[0] desired_perm = args[0]
return desired_perm in accessing_obj.permissions return desired_perm in accessing_obj.permissions
return False return False
Lock functions should most often be pretty general and ideally possible to 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") # Lock definition ("Lock string")
A lock definition is a string with a special syntax. It is added to 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. each object's lockhandler, making that lock available from then on.
The lock definition looks like this: 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 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. combined with AND or OR. NOT negates the result of the following call.
Example: Example:
We want to limit who may edit a particular object (let's call this access_type 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 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 only work for those with the Permission 'Builders'. So we use our lock
function above and define it like this: function above and define it like this:
'edit:perm(Builders)' 'edit:perm(Builders)'
Here, the lock-function perm() will be called with the string Here, the lock-function perm() will be called with the string
'Builders' (accessing_obj and accessed_obj are added automatically, 'Builders' (accessing_obj and accessed_obj are added automatically,
you only need to add the args/kwargs, if any). you only need to add the args/kwargs, if any).
@ -73,17 +73,17 @@ could use AND:
'edit:perm(Builders) AND perm(GoodGuy)' 'edit:perm(Builders) AND perm(GoodGuy)'
To allow EITHER Builders and GoodGuys, we replace AND with OR. perm() is just one example, 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. decide if the lock is passed or not.
'lift:attrib(very_strong) AND NOT attrib(bad_back)' '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 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)') 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: object would do something like this:
if not target_obj.lockhandler.has_perm(caller, 'edit'): 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'): if not target_obj.access(caller, 'edit'):
caller.msg("Sorry, you cannot edit that.") caller.msg("Sorry, you cannot edit that.")
# Permissions # Permissions
Permissions are just text strings stored in a comma-separated list on Permissions are just text strings stored in a comma-separated list on
typeclassed objects. The default perm() lock function uses them, typeclassed objects. The default perm() lock function uses them,
@ -106,8 +106,8 @@ to any other identifier you can use.
import re, inspect import re, inspect
from django.conf import settings from django.conf import settings
from src.utils import logger, utils from src.utils import logger, utils
__all__ = ("LockHandler", )
# #
# Exception class # Exception class
# #
@ -120,17 +120,17 @@ class LockException(Exception):
# Cached lock functions # Cached lock functions
# #
LOCKFUNCS = {} _LOCKFUNCS = {}
def cache_lockfuncs(): def _cache_lockfuncs():
"Updates the cache." "Updates the cache."
global LOCKFUNCS global _LOCKFUNCS
LOCKFUNCS = {} _LOCKFUNCS = {}
for modulepath in settings.LOCK_FUNC_MODULES: for modulepath in settings.LOCK_FUNC_MODULES:
modulepath = utils.pypath_to_realpath(modulepath) modulepath = utils.pypath_to_realpath(modulepath)
mod = utils.mod_import(modulepath) mod = utils.mod_import(modulepath)
if mod: if mod:
for tup in (tup for tup in inspect.getmembers(mod) if callable(tup[1])): 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: else:
logger.log_errmsg("Couldn't load %s from PERMISSION_FUNC_MODULES." % modulepath) logger.log_errmsg("Couldn't load %s from PERMISSION_FUNC_MODULES." % modulepath)
@ -138,21 +138,21 @@ def cache_lockfuncs():
# pre-compiled regular expressions # pre-compiled regular expressions
# #
RE_FUNCS = re.compile(r"\w+\([^)]*\)") _RE_FUNCS = re.compile(r"\w+\([^)]*\)")
RE_SEPS = re.compile(r"(?<=[ )])AND(?=\s)|(?<=[ )])OR(?=\s)|(?<=[ )])NOT(?=\s)") _RE_SEPS = re.compile(r"(?<=[ )])AND(?=\s)|(?<=[ )])OR(?=\s)|(?<=[ )])NOT(?=\s)")
RE_OK = re.compile(r"%s|and|or|not") _RE_OK = re.compile(r"%s|and|or|not")
# #
# #
# Lock handler # Lock handler
# #
# #
class LockHandler(object): class LockHandler(object):
""" """
This handler should be attached to all objects implementing This handler should be attached to all objects implementing
permission checks, under the property 'lockhandler'. permission checks, under the property 'lockhandler'.
""" """
def __init__(self, obj): def __init__(self, obj):
@ -160,13 +160,13 @@ class LockHandler(object):
Loads and pre-caches all relevant locks and their Loads and pre-caches all relevant locks and their
functions. functions.
""" """
if not LOCKFUNCS: if not _LOCKFUNCS:
cache_lockfuncs() _cache_lockfuncs()
self.obj = obj self.obj = obj
self.locks = {} self.locks = {}
self.log_obj = None self.log_obj = None
self.no_errors = True self.no_errors = True
self.reset_flag = False self.reset_flag = False
self._cache_locks(self.obj.lock_storage) self._cache_locks(self.obj.lock_storage)
@ -177,7 +177,7 @@ class LockHandler(object):
def _log_error(self, message): def _log_error(self, message):
"Try to log errors back to object" "Try to log errors back to object"
if self.log_obj and hasattr(self.log_obj, 'msg'): 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'): elif hasattr(self.obj, 'msg'):
self.obj.msg(message) self.obj.msg(message)
else: else:
@ -192,12 +192,12 @@ class LockHandler(object):
""" """
locks = {} locks = {}
if not storage_lockstring: if not storage_lockstring:
return locks return locks
nlocks = storage_lockstring.count(';') + 1 nlocks = storage_lockstring.count(';') + 1
duplicates = 0 duplicates = 0
elist = [] # errors elist = [] # errors
wlist = [] # warnings wlist = [] # warnings
for raw_lockstring in storage_lockstring.split(';'): for raw_lockstring in storage_lockstring.split(';'):
lock_funcs = [] lock_funcs = []
try: try:
access_type, rhs = (part.strip() for part in raw_lockstring.split(':', 1)) access_type, rhs = (part.strip() for part in raw_lockstring.split(':', 1))
@ -206,42 +206,42 @@ class LockHandler(object):
return locks return locks
# parse the lock functions and separators # 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') evalstring = rhs.replace('AND','and').replace('OR','or').replace('NOT','not')
nfuncs = len(funclist) nfuncs = len(funclist)
for funcstring in funclist: for funcstring in funclist:
funcname, rest = (part.strip().strip(')') for part in funcstring.split('(', 1)) 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): if not callable(func):
elist.append("Lock: function '%s' is not available." % funcstring) elist.append("Lock: function '%s' is not available." % funcstring)
continue continue
args = list(arg.strip() for arg in rest.split(',') if not '=' in arg) 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)) lock_funcs.append((func, args, kwargs))
evalstring = evalstring.replace(funcstring, '%s') evalstring = evalstring.replace(funcstring, '%s')
if len(lock_funcs) < nfuncs: if len(lock_funcs) < nfuncs:
continue continue
try: try:
# purge the eval string of any superfluos items, then test it # 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)) eval(evalstring % tuple(True for func in funclist))
except Exception: except Exception:
elist.append("Lock: definition '%s' has syntax errors." % raw_lockstring) elist.append("Lock: definition '%s' has syntax errors." % raw_lockstring)
continue continue
if access_type in locks: if access_type in locks:
duplicates += 1 duplicates += 1
wlist.append("Lock: access type '%s' changed from '%s' to '%s' " % \ wlist.append("Lock: access type '%s' changed from '%s' to '%s' " % \
(access_type, locks[access_type][2], raw_lockstring)) (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: if wlist and self.log_obj:
# a warning text was set, it's not an error, so only report if log_obj is available. # a warning text was set, it's not an error, so only report if log_obj is available.
self._log_error("\n".join(wlist)) self._log_error("\n".join(wlist))
if elist: if elist:
# an error text was set, raise exception. # an error text was set, raise exception.
raise LockException("\n".join(elist)) raise LockException("\n".join(elist))
self.no_errors = False self.no_errors = False
# return the gathered locks in an easily executable form # return the gathered locks in an easily executable form
return locks return locks
def _cache_locks(self, storage_lockstring): def _cache_locks(self, storage_lockstring):
"""Store data""" """Store data"""
@ -253,29 +253,29 @@ class LockHandler(object):
def add(self, lockstring, log_obj=None): def add(self, lockstring, log_obj=None):
""" """
Add a new lockstring on the form '<access_type>:<functions>'. Multiple Add a new lockstring on the form '<access_type>:<functions>'. Multiple
access types should be separated by semicolon (;). access types should be separated by semicolon (;).
If log_obj is given, it will be fed error information. If log_obj is given, it will be fed error information.
""" """
if log_obj: if log_obj:
self.log_obj = log_obj self.log_obj = log_obj
self.no_errors = True self.no_errors = True
# sanity checks # sanity checks
for lockdef in lockstring.split(';'): for lockdef in lockstring.split(';'):
if not ':' in lockstring: if not ':' in lockstring:
self._log_error("Lock: '%s' contains no colon (:)." % lockdef) self._log_error("Lock: '%s' contains no colon (:)." % lockdef)
return False return False
access_type, rhs = [part.strip() for part in lockdef.split(':', 1)] access_type, rhs = [part.strip() for part in lockdef.split(':', 1)]
if not access_type: if not access_type:
self._log_error("Lock: '%s' has no access_type (left-side of colon is empty)." % lockdef) 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(')'): if rhs.count('(') != rhs.count(')'):
self._log_error("Lock: '%s' has mismatched parentheses." % lockdef) self._log_error("Lock: '%s' has mismatched parentheses." % lockdef)
return False return False
if not RE_FUNCS.findall(rhs): if not _RE_FUNCS.findall(rhs):
self._log_error("Lock: '%s' has no valid lock functions." % lockdef) self._log_error("Lock: '%s' has no valid lock functions." % lockdef)
return False return False
# get the lock string # get the lock string
storage_lockstring = self.obj.lock_storage storage_lockstring = self.obj.lock_storage
if storage_lockstring: if storage_lockstring:
@ -285,8 +285,8 @@ class LockHandler(object):
# cache the locks will get rid of eventual doublets # cache the locks will get rid of eventual doublets
self._cache_locks(storage_lockstring) self._cache_locks(storage_lockstring)
self._save_locks() self._save_locks()
self.log_obj = None self.log_obj = None
return self.no_errors return self.no_errors
def get(self, access_type): def get(self, access_type):
"get the lockstring of a particular type" "get the lockstring of a particular type"
@ -297,8 +297,8 @@ class LockHandler(object):
if access_type in self.locks: if access_type in self.locks:
del self.locks[access_type] del self.locks[access_type]
self._save_locks() self._save_locks()
return True return True
return False return False
def clear(self): def clear(self):
"Remove all locks" "Remove all locks"
@ -307,10 +307,10 @@ class LockHandler(object):
def reset(self): def reset(self):
""" """
Set the reset flag, so the the lock will be re-cached at next checking. 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 self.reset_flag = True
def check(self, accessing_obj, access_type, default=False, no_superuser_bypass=False): def check(self, accessing_obj, access_type, default=False, no_superuser_bypass=False):
""" """
Checks a lock of the correct type by passing execution 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, no_superuser_bypass - don't use this unless you really, really need to,
it makes supersusers susceptible to the lock check. 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 Parsing the lockstring, we (during cache) extract the valid
lock functions and store their function objects in the right lock functions and store their function objects in the right
order along with their args/kwargs. These are now executed in order along with their args/kwargs. These are now executed in
@ -340,11 +340,11 @@ class LockHandler(object):
""" """
if self.reset_flag: if self.reset_flag:
# rebuild cache # rebuild cache
self._cache_locks(self.obj.lock_storage) 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) 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, '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)))): 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 return True
if access_type in self.locks: if access_type in self.locks:
# we have a lock, test it. # we have a lock, test it.
evalstring, func_tup, raw_string = self.locks[access_type] evalstring, func_tup, raw_string = self.locks[access_type]
# execute all lock funcs in the correct order, producing a tuple of True/False results. # 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) 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. # with AND/OR/NOT in order to get the final result.
return eval(evalstring % true_false) return eval(evalstring % true_false)
else: else:
@ -367,22 +367,22 @@ class LockHandler(object):
""" """
Do a direct check against a lockstring ('atype:func()..'), without any Do a direct check against a lockstring ('atype:func()..'), without any
intermediary storage on the accessed object (this can be left 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. put to a dummy value since no lock selection is made.
""" """
if ((hasattr(accessing_obj, 'is_superuser') and accessing_obj.is_superuser) 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, '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))): 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) locks = self. _parse_lockstring(lockstring)
for access_type in locks: for access_type in locks:
evalstring, func_tup, raw_string = locks[access_type] 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) true_false = tuple(tup[0](accessing_obj, self.obj, *tup[1], **tup[2]) for tup in func_tup)
return eval(evalstring % true_false) return eval(evalstring % true_false)
def test(): def _test():
# testing # testing
class TestObj(object): class TestObj(object):
pass pass
@ -390,9 +390,9 @@ def test():
import pdb import pdb
obj1 = TestObj() obj1 = TestObj()
obj2 = 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 = "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)" obj1.lock_storage = "listen:perm(Immortals)"
pdb.set_trace() pdb.set_trace()

View file

@ -2,7 +2,7 @@
""" """
This is part of Evennia's unittest framework, for testing 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. This module tests the lock functionality of Evennia.
@ -49,7 +49,7 @@ class TestLockfuncs(LockTest):
def testrun(self): def testrun(self):
self.obj2.permissions = ['Wizards'] self.obj2.permissions = ['Wizards']
self.assertEquals(True, lockfuncs.true(self.obj2, self.obj1)) 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(self.obj2, self.obj1, 'Wizards'))
self.assertEquals(True, lockfuncs.perm_above(self.obj2, self.obj1, 'Builders')) self.assertEquals(True, lockfuncs.perm_above(self.obj2, self.obj1, 'Builders'))
dbref = self.obj2.dbref dbref = self.obj2.dbref

View file

@ -10,9 +10,11 @@ from src.utils import utils
from src.utils.utils import to_unicode from src.utils.utils import to_unicode
from src.utils import logger from src.utils import logger
__all__ = ("ObjectManager",)
# Try to use a custom way to parse id-tagged multimatches. # 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): class ObjectManager(TypedObjectManager):
""" """
@ -288,7 +290,7 @@ class ObjectManager(TypedObjectManager):
matches = local_and_global_search(ostring, exact=True) matches = local_and_global_search(ostring, exact=True)
if not matches: if not matches:
# if we have no match, check if we are dealing with an "N-keyword" query - if so, strip it. # 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: if match_number != None and ostring:
# Run search again, without match number: # Run search again, without match number:
matches = local_and_global_search(ostring, exact=True) matches = local_and_global_search(ostring, exact=True)

View file

@ -32,7 +32,8 @@ from src.scripts.scripthandler import ScriptHandler
from src.utils import logger from src.utils import logger
from src.utils.utils import make_iter, to_unicode, to_str, mod_import 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)) _AT_SEARCH_RESULT = mod_import(*settings.SEARCH_AT_RESULT.rsplit('.', 1))
@ -211,7 +212,7 @@ class ObjectDB(TypedObject):
# aliases property (wraps (db_aliases) # aliases property (wraps (db_aliases)
#@property #@property
def aliases_get(self): def __aliases_get(self):
"Getter. Allows for value = self.aliases" "Getter. Allows for value = self.aliases"
try: try:
return _GA(self, "_cached_aliases") return _GA(self, "_cached_aliases")
@ -220,23 +221,23 @@ class ObjectDB(TypedObject):
_SA(self, "_cached_aliases", aliases) _SA(self, "_cached_aliases", aliases)
return aliases return aliases
#@aliases.setter #@aliases.setter
def aliases_set(self, aliases): def __aliases_set(self, aliases):
"Setter. Allows for self.aliases = value" "Setter. Allows for self.aliases = value"
for alias in make_iter(aliases): for alias in make_iter(aliases):
new_alias = Alias(db_key=alias, db_obj=self) new_alias = Alias(db_key=alias, db_obj=self)
new_alias.save() new_alias.save()
_SA(self, "_cached_aliases", aliases) _SA(self, "_cached_aliases", aliases)
#@aliases.deleter #@aliases.deleter
def aliases_del(self): def __aliases_del(self):
"Deleter. Allows for del self.aliases" "Deleter. Allows for del self.aliases"
for alias in Alias.objects.filter(db_obj=self): for alias in Alias.objects.filter(db_obj=self):
alias.delete() alias.delete()
_DA(self, "_cached_aliases") _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) # player property (wraps db_player)
#@property #@property
def player_get(self): def __player_get(self):
""" """
Getter. Allows for value = self.player. Getter. Allows for value = self.player.
We have to be careful here since Player is also We have to be careful here since Player is also
@ -244,29 +245,29 @@ class ObjectDB(TypedObject):
""" """
return _get_cache(self, "player") return _get_cache(self, "player")
#@player.setter #@player.setter
def player_set(self, player): def __player_set(self, player):
"Setter. Allows for self.player = value" "Setter. Allows for self.player = value"
if isinstance(player, TypeClass): if isinstance(player, TypeClass):
player = player.dbobj player = player.dbobj
_set_cache(self, "player", player) _set_cache(self, "player", player)
#@player.deleter #@player.deleter
def player_del(self): def __player_del(self):
"Deleter. Allows for del self.player" "Deleter. Allows for del self.player"
self.db_player = None self.db_player = None
self.save() self.save()
_del_cache(self, "player") _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) # location property (wraps db_location)
#@property #@property
def location_get(self): def __location_get(self):
"Getter. Allows for value = self.location." "Getter. Allows for value = self.location."
loc = _get_cache(self, "location") loc = _get_cache(self, "location")
if loc: if loc:
return loc.typeclass return loc.typeclass
return None return None
#@location.setter #@location.setter
def location_set(self, location): def __location_set(self, location):
"Setter. Allows for self.location = location" "Setter. Allows for self.location = location"
try: try:
if location == None or type(location) == ObjectDB: if location == None or type(location) == ObjectDB:
@ -289,23 +290,23 @@ class ObjectDB(TypedObject):
logger.log_trace(string) logger.log_trace(string)
raise raise
#@location.deleter #@location.deleter
def location_del(self): def __location_del(self):
"Deleter. Allows for del self.location" "Deleter. Allows for del self.location"
self.db_location = None self.db_location = None
self.save() self.save()
_del_cache(self, "location") _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) # home property (wraps db_home)
#@property #@property
def home_get(self): def __home_get(self):
"Getter. Allows for value = self.home" "Getter. Allows for value = self.home"
home = _get_cache(self, "home") home = _get_cache(self, "home")
if home: if home:
return home.typeclass return home.typeclass
return None return None
#@home.setter #@home.setter
def home_set(self, home): def __home_set(self, home):
"Setter. Allows for self.home = value" "Setter. Allows for self.home = value"
try: try:
if home == None or type(home) == ObjectDB: if home == None or type(home) == ObjectDB:
@ -326,23 +327,23 @@ class ObjectDB(TypedObject):
logger.log_trace(string) logger.log_trace(string)
#raise #raise
#@home.deleter #@home.deleter
def home_del(self): def __home_del(self):
"Deleter. Allows for del self.home." "Deleter. Allows for del self.home."
self.db_home = None self.db_home = None
self.save() self.save()
_del_cache(self, "home") _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) # destination property (wraps db_destination)
#@property #@property
def destination_get(self): def __destination_get(self):
"Getter. Allows for value = self.destination." "Getter. Allows for value = self.destination."
dest = _get_cache(self, "destination") dest = _get_cache(self, "destination")
if dest: if dest:
return dest.typeclass return dest.typeclass
return None return None
#@destination.setter #@destination.setter
def destination_set(self, destination): def __destination_set(self, destination):
"Setter. Allows for self.destination = destination" "Setter. Allows for self.destination = destination"
try: try:
if destination == None or type(destination) == ObjectDB: if destination == None or type(destination) == ObjectDB:
@ -365,33 +366,33 @@ class ObjectDB(TypedObject):
logger.log_trace(string) logger.log_trace(string)
raise raise
#@destination.deleter #@destination.deleter
def destination_del(self): def __destination_del(self):
"Deleter. Allows for del self.destination" "Deleter. Allows for del self.destination"
self.db_destination = None self.db_destination = None
self.save() self.save()
_del_cache(self, "destination") _del_cache(self, "destination")
destination = property(destination_get, destination_set, destination_del) destination = property(__destination_get, __destination_set, __destination_del)
# cmdset_storage property. # cmdset_storage property.
# This seems very sensitive to caching, so leaving it be for now. /Griatch # This seems very sensitive to caching, so leaving it be for now. /Griatch
#@property #@property
def cmdset_storage_get(self): def __cmdset_storage_get(self):
"Getter. Allows for value = self.name. Returns a list of cmdset_storage." "Getter. Allows for value = self.name. Returns a list of cmdset_storage."
if self.db_cmdset_storage: if self.db_cmdset_storage:
return [path.strip() for path in self.db_cmdset_storage.split(',')] return [path.strip() for path in self.db_cmdset_storage.split(',')]
return [] return []
#@cmdset_storage.setter #@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." "Setter. Allows for self.name = value. Stores as a comma-separated string."
value = ",".join(str(val).strip() for val in make_iter(value)) value = ",".join(str(val).strip() for val in make_iter(value))
self.db_cmdset_storage = value self.db_cmdset_storage = value
self.save() self.save()
#@cmdset_storage.deleter #@cmdset_storage.deleter
def cmdset_storage_del(self): def __cmdset_storage_del(self):
"Deleter. Allows for del self.name" "Deleter. Allows for del self.name"
self.db_cmdset_storage = "" self.db_cmdset_storage = ""
self.save() 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: class Meta:
"Define Django meta options" "Define Django meta options"
@ -409,7 +410,7 @@ class ObjectDB(TypedObject):
_default_typeclass_path = settings.BASE_OBJECT_TYPECLASS or "src.objects.objects.Object" _default_typeclass_path = settings.BASE_OBJECT_TYPECLASS or "src.objects.objects.Object"
#@property #@property
def sessions_get(self): def __sessions_get(self):
""" """
Retrieve sessions connected to this object. Retrieve sessions connected to this object.
""" """
@ -417,42 +418,43 @@ class ObjectDB(TypedObject):
if self.player: if self.player:
return self.player.sessions return self.player.sessions
return [] return []
sessions = property(sessions_get) sessions = property(__sessions_get)
#@property #@property
def has_player_get(self): def __has_player_get(self):
""" """
Convenience function for checking if an active player is Convenience function for checking if an active player is
currently connected to this object currently connected to this object
""" """
return any(self.sessions) return any(self.sessions)
has_player = property(has_player_get) has_player = property(__has_player_get)
is_player = property(has_player_get) is_player = property(__has_player_get)
#@property #@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." "Check if user has a player, and if so, if it is a superuser."
return any(self.sessions) and self.player.is_superuser return any(self.sessions) and self.player.is_superuser
is_superuser = property(is_superuser_get) is_superuser = property(__is_superuser_get)
#@property #@property
def contents_get(self, exclude=None): def contents_get(self, exclude=None):
""" """
Returns the contents of this object, i.e. all Returns the contents of this object, i.e. all
objects that has this object set as its location. objects that has this object set as its location.
This should be publically available.
""" """
return ObjectDB.objects.get_contents(self, excludeobj=exclude) return ObjectDB.objects.get_contents(self, excludeobj=exclude)
contents = property(contents_get) contents = property(contents_get)
#@property #@property
def exits_get(self): def __exits_get(self):
""" """
Returns all exits from this object, i.e. all objects Returns all exits from this object, i.e. all objects
at this location having the property destination != None. at this location having the property destination != None.
""" """
return [exi for exi in self.contents return [exi for exi in self.contents
if exi.destination] if exi.destination]
exits = property(exits_get) exits = property(__exits_get)
# #

View file

@ -17,6 +17,7 @@ they control by simply linking to a new object's user property.
from src.typeclasses.typeclass import TypeClass from src.typeclasses.typeclass import TypeClass
from src.commands import cmdset, command from src.commands import cmdset, command
__all__ = ("Object", "Character", "Room", "Exit")
# #
# Base class to inherit from. # Base class to inherit from.
@ -24,11 +25,10 @@ from src.commands import cmdset, command
class Object(TypeClass): class Object(TypeClass):
""" """
This is the base class for all in-game objects. This is the base class for all in-game objects. Inherit from this
Inherit from this to create different types of to create different types of objects in the game.
objects in the game.
""" """
# __init__ is only defined here in order to present docstring to API.
def __init__(self, dbobj): def __init__(self, dbobj):
""" """
This is the root typeclass object, implementing an in-game Evennia This is the root typeclass object, implementing an in-game Evennia

View file

@ -2,12 +2,13 @@
The managers for the custom Player object and permissions. The managers for the custom Player object and permissions.
""" """
import datetime import datetime
from functools import update_wrapper from functools import update_wrapper
from django.db import models from django.db import models
from django.contrib.auth.models import User from django.contrib.auth.models import User
from src.typeclasses.managers import returns_typeclass_list, returns_typeclass, TypedObjectManager from src.typeclasses.managers import returns_typeclass_list, returns_typeclass, TypedObjectManager
from src.utils import logger from src.utils import logger
__all__ = ("PlayerManager",)
# #
# Player Manager # Player Manager
@ -35,16 +36,16 @@ def returns_player_list(method):
players.append(user.get_profile()) players.append(user.get_profile())
except Exception: except Exception:
# there is something wrong with get_profile. But # 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. # try to go the other way instead.
from src.players.models import PlayerDB from src.players.models import PlayerDB
match = PlayerDB.objects.filter(user=user) match = PlayerDB.objects.filter(user=user)
if match: if match:
players.append(match[0]) players.append(match[0])
else: 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 players
return update_wrapper(func, method) return update_wrapper(func, method)
def returns_player(method): def returns_player(method):
""" """
@ -57,18 +58,18 @@ def returns_player(method):
if match: if match:
return match[0] return match[0]
else: else:
return None return None
return update_wrapper(func, method) return update_wrapper(func, method)
class PlayerManager(TypedObjectManager): class PlayerManager(TypedObjectManager):
""" """
This PlayerManager implements methods for searching This PlayerManager implements methods for searching
and manipulating Players directly from the database. and manipulating Players directly from the database.
Evennia-specific search methods (will return Characters if Evennia-specific search methods (will return Characters if
possible or a Typeclass/list of Typeclassed objects, whereas possible or a Typeclass/list of Typeclassed objects, whereas
Django-general methods will return Querysets or database objects): Django-general methods will return Querysets or database objects):
dbref (converter) dbref (converter)
dbref_search dbref_search
get_dbref_range get_dbref_range
@ -83,14 +84,14 @@ class PlayerManager(TypedObjectManager):
get_player_from_name get_player_from_name
player_search (equivalent to ev.search_player) player_search (equivalent to ev.search_player)
swap_character swap_character
""" """
def num_total_players(self): def num_total_players(self):
""" """
Returns the total number of registered users/players. Returns the total number of registered users/players.
""" """
return self.count() return self.count()
@returns_typeclass_list @returns_typeclass_list
def get_connected_players(self): def get_connected_players(self):
""" """
@ -99,7 +100,7 @@ class PlayerManager(TypedObjectManager):
return [player for player in self.all() if player.sessions] return [player for player in self.all() if player.sessions]
@returns_typeclass_list @returns_typeclass_list
@returns_player_list @returns_player_list
def get_recently_created_players(self, days=7): def get_recently_created_players(self, days=7):
""" """
Returns a QuerySet containing the player User accounts that have been Returns a QuerySet containing the player User accounts that have been
@ -145,8 +146,8 @@ class PlayerManager(TypedObjectManager):
players = self.filter(user__username=uname) players = self.filter(user__username=uname)
if players: if players:
return players[0] return players[0]
return None return None
# @returns_typeclass_list # @returns_typeclass_list
# def get_players_with_perm(self, permstring): # def get_players_with_perm(self, permstring):
# """ # """
@ -159,7 +160,7 @@ class PlayerManager(TypedObjectManager):
# @returns_typeclass_list # @returns_typeclass_list
# def get_players_with_group(self, groupstring): # 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() # return [player.user for player in self.all()
# if player.has_group(groupstring)] # if player.has_group(groupstring)]
@ -167,9 +168,9 @@ class PlayerManager(TypedObjectManager):
@returns_typeclass_list @returns_typeclass_list
def player_search(self, ostring): def player_search(self, ostring):
""" """
Searches for a particular player by name or Searches for a particular player by name or
database id. database id.
ostring = a string or database id. ostring = a string or database id.
""" """
ostring = ostring.lstrip("*") ostring = ostring.lstrip("*")
@ -178,7 +179,7 @@ class PlayerManager(TypedObjectManager):
matches = self.filter(id=dbref) matches = self.filter(id=dbref)
if matches: if matches:
return 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): def swap_character(self, player, new_character, delete_old_character=False):
""" """
@ -192,7 +193,7 @@ class PlayerManager(TypedObjectManager):
return False return False
# do the swap # do the swap
old_character = player.character old_character = player.character
if old_character: if old_character:
old_character.player = None old_character.player = None
try: try:
@ -202,8 +203,7 @@ class PlayerManager(TypedObjectManager):
# recover old setup # recover old setup
old_character.player = player old_character.player = player
player.character = old_character player.character = old_character
return False return False
if delete_old_character: if delete_old_character:
old_character.delete() old_character.delete()
return True return True

View file

@ -53,6 +53,8 @@ from src.utils import logger, utils
from src.commands.cmdsethandler import CmdSetHandler from src.commands.cmdsethandler import CmdSetHandler
from src.commands import cmdhandler from src.commands import cmdhandler
__all__ = ("PlayerAttribute", "PlayerNick", "PlayerDB")
_AT_SEARCH_RESULT = utils.mod_import(*settings.SEARCH_AT_RESULT.rsplit('.', 1)) _AT_SEARCH_RESULT = utils.mod_import(*settings.SEARCH_AT_RESULT.rsplit('.', 1))
#------------------------------------------------------------ #------------------------------------------------------------

View file

@ -1,10 +1,10 @@
""" """
Typeclass for Player objects Typeclass for Player objects
Note that this object is primarily intended to Note that this object is primarily intended to
store OOC information, not game info! This store OOC information, not game info! This
object represents the actual user (not their 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 game world (this is handled by the associated
character object, so you should customize that character object, so you should customize that
instead for most things). instead for most things).
@ -12,12 +12,12 @@ instead for most things).
""" """
from django.conf import settings from django.conf import settings
from src.typeclasses.typeclass import TypeClass from src.typeclasses.typeclass import TypeClass
__all__ = ("Player",)
CMDSET_OOC = settings.CMDSET_OOC CMDSET_OOC = settings.CMDSET_OOC
class Player(TypeClass): class Player(TypeClass):
""" """
Base typeclass for all Players. Base typeclass for all Players.
""" """
def __init__(self, dbobj): def __init__(self, dbobj):
""" """
@ -27,7 +27,7 @@ class Player(TypeClass):
can connect to a Character Object in order to "enter" the can connect to a Character Object in order to "enter" the
game. game.
Player Typeclass API: Player Typeclass API:
* Available properties (only available on initiated typeclass objects) * 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 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. 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 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 user (User, read-only) - django User authorization object
obj (Object) - game object controlled by player. 'character' can also be used. obj (Object) - game object controlled by player. 'character' can also be used.
sessions (list of Sessions) - sessions connected to this player sessions (list of Sessions) - sessions connected to this player
is_superuser (bool, read-only) - if the connected user is a superuser 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 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 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() scripts - script-handler. Add new scripts to object with scripts.add()
cmdset - cmdset-handler. Use cmdset.add() to add new cmdsets to object cmdset - cmdset-handler. Use cmdset.add() to add new cmdsets to object
nicks - nick-handler. New nicks with nicks.add(). nicks - nick-handler. New nicks with nicks.add().
* Helper methods * Helper methods
msg(outgoing_string, from_obj=None, data=None) msg(outgoing_string, from_obj=None, data=None)
@ -68,12 +68,12 @@ class Player(TypeClass):
* Hook methods * Hook methods
basetype_setup() basetype_setup()
at_player_creation() at_player_creation()
- note that the following hooks are also found on Objects and are - note that the following hooks are also found on Objects and are
usually handled on the character level: usually handled on the character level:
at_init() at_init()
at_cmdset_get() at_cmdset_get()
at_first_login() at_first_login()
at_post_login() at_post_login()
@ -86,11 +86,11 @@ class Player(TypeClass):
""" """
super(Player, self).__init__(dbobj) 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): 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. This is the main route for sending data back to the user from the server.
outgoing_string (string) - text data to send outgoing_string (string) - text data to send
@ -106,7 +106,7 @@ class Player(TypeClass):
new_character (Object) - character/object to swap to new_character (Object) - character/object to swap to
delete_old_character (bool) - delete the old character when swapping delete_old_character (bool) - delete the old character when swapping
Returns: True/False depending on if swap suceeded or not. Returns: True/False depending on if swap suceeded or not.
""" """
return self.dbobj.swap_character(new_character, delete_old_character=delete_old_character) 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 lets its typeclass execute the command. Evennia also calls
this method whenever the player sends a command on the command line. this method whenever the player sends a command on the command line.
Argument: Argument:
raw_string (string) - raw command input raw_string (string) - raw command input
Returns Deferred - this is an asynchronous Twisted object that will Returns Deferred - this is an asynchronous Twisted object that will
not fire until the command has actually finished executing. To overload 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 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 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) 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): location=None, ignore_errors=False, player=False):
""" """
This method mimicks object.search if self.character is set. Otherwise only 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) location=location, ignore_errors=ignore_errors, player=player)
def is_typeclass(self, typeclass, exact=False): def is_typeclass(self, typeclass, exact=False):
""" """
Returns true if this object has this type Returns true if this object has this type
OR has a typeclass which is an subclass of OR has a typeclass which is an subclass of
the given typeclass. the given typeclass.
typeclass - can be a class object or the 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 exact - returns true only if the object's
type is exactly this typeclass, ignoring type is exactly this typeclass, ignoring
parents. parents.
Returns: Boolean Returns: Boolean
""" """
return self.dbobj.is_typeclass(typeclass, exact=exact) return self.dbobj.is_typeclass(typeclass, exact=exact)
def swap_typeclass(self, new_typeclass, clean_attributes=False, no_default=True): 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 This performs an in-situ swap of the typeclass. This means
that in-game, this object will suddenly be something else. that in-game, this object will suddenly be something else.
Player will not be affected. To 'move' a player to a different 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(). 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 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 bug test your code if using this feature! Often its easiest
to create a new object and just swap the player over to to create a new object and just swap the player over to
that one instead. that one instead.
Arguments: Arguments:
new_typeclass (path/classobj) - type to switch to new_typeclass (path/classobj) - type to switch to
clean_attributes (bool/list) - will delete all attributes clean_attributes (bool/list) - will delete all attributes
stored on this object (but not any stored on this object (but not any
of the database fields such as name or 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 no_default - if this is active, the swapper will not allow for
swapping to a default typeclass in case the given swapping to a default typeclass in case the given
one fails for some reason. Instead the old one one fails for some reason. Instead the old one
will be preserved. will be preserved.
Returns: Returns:
boolean True/False depending on if the swap worked or not. 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) 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 accessing_obj (Object)- object trying to access this one
access_type (string) - type of access sought access_type (string) - type of access sought
default (bool) - what to return if no lock of access_type was found 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) return self.dbobj.access(accessing_obj, access_type=access_type, default=default)
def check_permstring(self, permstring): def check_permstring(self, permstring):
""" """
This explicitly checks the given string against this object's This explicitly checks the given string against this object's
'permissions' property without involving any locks. 'permissions' property without involving any locks.
permstring (string) - permission string that need to match a permission on the object. permstring (string) - permission string that need to match a permission on the object.
(example: 'Builders') (example: 'Builders')
""" """
return self.dbobj.check_permstring(permstring) return self.dbobj.check_permstring(permstring)
## player hooks ## player hooks
def basetype_setup(self): 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 Overload this with at_player_creation rather than
changing this method. changing this method.
""" """
# the text encoding to use. # the text encoding to use.
self.db.encoding = "utf-8" self.db.encoding = "utf-8"
# A basic security setup # A basic security setup
self.locks.add("examine:perm(Wizards)") self.locks.add("examine:perm(Wizards)")
self.locks.add("edit:perm(Wizards)") self.locks.add("edit:perm(Wizards)")
self.locks.add("delete: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()") self.locks.add("msg:all()")
# The ooc player cmdset # The ooc player cmdset
self.cmdset.add_default(CMDSET_OOC, permanent=True) self.cmdset.add_default(CMDSET_OOC, permanent=True)
self.cmdset.outside_access = False self.cmdset.outside_access = False
def at_player_creation(self): def at_player_creation(self):
""" """
This is called once, the very first time This is called once, the very first time
the player is created (i.e. first time they the player is created (i.e. first time they
register with the game). It's a good place register with the game). It's a good place
to store attributes all players should have, to store attributes all players should have,
like configuration values etc. like configuration values etc.
""" """
pass pass
def at_init(self): def at_init(self):
""" """
@ -257,7 +257,7 @@ class Player(TypeClass):
happens the moment the player logs in or reconnects after a happens the moment the player logs in or reconnects after a
reload. reload.
""" """
pass pass
# Note that the hooks below also exist in the character object's # Note that the hooks below also exist in the character object's
# typeclass. You can often ignore these and rely on the character # 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 Called at the end of the login
process, just before letting process, just before letting
them loose. them loose.
""" """
pass pass
@ -307,29 +307,29 @@ class Player(TypeClass):
""" """
Called when any text is emitted to this Called when any text is emitted to this
object. If it returns False, no text 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): def at_message_send(self, message, to_object):
""" """
Called whenever this object tries to send text Called whenever this object tries to send text
to another object. Only called if the object supplied 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): 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, 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 pass
def at_server_shutdown(self): def at_server_shutdown(self):
""" """
This hook is called whenever the server is shutting down fully (i.e. not for This hook is called whenever the server is shutting down fully (i.e. not for
a restart). a restart).
""" """
pass pass

View file

@ -4,18 +4,19 @@ The custom manager for Scripts.
from src.typeclasses.managers import TypedObjectManager from src.typeclasses.managers import TypedObjectManager
from src.typeclasses.managers import returns_typeclass_list from src.typeclasses.managers import returns_typeclass_list
__all__ = ("ScriptManager",)
VALIDATE_ITERATION = 0 VALIDATE_ITERATION = 0
class ScriptManager(TypedObjectManager): class ScriptManager(TypedObjectManager):
""" """
This Scriptmanager implements methods for searching This Scriptmanager implements methods for searching
and manipulating Scripts directly from the database. and manipulating Scripts directly from the database.
Evennia-specific search methods (will return Typeclasses or Evennia-specific search methods (will return Typeclasses or
lists of Typeclasses, whereas Django-general methods will return lists of Typeclasses, whereas Django-general methods will return
Querysets or database objects). Querysets or database objects).
dbref (converter) dbref (converter)
dbref_search dbref_search
get_dbref_range get_dbref_range
@ -38,15 +39,15 @@ class ScriptManager(TypedObjectManager):
if not obj: if not obj:
return [] return []
scripts = self.filter(db_obj=obj) scripts = self.filter(db_obj=obj)
if key: if key:
return scripts.filter(db_key=key) return scripts.filter(db_key=key)
return scripts return scripts
@returns_typeclass_list @returns_typeclass_list
def get_all_scripts(self, key=None): def get_all_scripts(self, key=None):
""" """
Return all scripts, alternative only Return all scripts, alternative only
scripts with a certain key/dbref or path. scripts with a certain key/dbref or path.
""" """
if key: if key:
dbref = self.dbref(key) dbref = self.dbref(key)
@ -66,7 +67,7 @@ class ScriptManager(TypedObjectManager):
This stops and deletes a specific script directly This stops and deletes a specific script directly
from the script database. This might be from the script database. This might be
needed for global scripts not tied to needed for global scripts not tied to
a specific game object. a specific game object.
""" """
scripts = self.get_id(dbref) scripts = self.get_id(dbref)
for script in scripts: for script in scripts:
@ -76,7 +77,7 @@ class ScriptManager(TypedObjectManager):
""" """
This cleans up the script database of all non-persistent This cleans up the script database of all non-persistent
scripts, or only those on obj. It is called every time the server restarts scripts, or only those on obj. It is called every time the server restarts
and and
""" """
if obj: if obj:
to_stop = self.filter(db_persistent=False, db_obj=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): for script in to_stop.filter(db_is_active=True):
script.stop() script.stop()
for script in to_stop.filter(db_is_active=False): for script in to_stop.filter(db_is_active=False):
script.delete() script.delete()
return nr_deleted 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): init_mode=False):
""" """
This will step through the script database and make sure This will step through the script database and make sure
all objects run scripts that are still valid in the context all objects run scripts that are still valid in the context
they are in. This is called by the game engine at regular 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 If key and/or obj is given, only update the related
script/object. script/object.
Only one of the arguments are supposed to be supplied Only one of the arguments are supposed to be supplied
at a time, since they are exclusive to each other. at a time, since they are exclusive to each other.
scripts = a list of scripts objects obtained somewhere. scripts = a list of scripts objects obtained somewhere.
obj = validate only scripts defined on a special object. obj = validate only scripts defined on a special object.
key = validate only scripts with a particular key 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 init_mode - This is used during server upstart and can have
three values: three values:
False (no init mode). Called during run. False (no init mode). Called during run.
"reset" - server reboot. Kill non-persistent scripts "reset" - server reboot. Kill non-persistent scripts
"reload" - server reload. Keep non-persistent scripts. "reload" - server reload. Keep non-persistent scripts.
This method also makes sure start any scripts it validates, This method also makes sure start any scripts it validates,
this should be harmless, since already-active scripts 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 # we store a variable that tracks if we are calling a
# validation from within another validation (avoids # validation from within another validation (avoids
# loops). # loops).
global VALIDATE_ITERATION global VALIDATE_ITERATION
if VALIDATE_ITERATION > 0: if VALIDATE_ITERATION > 0:
# we are in a nested validation. Exit. # we are in a nested validation. Exit.
VALIDATE_ITERATION -= 1 VALIDATE_ITERATION -= 1
return None, None return None, None
VALIDATE_ITERATION += 1 VALIDATE_ITERATION += 1
# not in a validation - loop. Validate as normal. # not in a validation - loop. Validate as normal.
nr_started = 0 nr_started = 0
nr_stopped = 0 nr_stopped = 0
if init_mode: if init_mode:
if init_mode == 'reset': if init_mode == 'reset':
# special mode when server starts or object logs in. # special mode when server starts or object logs in.
# This deletes all non-persistent scripts from database # This deletes all non-persistent scripts from database
nr_stopped += self.remove_non_persistent(obj=obj) nr_stopped += self.remove_non_persistent(obj=obj)
# turn off the activity flag for all remaining scripts # turn off the activity flag for all remaining scripts
scripts = self.get_all_scripts() scripts = self.get_all_scripts()
for script in scripts: for script in scripts:
script.dbobj.is_active = False script.dbobj.is_active = False
elif not scripts: elif not scripts:
# normal operation # normal operation
if dbref and self.dbref(dbref): if dbref and self.dbref(dbref):
scripts = self.get_id(dbref) scripts = self.get_id(dbref)
elif obj: elif obj:
scripts = self.get_all_scripts_on_obj(obj, key=key) scripts = self.get_all_scripts_on_obj(obj, key=key)
else: else:
scripts = self.get_all_scripts(key=key) #self.model.get_all_cached_instances() scripts = self.get_all_scripts(key=key) #self.model.get_all_cached_instances()
@ -158,46 +159,46 @@ class ScriptManager(TypedObjectManager):
VALIDATE_ITERATION -= 1 VALIDATE_ITERATION -= 1
return None, None 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: for script in scripts:
#print "validating %s (%i) (init_mode=%s)" % (script.key, id(script.dbobj), init_mode) #print "validating %s (%i) (init_mode=%s)" % (script.key, id(script.dbobj), init_mode)
if script.is_valid(): if script.is_valid():
nr_started += script.start(force_restart=init_mode) nr_started += script.start(force_restart=init_mode)
#print "back from start. nr_started=", nr_started #print "back from start. nr_started=", nr_started
else: else:
script.stop() script.stop()
nr_stopped += 1 nr_stopped += 1
VALIDATE_ITERATION -= 1 VALIDATE_ITERATION -= 1
return nr_started, nr_stopped return nr_started, nr_stopped
@returns_typeclass_list @returns_typeclass_list
def script_search(self, ostring, obj=None, only_timed=False): def script_search(self, ostring, obj=None, only_timed=False):
""" """
Search for a particular script. Search for a particular script.
ostring - search criterion - a script ID or key ostring - search criterion - a script ID or key
obj - limit search to scripts defined on this object obj - limit search to scripts defined on this object
only_timed - limit search only to scripts that run only_timed - limit search only to scripts that run
on a timer. on a timer.
""" """
ostring = ostring.strip() ostring = ostring.strip()
dbref = self.dbref(ostring) dbref = self.dbref(ostring)
if dbref: if dbref:
# this is a dbref, try to find the script directly # this is a dbref, try to find the script directly
dbref_match = self.dbref_search(dbref) dbref_match = self.dbref_search(dbref)
if dbref_match: if dbref_match:
ok = True ok = True
if obj and obj != dbref_match.obj: if obj and obj != dbref_match.obj:
ok = False ok = False
if only_timed and dbref_match.interval: if only_timed and dbref_match.interval:
ok = False ok = False
if ok: if ok:
return [dbref_match] return [dbref_match]
# not a dbref; normal search # not a dbref; normal search
scripts = self.filter(db_key__iexact=ostring) scripts = self.filter(db_key__iexact=ostring)
if obj: if obj:
scripts = scripts.exclude(db_obj=None).filter(db_obj__db_key__iexact=ostring) scripts = scripts.exclude(db_obj=None).filter(db_obj__db_key__iexact=ostring)
if only_timed: if only_timed:

View file

@ -30,6 +30,8 @@ from src.typeclasses.models import Attribute, TypedObject
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from src.scripts.manager import ScriptManager from src.scripts.manager import ScriptManager
__all__ = ("ScriptAttribute", "ScriptDB")
#------------------------------------------------------------ #------------------------------------------------------------
# #
# ScriptAttribute # ScriptAttribute
@ -121,122 +123,122 @@ class ScriptDB(TypedObject):
# desc property (wraps db_desc) # desc property (wraps db_desc)
#@property #@property
def desc_get(self): def __desc_get(self):
"Getter. Allows for value = self.desc" "Getter. Allows for value = self.desc"
return self.db_desc return self.db_desc
#@desc.setter #@desc.setter
def desc_set(self, value): def __desc_set(self, value):
"Setter. Allows for self.desc = value" "Setter. Allows for self.desc = value"
self.db_desc = value self.db_desc = value
self.save() self.save()
#@desc.deleter #@desc.deleter
def desc_del(self): def __desc_del(self):
"Deleter. Allows for del self.desc" "Deleter. Allows for del self.desc"
self.db_desc = "" self.db_desc = ""
self.save() self.save()
desc = property(desc_get, desc_set, desc_del) desc = property(__desc_get, __desc_set, __desc_del)
# obj property (wraps db_obj) # obj property (wraps db_obj)
#@property #@property
def obj_get(self): def __obj_get(self):
"Getter. Allows for value = self.obj" "Getter. Allows for value = self.obj"
return self.db_obj return self.db_obj
#@obj.setter #@obj.setter
def obj_set(self, value): def __obj_set(self, value):
"Setter. Allows for self.obj = value" "Setter. Allows for self.obj = value"
self.db_obj = value self.db_obj = value
self.save() self.save()
#@obj.deleter #@obj.deleter
def obj_del(self): def __obj_del(self):
"Deleter. Allows for del self.obj" "Deleter. Allows for del self.obj"
self.db_obj = None self.db_obj = None
self.save() self.save()
obj = property(obj_get, obj_set, obj_del) obj = property(__obj_get, __obj_set, __obj_del)
# interval property (wraps db_interval) # interval property (wraps db_interval)
#@property #@property
def interval_get(self): def __interval_get(self):
"Getter. Allows for value = self.interval" "Getter. Allows for value = self.interval"
return self.db_interval return self.db_interval
#@interval.setter #@interval.setter
def interval_set(self, value): def __interval_set(self, value):
"Setter. Allows for self.interval = value" "Setter. Allows for self.interval = value"
self.db_interval = int(value) self.db_interval = int(value)
self.save() self.save()
#@interval.deleter #@interval.deleter
def interval_del(self): def __interval_del(self):
"Deleter. Allows for del self.interval" "Deleter. Allows for del self.interval"
self.db_interval = 0 self.db_interval = 0
self.save() 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) # start_delay property (wraps db_start_delay)
#@property #@property
def start_delay_get(self): def __start_delay_get(self):
"Getter. Allows for value = self.start_delay" "Getter. Allows for value = self.start_delay"
return self.db_start_delay return self.db_start_delay
#@start_delay.setter #@start_delay.setter
def start_delay_set(self, value): def __start_delay_set(self, value):
"Setter. Allows for self.start_delay = value" "Setter. Allows for self.start_delay = value"
self.db_start_delay = value self.db_start_delay = value
self.save() self.save()
#@start_delay.deleter #@start_delay.deleter
def start_delay_del(self): def __start_delay_del(self):
"Deleter. Allows for del self.start_delay" "Deleter. Allows for del self.start_delay"
self.db_start_delay = False self.db_start_delay = False
self.save() 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) # repeats property (wraps db_repeats)
#@property #@property
def repeats_get(self): def __repeats_get(self):
"Getter. Allows for value = self.repeats" "Getter. Allows for value = self.repeats"
return self.db_repeats return self.db_repeats
#@repeats.setter #@repeats.setter
def repeats_set(self, value): def __repeats_set(self, value):
"Setter. Allows for self.repeats = value" "Setter. Allows for self.repeats = value"
self.db_repeats = int(value) self.db_repeats = int(value)
self.save() self.save()
#@repeats.deleter #@repeats.deleter
def repeats_del(self): def __repeats_del(self):
"Deleter. Allows for del self.repeats" "Deleter. Allows for del self.repeats"
self.db_repeats = 0 self.db_repeats = 0
self.save() self.save()
repeats = property(repeats_get, repeats_set, repeats_del) repeats = property(__repeats_get, __repeats_set, __repeats_del)
# persistent property (wraps db_persistent) # persistent property (wraps db_persistent)
#@property #@property
def persistent_get(self): def __persistent_get(self):
"Getter. Allows for value = self.persistent" "Getter. Allows for value = self.persistent"
return self.db_persistent return self.db_persistent
#@persistent.setter #@persistent.setter
def persistent_set(self, value): def __persistent_set(self, value):
"Setter. Allows for self.persistent = value" "Setter. Allows for self.persistent = value"
self.db_persistent = value self.db_persistent = value
self.save() self.save()
#@persistent.deleter #@persistent.deleter
def persistent_del(self): def __persistent_del(self):
"Deleter. Allows for del self.persistent" "Deleter. Allows for del self.persistent"
self.db_persistent = False self.db_persistent = False
self.save() 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) # is_active property (wraps db_is_active)
#@property #@property
def is_active_get(self): def __is_active_get(self):
"Getter. Allows for value = self.is_active" "Getter. Allows for value = self.is_active"
return self.db_is_active return self.db_is_active
#@is_active.setter #@is_active.setter
def is_active_set(self, value): def __is_active_set(self, value):
"Setter. Allows for self.is_active = value" "Setter. Allows for self.is_active = value"
self.db_is_active = value self.db_is_active = value
self.save() self.save()
#@is_active.deleter #@is_active.deleter
def is_active_del(self): def __is_active_del(self):
"Deleter. Allows for del self.is_active" "Deleter. Allows for del self.is_active"
self.db_is_active = False self.db_is_active = False
self.save() 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)
# #
# #

View file

@ -2,27 +2,29 @@
This module contains the base Script class that all This module contains the base Script class that all
scripts are inheriting from. 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.defer import maybeDeferred
from twisted.internet.task import LoopingCall from twisted.internet.task import LoopingCall
from twisted.internet import task from twisted.internet import task
from src.server.sessionhandler import SESSIONS from src.server.sessionhandler import SESSIONS
from src.typeclasses.typeclass import TypeClass from src.typeclasses.typeclass import TypeClass
from src.scripts.models import ScriptDB from src.scripts.models import ScriptDB
from src.comms import channelhandler from src.comms import channelhandler
from src.utils import logger 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): class ScriptClass(TypeClass):
""" """
Base class for scripts. Don't inherit from this, inherit from Script instead. Base class for scripts. Don't inherit from this, inherit from Script instead.
""" """
# private methods # private methods
def __eq__(self, other): def __eq__(self, other):
""" """
@ -32,7 +34,7 @@ class ScriptClass(TypeClass):
try: try:
return other.id == self.id return other.id == self.id
except Exception: except Exception:
return False return False
def _start_task(self, start_now=True): def _start_task(self, start_now=True):
"start task runner" "start task runner"
@ -43,17 +45,17 @@ class ScriptClass(TypeClass):
#print " start with paused time:", self.key, self.ndb._paused_time #print " start with paused time:", self.key, self.ndb._paused_time
self.ndb.twisted_task.start(self.ndb._paused_time, now=False) self.ndb.twisted_task.start(self.ndb._paused_time, now=False)
else: else:
# starting script anew. # starting script anew.
#print "_start_task: self.interval:", self.key, self.dbobj.interval #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.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): def _stop_task(self):
"stop task runner" "stop task runner"
try: try:
#print "stopping twisted task:", id(self.ndb.twisted_task), self.obj #print "stopping twisted task:", id(self.ndb.twisted_task), self.obj
if self.ndb.twisted_task and self.ndb.twisted_task.running: if self.ndb.twisted_task and self.ndb.twisted_task.running:
self.ndb.twisted_task.stop() self.ndb.twisted_task.stop()
except Exception: except Exception:
logger.log_trace() logger.log_trace()
def _step_err_callback(self, e): def _step_err_callback(self, e):
@ -69,14 +71,14 @@ class ScriptClass(TypeClass):
"step task runner. No try..except needed due to defer wrap." "step task runner. No try..except needed due to defer wrap."
if not self.is_valid(): if not self.is_valid():
self.stop() self.stop()
return return
self.at_repeat() self.at_repeat()
repeats = self.dbobj.db_repeats repeats = self.dbobj.db_repeats
if repeats <= 0: if repeats <= 0:
pass # infinite repeat pass # infinite repeat
elif repeats == 1: elif repeats == 1:
self.stop() self.stop()
return return
else: else:
self.dbobj.db_repeats -= 1 self.dbobj.db_repeats -= 1
self.ndb.time_last_called = int(time()) self.ndb.time_last_called = int(time())
@ -84,7 +86,7 @@ class ScriptClass(TypeClass):
if self.ndb._paused_time: if self.ndb._paused_time:
# this means we were running an unpaused script, for the time remaining # 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 #print "switching to normal run:", self.key
del self.ndb._paused_time del self.ndb._paused_time
self._stop_task() self._stop_task()
@ -93,23 +95,23 @@ class ScriptClass(TypeClass):
def _step_task(self): def _step_task(self):
"step task" "step task"
try: try:
d = maybeDeferred(self._step_succ_callback) d = maybeDeferred(self._step_succ_callback)
d.addErrback(self._step_err_callback) d.addErrback(self._step_err_callback)
return d return d
except Exception: except Exception:
logger.log_trace() logger.log_trace()
# Public methods # Public methods
def time_until_next_repeat(self): def time_until_next_repeat(self):
""" """
Returns the time in seconds until the script will be 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 This is not used in any way by the script's stepping
system; it's only here for the user to be able to 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: try:
if self.ndb._paused_time: if self.ndb._paused_time:
@ -117,7 +119,7 @@ class ScriptClass(TypeClass):
else: else:
return max(0, (self.ndb.time_last_called + self.dbobj.db_interval) - int(time())) return max(0, (self.ndb.time_last_called + self.dbobj.db_interval) - int(time()))
except Exception: except Exception:
return None return None
def start(self, force_restart=False): def start(self, force_restart=False):
""" """
@ -125,33 +127,33 @@ class ScriptClass(TypeClass):
persistent scripts, this is usually once every server start) persistent scripts, this is usually once every server start)
force_restart - if True, will always restart the script, regardless force_restart - if True, will always restart the script, regardless
of if it has started before. of if it has started before.
returns 0 or 1 to indicated the script has been started or not. Used in counting. 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), #print "Script %s (%s) start (active:%s, force:%s) ..." % (self.key, id(self.dbobj),
# self.is_active, force_restart) # self.is_active, force_restart)
if self.dbobj.is_active and not force_restart: if self.dbobj.is_active and not force_restart:
# script already runs and should not be restarted. # script already runs and should not be restarted.
return 0 return 0
obj = self.obj obj = self.obj
if obj: if obj:
# check so the scripted object is valid and initalized # check so the scripted object is valid and initalized
try: try:
dummy = object.__getattribute__(obj, 'cmdset') dummy = object.__getattribute__(obj, 'cmdset')
except AttributeError: except AttributeError:
# this means the object is not initialized. # this means the object is not initialized.
self.dbobj.is_active = False self.dbobj.is_active = False
return 0 return 0
# try to restart a paused script # try to restart a paused script
if self.unpause(): if self.unpause():
return 1 return 1
# try to start the script from scratch # try to start the script from scratch
try: try:
self.dbobj.is_active = True self.dbobj.is_active = True
self.at_start() self.at_start()
if self.dbobj.db_interval > 0: if self.dbobj.db_interval > 0:
@ -159,15 +161,15 @@ class ScriptClass(TypeClass):
return 1 return 1
except Exception: except Exception:
logger.log_trace() logger.log_trace()
self.dbobj.is_active = False self.dbobj.is_active = False
return 0 return 0
def stop(self, kill=False): def stop(self, kill=False):
""" """
Called to stop the script from running. 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 #print "stopping script %s" % self.key
#import pdb #import pdb
@ -197,29 +199,29 @@ class ScriptClass(TypeClass):
#print "pausing", self.key, self.time_until_next_repeat() #print "pausing", self.key, self.time_until_next_repeat()
dt = self.time_until_next_repeat() dt = self.time_until_next_repeat()
if dt == None: if dt == None:
return return
self.db._paused_time = dt self.db._paused_time = dt
self._stop_task() self._stop_task()
def unpause(self): 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 #print "unpausing", self.key, self.db._paused_time
dt = self.db._paused_time dt = self.db._paused_time
if dt == None: if dt == None:
return False return False
try: try:
self.dbobj.is_active = True self.dbobj.is_active = True
self.at_start() self.at_start()
self.ndb._paused_time = dt self.ndb._paused_time = dt
self._start_task(start_now=False) self._start_task(start_now=False)
del self.db._paused_time del self.db._paused_time
except Exception, e: except Exception, e:
logger.log_trace() logger.log_trace()
self.dbobj.is_active = False self.dbobj.is_active = False
return False return False
return True return True
# hooks # hooks
def at_script_creation(self): def at_script_creation(self):
@ -230,7 +232,7 @@ class ScriptClass(TypeClass):
pass pass
def at_start(self): def at_start(self):
"placeholder." "placeholder."
pass pass
def at_stop(self): def at_stop(self):
"placeholder" "placeholder"
pass pass
@ -240,7 +242,7 @@ class ScriptClass(TypeClass):
def at_init(self): def at_init(self):
"called when typeclass re-caches. Usually not used for scripts." "called when typeclass re-caches. Usually not used for scripts."
pass pass
# #
# Base Script - inherit from this # Base Script - inherit from this
@ -255,36 +257,36 @@ class Script(ScriptClass):
def __init__(self, dbobj): def __init__(self, dbobj):
""" """
This is the base TypeClass for all Scripts. Scripts describe events, timers and states in game, 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: Script API:
* Available properties (only available on initiated Typeclass objects) * Available properties (only available on initiated Typeclass objects)
key (string) - name of object key (string) - name of object
name (string)- same as key 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. 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. 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 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. 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 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 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()) 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 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 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 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 * Handlers
locks - lock-handler: use locks.add() to add new lock strings 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 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) start() - start script (this usually happens automatically at creation and obj.script.add() etc)
stop() - stop script, and delete it 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. 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 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 at_script_creation() - called only once, when an object of this
class is first created. class is first created.
is_valid() - is called to check if the script is valid to be running 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 at the current time. If is_valid() returns False, the running
script is stopped and removed from the game. You can use this script is stopped and removed from the game. You can use this
to check state changes (i.e. an script tracking some combat to check state changes (i.e. an script tracking some combat
stats at regular intervals is only valid to run while there is stats at regular intervals is only valid to run while there is
actual combat going on). actual combat going on).
at_start() - Called every time the script is started, which for persistent at_start() - Called every time the script is started, which for persistent
scripts is at least once every server start. Note that this is scripts is at least once every server start. Note that this is
unaffected by self.delay_start, which only delays the first call 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 at_repeat() - Called every self.interval seconds. It will be called immediately
upon launch unless self.delay_start is True, which will delay upon launch unless self.delay_start is True, which will delay
the first call of this method by self.interval seconds. If the first call of this method by self.interval seconds. If
self.interval==0, this method will never be called. 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 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. 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. 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) super(Script, self).__init__(dbobj)
def at_script_creation(self): def at_script_creation(self):
""" """
Only called once, by the create function. Only called once, by the create function.
""" """
self.key = "<unnamed>" self.key = "<unnamed>"
self.desc = "" self.desc = ""
self.interval = 0 # infinite self.interval = 0 # infinite
self.start_delay = False self.start_delay = False
self.repeats = 0 # infinite self.repeats = 0 # infinite
self.persistent = False self.persistent = False
def is_valid(self): 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 Should return a boolean. The method is assumed to collect all needed
information from its related self.obj. information from its related self.obj.
""" """
@ -342,7 +344,7 @@ class Script(ScriptClass):
def at_start(self): def at_start(self):
""" """
Called whenever the script is started, which for persistent 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) when starting again after a pause (such as after a server reload)
""" """
pass pass
@ -350,10 +352,10 @@ class Script(ScriptClass):
def at_repeat(self): def at_repeat(self):
""" """
Called repeatedly if this Script is set to repeat Called repeatedly if this Script is set to repeat
regularly. regularly.
""" """
pass pass
def at_stop(self): def at_stop(self):
""" """
Called whenever when it's time for this script to stop Called whenever when it's time for this script to stop
@ -363,38 +365,38 @@ class Script(ScriptClass):
def at_server_reload(self): 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, 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 pass
def at_server_shutdown(self): def at_server_shutdown(self):
""" """
This hook is called whenever the server is shutting down fully (i.e. not for This hook is called whenever the server is shutting down fully (i.e. not for
a restart). a restart).
""" """
pass pass
# Some useful default Script types used by Evennia. # Some useful default Script types used by Evennia.
class DoNothing(Script): class DoNothing(Script):
"An script that does nothing. Used as default fallback." "An script that does nothing. Used as default fallback."
def at_script_creation(self): def at_script_creation(self):
"Setup the script" "Setup the script"
self.key = "sys_do_nothing" self.key = "sys_do_nothing"
self.desc = "This is a placeholder script." self.desc = "This is a placeholder script."
class CheckSessions(Script): class CheckSessions(Script):
"Check sessions regularly." "Check sessions regularly."
def at_script_creation(self): def at_script_creation(self):
"Setup the script" "Setup the script"
self.key = "sys_session_check" self.key = "sys_session_check"
self.desc = "Checks sessions so they are live." self.desc = "Checks sessions so they are live."
self.interval = 60 # repeat every 60 seconds self.interval = 60 # repeat every 60 seconds
self.persistent = True self.persistent = True
def at_repeat(self): def at_repeat(self):
"called every 60 seconds" "called every 60 seconds"
@ -403,7 +405,7 @@ class CheckSessions(Script):
SESSIONS.validate_sessions() SESSIONS.validate_sessions()
class ValidateScripts(Script): class ValidateScripts(Script):
"Check script validation regularly" "Check script validation regularly"
def at_script_creation(self): def at_script_creation(self):
"Setup the script" "Setup the script"
self.key = "sys_scripts_validate" self.key = "sys_scripts_validate"
@ -412,31 +414,31 @@ class ValidateScripts(Script):
self.persistent = True self.persistent = True
def at_repeat(self): def at_repeat(self):
"called every hour" "called every hour"
#print "ValidateScripts run." #print "ValidateScripts run."
ScriptDB.objects.validate() ScriptDB.objects.validate()
class ValidateChannelHandler(Script): 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): def at_script_creation(self):
"Setup the script" "Setup the script"
self.key = "sys_channels_validate" 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.interval = 3700 # validate a little later than ValidateScripts
self.persistent = True self.persistent = True
def at_repeat(self): def at_repeat(self):
"called every hour+" "called every hour+"
#print "ValidateChannelHandler run." #print "ValidateChannelHandler run."
channelhandler.CHANNELHANDLER.update() channelhandler.CHANNELHANDLER.update()
class AddCmdSet(Script): class AddCmdSet(Script):
""" """
This script permanently assigns a command set This script permanently assigns a command set
to an object whenever it is started. This is not to an object whenever it is started. This is not
used by the core system anymore, it's here mostly used by the core system anymore, it's here mostly
as an example. as an example.
""" """
def at_script_creation(self): def at_script_creation(self):
"Setup the script" "Setup the script"
@ -444,12 +446,12 @@ class AddCmdSet(Script):
self.key = "add_cmdset" self.key = "add_cmdset"
if not self.desc: if not self.desc:
self.desc = "Adds a cmdset to an object." self.desc = "Adds a cmdset to an object."
self.persistent = True self.persistent = True
# this needs to be assigned to upon creation. # this needs to be assigned to upon creation.
# It should be a string pointing to the right # It should be a string pointing to the right
# cmdset module and cmdset class name, e.g. # cmdset module and cmdset class name, e.g.
# 'examples.cmdset_redbutton.RedButtonCmdSet' # 'examples.cmdset_redbutton.RedButtonCmdSet'
# self.db.cmdset = <cmdset_path> # self.db.cmdset = <cmdset_path>
# self.db.add_default = <bool> # self.db.add_default = <bool>
@ -461,12 +463,12 @@ class AddCmdSet(Script):
self.obj.cmdset.add_default(cmdset) self.obj.cmdset.add_default(cmdset)
else: else:
self.obj.cmdset.add(cmdset) self.obj.cmdset.add(cmdset)
def at_stop(self): def at_stop(self):
""" """
This removes the cmdset when the script stops This removes the cmdset when the script stops
""" """
cmdset = self.db.cmdset cmdset = self.db.cmdset
if cmdset: if cmdset:
if self.db.add_default: if self.db.add_default:
self.obj.cmdset.delete_default() self.obj.cmdset.delete_default()

View file

@ -7,7 +7,7 @@ from functools import update_wrapper
from django.db import models from django.db import models
from src.utils import idmapper from src.utils import idmapper
from src.utils.utils import make_iter from src.utils.utils import make_iter
#from src.typeclasses import idmap __all__ = ("AttributeManager", "TypedObjectManager")
# Managers # Managers

View file

@ -43,6 +43,8 @@ from src.locks.lockhandler import LockHandler
from src.utils import logger, utils from src.utils import logger, utils
from src.utils.utils import make_iter, is_iter, has_parent, to_unicode, to_str 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] _PERMISSION_HIERARCHY = [p.lower() for p in settings.PERMISSION_HIERARCHY]
_CTYPEGET = ContentType.objects.get _CTYPEGET = ContentType.objects.get

View file

@ -13,6 +13,8 @@ used by the typesystem or django itself.
from src.utils.logger import log_trace, log_errmsg from src.utils.logger import log_trace, log_errmsg
from django.conf import settings from django.conf import settings
__all__ = ("TypeClass",)
# these are called so many times it's worth to avoid lookup calls # these are called so many times it's worth to avoid lookup calls
_GA = object.__getattribute__ _GA = object.__getattribute__
_SA = object.__setattr__ _SA = object.__setattr__