Fixes to content_cache handler. Started a simple test of a cmd-limiter.
This commit is contained in:
parent
b94bb17576
commit
e201cda2c3
5 changed files with 75 additions and 30 deletions
|
|
@ -265,19 +265,26 @@ class ObjectDB(TypedObject):
|
||||||
self.save(update_fields=["db_location"])
|
self.save(update_fields=["db_location"])
|
||||||
location = property(__location_get, __location_set, __location_del)
|
location = property(__location_get, __location_set, __location_del)
|
||||||
|
|
||||||
def _db_location_post_save(self):
|
def at_db_location_postsave(self, new):
|
||||||
"""
|
"""
|
||||||
This is called automatically after the location field was saved,
|
This is called automatically after the location field was
|
||||||
no matter how. It checks for a variable _safe_contents_update to
|
saved, no matter how. It checks for a variable
|
||||||
know if the save was triggered via the proper handler or not.
|
_safe_contents_update to know if the save was triggered via
|
||||||
|
the location handler (which updates the contents cache) or
|
||||||
Since we cannot know at this point was old_location was, we
|
not.
|
||||||
trigger a full-on contents_cache update here.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if not hasattr(self, "_safe_contents_update"):
|
if not hasattr(self, "_safe_contents_update"):
|
||||||
logger.log_warn("db_location direct save triggered contents_cache.init() for all objects!")
|
# changed/set outside of the location handler
|
||||||
[o.contents_cache.init() for o in self.__dbclass__.get_all_cached_instances()]
|
if new:
|
||||||
|
# if new, there is no previous location to worry about
|
||||||
|
if self.db_location:
|
||||||
|
self.db_location.contents_cache.add(self)
|
||||||
|
else:
|
||||||
|
# Since we cannot know at this point was old_location was, we
|
||||||
|
# trigger a full-on contents_cache update here.
|
||||||
|
logger.log_warn("db_location direct save triggered contents_cache.init() for all objects!")
|
||||||
|
[o.contents_cache.init() for o in self.__dbclass__.get_all_cached_instances()]
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"Define Django meta options"
|
"Define Django meta options"
|
||||||
|
|
|
||||||
|
|
@ -53,18 +53,22 @@ if not _OOB_ERROR:
|
||||||
class OOBFieldMonitor(object):
|
class OOBFieldMonitor(object):
|
||||||
"""
|
"""
|
||||||
This object should be stored on the
|
This object should be stored on the
|
||||||
tracked object as "_oob_at_<fieldname>_update".
|
tracked object as "_oob_at_<fieldname>_postsave".
|
||||||
the update() method will be called by the
|
the update() method w ill be called by the
|
||||||
save mechanism, which in turn will call the
|
save mechanism, which in turn will call the
|
||||||
user-customizable func()
|
user-customizable func()
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self, obj):
|
||||||
"""
|
"""
|
||||||
This initializes the monitor with the object it sits on.
|
This initializes the monitor with the object it sits on.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
obj (Object): object handler is defined on.
|
||||||
"""
|
"""
|
||||||
|
self.obj = obj
|
||||||
self.subscribers = defaultdict(list)
|
self.subscribers = defaultdict(list)
|
||||||
|
|
||||||
def __call__(self, obj, fieldname):
|
def __call__(self, fieldname):
|
||||||
"""
|
"""
|
||||||
Called by the save() mechanism when the given
|
Called by the save() mechanism when the given
|
||||||
field has updated.
|
field has updated.
|
||||||
|
|
@ -74,7 +78,7 @@ class OOBFieldMonitor(object):
|
||||||
# a potential list of oob commands to call when this
|
# a potential list of oob commands to call when this
|
||||||
# field changes.
|
# field changes.
|
||||||
for (oobfuncname, args, kwargs) in oobtuples:
|
for (oobfuncname, args, kwargs) in oobtuples:
|
||||||
OOB_HANDLER.execute_cmd(sessid, oobfuncname, fieldname, obj, *args, **kwargs)
|
OOB_HANDLER.execute_cmd(sessid, oobfuncname, fieldname, self.obj, *args, **kwargs)
|
||||||
|
|
||||||
def add(self, sessid, oobfuncname, *args, **kwargs):
|
def add(self, sessid, oobfuncname, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
@ -156,7 +160,7 @@ class OOBHandler(TickerHandler):
|
||||||
fieldmonitorname = self._get_fieldmonitor_name(fieldname)
|
fieldmonitorname = self._get_fieldmonitor_name(fieldname)
|
||||||
if not hasattr(obj, fieldmonitorname):
|
if not hasattr(obj, fieldmonitorname):
|
||||||
# assign a new fieldmonitor to the object
|
# assign a new fieldmonitor to the object
|
||||||
_SA(obj, fieldmonitorname, OOBFieldMonitor())
|
_SA(obj, fieldmonitorname, OOBFieldMonitor(obj))
|
||||||
# register the session with the monitor
|
# register the session with the monitor
|
||||||
_GA(obj, fieldmonitorname).add(sessid, oobfuncname, *args, **kwargs)
|
_GA(obj, fieldmonitorname).add(sessid, oobfuncname, *args, **kwargs)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ It is stored on the Server side (as opposed to protocol-specific sessions which
|
||||||
are stored on the Portal side)
|
are stored on the Portal side)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import time
|
from time import time
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from evennia.comms.models import ChannelDB
|
from evennia.comms.models import ChannelDB
|
||||||
|
|
@ -48,6 +48,7 @@ class ServerSession(Session):
|
||||||
self.player = None
|
self.player = None
|
||||||
self.cmdset_storage_string = ""
|
self.cmdset_storage_string = ""
|
||||||
self.cmdset = CmdSetHandler(self, True)
|
self.cmdset = CmdSetHandler(self, True)
|
||||||
|
self.cmd_per_second = 0.0
|
||||||
|
|
||||||
def __cmdset_storage_get(self):
|
def __cmdset_storage_get(self):
|
||||||
return [path.strip() for path in self.cmdset_storage_string.split(',')]
|
return [path.strip() for path in self.cmdset_storage_string.split(',')]
|
||||||
|
|
@ -98,7 +99,7 @@ class ServerSession(Session):
|
||||||
self.uid = self.player.id
|
self.uid = self.player.id
|
||||||
self.uname = self.player.username
|
self.uname = self.player.username
|
||||||
self.logged_in = True
|
self.logged_in = True
|
||||||
self.conn_time = time.time()
|
self.conn_time = time()
|
||||||
self.puid = None
|
self.puid = None
|
||||||
self.puppet = None
|
self.puppet = None
|
||||||
self.cmdset_storage = settings.CMDSET_SESSION
|
self.cmdset_storage = settings.CMDSET_SESSION
|
||||||
|
|
@ -184,12 +185,11 @@ class ServerSession(Session):
|
||||||
and command counters.
|
and command counters.
|
||||||
"""
|
"""
|
||||||
# Store the timestamp of the user's last command.
|
# Store the timestamp of the user's last command.
|
||||||
self.cmd_last = time.time()
|
|
||||||
if not idle:
|
if not idle:
|
||||||
# Increment the user's command counter.
|
# Increment the user's command counter.
|
||||||
self.cmd_total += 1
|
self.cmd_total += 1
|
||||||
# Player-visible idle time, not used in idle timeout calcs.
|
# Player-visible idle time, not used in idle timeout calcs.
|
||||||
self.cmd_last_visible = time.time()
|
self.cmd_last_visible = time()
|
||||||
|
|
||||||
def data_in(self, text=None, **kwargs):
|
def data_in(self, text=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
@ -200,6 +200,10 @@ class ServerSession(Session):
|
||||||
oobhandler at this point.
|
oobhandler at this point.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
now = time()
|
||||||
|
self.cmd_per_second = 1.0 / (now - self.cmd_last)
|
||||||
|
self.cmd_last = now
|
||||||
|
|
||||||
#explicitly check for None since text can be an empty string, which is
|
#explicitly check for None since text can be an empty string, which is
|
||||||
#also valid
|
#also valid
|
||||||
if text is not None:
|
if text is not None:
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,12 @@ There are two similar but separate stores of sessions:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import time
|
from time import time
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from evennia.commands.cmdhandler import CMD_LOGINSTART
|
from evennia.commands.cmdhandler import CMD_LOGINSTART
|
||||||
from evennia.utils.utils import variable_from_module, is_iter, \
|
from evennia.utils.utils import variable_from_module, is_iter, \
|
||||||
to_str, to_unicode, strip_control_sequences, make_iter
|
to_str, to_unicode, strip_control_sequences, make_iter
|
||||||
|
from evennia.utils import logger
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import cPickle as pickle
|
import cPickle as pickle
|
||||||
|
|
@ -46,11 +47,15 @@ PCONNSYNC = chr(10) # portal post-syncing session
|
||||||
# i18n
|
# i18n
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
SERVERNAME = settings.SERVERNAME
|
_SERVERNAME = settings.SERVERNAME
|
||||||
_MULTISESSION_MODE = settings.MULTISESSION_MODE
|
_MULTISESSION_MODE = settings.MULTISESSION_MODE
|
||||||
IDLE_TIMEOUT = settings.IDLE_TIMEOUT
|
_IDLE_TIMEOUT = settings.IDLE_TIMEOUT
|
||||||
|
_MAX_SERVER_COMMANDS_PER_SECOND = 100.0
|
||||||
|
_MAX_SESSION_COMMANDS_PER_SECOND = 5.0
|
||||||
|
|
||||||
|
|
||||||
|
_ERROR_COMMAND_OVERFLOW = "You entered commands too fast. Wait a moment and try again."
|
||||||
|
|
||||||
def delayed_import():
|
def delayed_import():
|
||||||
"Helper method for delayed import of all needed entities"
|
"Helper method for delayed import of all needed entities"
|
||||||
global _ServerSession, _PlayerDB, _ServerConfig, _ScriptDB
|
global _ServerSession, _PlayerDB, _ServerConfig, _ScriptDB
|
||||||
|
|
@ -130,7 +135,9 @@ class ServerSessionHandler(SessionHandler):
|
||||||
"""
|
"""
|
||||||
self.sessions = {}
|
self.sessions = {}
|
||||||
self.server = None
|
self.server = None
|
||||||
self.server_data = {"servername": SERVERNAME}
|
self.server_data = {"servername": _SERVERNAME}
|
||||||
|
self.cmd_last = time()
|
||||||
|
self.cmd_per_second = 0.0
|
||||||
|
|
||||||
def portal_connect(self, portalsession):
|
def portal_connect(self, portalsession):
|
||||||
"""
|
"""
|
||||||
|
|
@ -359,11 +366,11 @@ class ServerSessionHandler(SessionHandler):
|
||||||
Check all currently connected sessions (logged in and not)
|
Check all currently connected sessions (logged in and not)
|
||||||
and see if any are dead or idle
|
and see if any are dead or idle
|
||||||
"""
|
"""
|
||||||
tcurr = time.time()
|
tcurr = time()
|
||||||
reason = _("Idle timeout exceeded, disconnecting.")
|
reason = _("Idle timeout exceeded, disconnecting.")
|
||||||
for session in (session for session in self.sessions.values()
|
for session in (session for session in self.sessions.values()
|
||||||
if session.logged_in and IDLE_TIMEOUT > 0
|
if session.logged_in and _IDLE_TIMEOUT > 0
|
||||||
and (tcurr - session.cmd_last) > IDLE_TIMEOUT):
|
and (tcurr - session.cmd_last) > _IDLE_TIMEOUT):
|
||||||
self.disconnect(session, reason=reason)
|
self.disconnect(session, reason=reason)
|
||||||
|
|
||||||
def player_count(self, count=True):
|
def player_count(self, count=True):
|
||||||
|
|
@ -493,6 +500,17 @@ class ServerSessionHandler(SessionHandler):
|
||||||
"""
|
"""
|
||||||
session = self.sessions.get(sessid, None)
|
session = self.sessions.get(sessid, None)
|
||||||
if session:
|
if session:
|
||||||
|
|
||||||
|
now = time()
|
||||||
|
self.cmd_per_second = 1.0 / (now - self.cmd_last)
|
||||||
|
self.cmd_last = now
|
||||||
|
|
||||||
|
if self.cmd_per_second > _MAX_SERVER_COMMANDS_PER_SECOND:
|
||||||
|
if session.cmd_per_second > _MAX_SESSION_COMMANDS_PER_SECOND:
|
||||||
|
session.data.out(text=_ERROR_COMMAND_OVERFLOW)
|
||||||
|
logger.log_infomsg("overflow kicked in for session %s: %s" % (session.sessid, text))
|
||||||
|
return
|
||||||
|
|
||||||
text = text and to_unicode(strip_control_sequences(text), encoding=session.encoding)
|
text = text and to_unicode(strip_control_sequences(text), encoding=session.encoding)
|
||||||
if "oob" in kwargs:
|
if "oob" in kwargs:
|
||||||
# incoming data is always on the form (cmdname, args, kwargs)
|
# incoming data is always on the form (cmdname, args, kwargs)
|
||||||
|
|
|
||||||
|
|
@ -329,7 +329,17 @@ class SharedMemoryModel(Model):
|
||||||
super(SharedMemoryModel, self).delete(*args, **kwargs)
|
super(SharedMemoryModel, self).delete(*args, **kwargs)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
"save method tracking process/thread issues"
|
"""
|
||||||
|
Central database save operation.
|
||||||
|
|
||||||
|
Arguments as per django documentation
|
||||||
|
|
||||||
|
Calls:
|
||||||
|
self.at_<fieldname>_postsave(new)
|
||||||
|
# this is a wrapper set by oobhandler:
|
||||||
|
self._oob_at_<fieldname>_postsave()
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
if _IS_SUBPROCESS:
|
if _IS_SUBPROCESS:
|
||||||
# we keep a store of objects modified in subprocesses so
|
# we keep a store of objects modified in subprocesses so
|
||||||
|
|
@ -348,24 +358,26 @@ class SharedMemoryModel(Model):
|
||||||
callFromThread(_save_callback, self, *args, **kwargs)
|
callFromThread(_save_callback, self, *args, **kwargs)
|
||||||
|
|
||||||
# update field-update hooks and eventual OOB watchers
|
# update field-update hooks and eventual OOB watchers
|
||||||
|
new = False
|
||||||
if "update_fields" in kwargs and kwargs["update_fields"]:
|
if "update_fields" in kwargs and kwargs["update_fields"]:
|
||||||
# get field objects from their names
|
# get field objects from their names
|
||||||
update_fields = (self._meta.get_field_by_name(field)[0]
|
update_fields = (self._meta.get_field_by_name(field)[0]
|
||||||
for field in kwargs.get("update_fields"))
|
for field in kwargs.get("update_fields"))
|
||||||
else:
|
else:
|
||||||
# meta.fields are already field objects; get them all
|
# meta.fields are already field objects; get them all
|
||||||
|
new =True
|
||||||
update_fields = self._meta.fields
|
update_fields = self._meta.fields
|
||||||
for field in update_fields:
|
for field in update_fields:
|
||||||
fieldname = field.name
|
fieldname = field.name
|
||||||
# if a hook is defined it must be named exactly on this form
|
# if a hook is defined it must be named exactly on this form
|
||||||
hookname = "_at_%s_postsave" % fieldname
|
hookname = "at_%s_postsave" % fieldname
|
||||||
if hasattr(self, hookname) and callable(_GA(self, hookname)):
|
if hasattr(self, hookname) and callable(_GA(self, hookname)):
|
||||||
_GA(self, hookname)()
|
_GA(self, hookname)(new)
|
||||||
# if a trackerhandler is set on this object, update it with the
|
# if a trackerhandler is set on this object, update it with the
|
||||||
# fieldname and the new value
|
# fieldname and the new value
|
||||||
fieldtracker = "_oob_at_%s_postsave" % fieldname
|
fieldtracker = "_oob_at_%s_postsave" % fieldname
|
||||||
if hasattr(self, fieldtracker):
|
if hasattr(self, fieldtracker):
|
||||||
_GA(self, fieldtracker)(self, fieldname)
|
_GA(self, fieldtracker)(fieldname)
|
||||||
|
|
||||||
|
|
||||||
class WeakSharedMemoryModelBase(SharedMemoryModelBase):
|
class WeakSharedMemoryModelBase(SharedMemoryModelBase):
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue