mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
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"])
|
||||
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,
|
||||
no matter how. It checks for a variable _safe_contents_update to
|
||||
know if the save was triggered via the proper handler or not.
|
||||
|
||||
Since we cannot know at this point was old_location was, we
|
||||
trigger a full-on contents_cache update here.
|
||||
This is called automatically after the location field was
|
||||
saved, no matter how. It checks for a variable
|
||||
_safe_contents_update to know if the save was triggered via
|
||||
the location handler (which updates the contents cache) or
|
||||
not.
|
||||
|
||||
"""
|
||||
if not hasattr(self, "_safe_contents_update"):
|
||||
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()]
|
||||
# changed/set outside of the location handler
|
||||
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:
|
||||
"Define Django meta options"
|
||||
|
|
|
|||
|
|
@ -53,18 +53,22 @@ if not _OOB_ERROR:
|
|||
class OOBFieldMonitor(object):
|
||||
"""
|
||||
This object should be stored on the
|
||||
tracked object as "_oob_at_<fieldname>_update".
|
||||
the update() method will be called by the
|
||||
tracked object as "_oob_at_<fieldname>_postsave".
|
||||
the update() method w ill be called by the
|
||||
save mechanism, which in turn will call the
|
||||
user-customizable func()
|
||||
"""
|
||||
def __init__(self):
|
||||
def __init__(self, obj):
|
||||
"""
|
||||
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)
|
||||
|
||||
def __call__(self, obj, fieldname):
|
||||
def __call__(self, fieldname):
|
||||
"""
|
||||
Called by the save() mechanism when the given
|
||||
field has updated.
|
||||
|
|
@ -74,7 +78,7 @@ class OOBFieldMonitor(object):
|
|||
# a potential list of oob commands to call when this
|
||||
# field changes.
|
||||
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):
|
||||
"""
|
||||
|
|
@ -156,7 +160,7 @@ class OOBHandler(TickerHandler):
|
|||
fieldmonitorname = self._get_fieldmonitor_name(fieldname)
|
||||
if not hasattr(obj, fieldmonitorname):
|
||||
# assign a new fieldmonitor to the object
|
||||
_SA(obj, fieldmonitorname, OOBFieldMonitor())
|
||||
_SA(obj, fieldmonitorname, OOBFieldMonitor(obj))
|
||||
# register the session with the monitor
|
||||
_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)
|
||||
"""
|
||||
|
||||
import time
|
||||
from time import time
|
||||
from datetime import datetime
|
||||
from django.conf import settings
|
||||
from evennia.comms.models import ChannelDB
|
||||
|
|
@ -48,6 +48,7 @@ class ServerSession(Session):
|
|||
self.player = None
|
||||
self.cmdset_storage_string = ""
|
||||
self.cmdset = CmdSetHandler(self, True)
|
||||
self.cmd_per_second = 0.0
|
||||
|
||||
def __cmdset_storage_get(self):
|
||||
return [path.strip() for path in self.cmdset_storage_string.split(',')]
|
||||
|
|
@ -98,7 +99,7 @@ class ServerSession(Session):
|
|||
self.uid = self.player.id
|
||||
self.uname = self.player.username
|
||||
self.logged_in = True
|
||||
self.conn_time = time.time()
|
||||
self.conn_time = time()
|
||||
self.puid = None
|
||||
self.puppet = None
|
||||
self.cmdset_storage = settings.CMDSET_SESSION
|
||||
|
|
@ -184,12 +185,11 @@ class ServerSession(Session):
|
|||
and command counters.
|
||||
"""
|
||||
# Store the timestamp of the user's last command.
|
||||
self.cmd_last = time.time()
|
||||
if not idle:
|
||||
# Increment the user's command counter.
|
||||
self.cmd_total += 1
|
||||
# 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):
|
||||
"""
|
||||
|
|
@ -200,6 +200,10 @@ class ServerSession(Session):
|
|||
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
|
||||
#also valid
|
||||
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 evennia.commands.cmdhandler import CMD_LOGINSTART
|
||||
from evennia.utils.utils import variable_from_module, is_iter, \
|
||||
to_str, to_unicode, strip_control_sequences, make_iter
|
||||
from evennia.utils import logger
|
||||
|
||||
try:
|
||||
import cPickle as pickle
|
||||
|
|
@ -46,11 +47,15 @@ PCONNSYNC = chr(10) # portal post-syncing session
|
|||
# i18n
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
SERVERNAME = settings.SERVERNAME
|
||||
_SERVERNAME = settings.SERVERNAME
|
||||
_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():
|
||||
"Helper method for delayed import of all needed entities"
|
||||
global _ServerSession, _PlayerDB, _ServerConfig, _ScriptDB
|
||||
|
|
@ -130,7 +135,9 @@ class ServerSessionHandler(SessionHandler):
|
|||
"""
|
||||
self.sessions = {}
|
||||
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):
|
||||
"""
|
||||
|
|
@ -359,11 +366,11 @@ class ServerSessionHandler(SessionHandler):
|
|||
Check all currently connected sessions (logged in and not)
|
||||
and see if any are dead or idle
|
||||
"""
|
||||
tcurr = time.time()
|
||||
tcurr = time()
|
||||
reason = _("Idle timeout exceeded, disconnecting.")
|
||||
for session in (session for session in self.sessions.values()
|
||||
if session.logged_in and IDLE_TIMEOUT > 0
|
||||
and (tcurr - session.cmd_last) > IDLE_TIMEOUT):
|
||||
if session.logged_in and _IDLE_TIMEOUT > 0
|
||||
and (tcurr - session.cmd_last) > _IDLE_TIMEOUT):
|
||||
self.disconnect(session, reason=reason)
|
||||
|
||||
def player_count(self, count=True):
|
||||
|
|
@ -493,6 +500,17 @@ class ServerSessionHandler(SessionHandler):
|
|||
"""
|
||||
session = self.sessions.get(sessid, None)
|
||||
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)
|
||||
if "oob" in kwargs:
|
||||
# incoming data is always on the form (cmdname, args, kwargs)
|
||||
|
|
|
|||
|
|
@ -329,7 +329,17 @@ class SharedMemoryModel(Model):
|
|||
super(SharedMemoryModel, self).delete(*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:
|
||||
# we keep a store of objects modified in subprocesses so
|
||||
|
|
@ -348,24 +358,26 @@ class SharedMemoryModel(Model):
|
|||
callFromThread(_save_callback, self, *args, **kwargs)
|
||||
|
||||
# update field-update hooks and eventual OOB watchers
|
||||
new = False
|
||||
if "update_fields" in kwargs and kwargs["update_fields"]:
|
||||
# get field objects from their names
|
||||
update_fields = (self._meta.get_field_by_name(field)[0]
|
||||
for field in kwargs.get("update_fields"))
|
||||
else:
|
||||
# meta.fields are already field objects; get them all
|
||||
new =True
|
||||
update_fields = self._meta.fields
|
||||
for field in update_fields:
|
||||
fieldname = field.name
|
||||
# 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)):
|
||||
_GA(self, hookname)()
|
||||
_GA(self, hookname)(new)
|
||||
# if a trackerhandler is set on this object, update it with the
|
||||
# fieldname and the new value
|
||||
fieldtracker = "_oob_at_%s_postsave" % fieldname
|
||||
if hasattr(self, fieldtracker):
|
||||
_GA(self, fieldtracker)(self, fieldname)
|
||||
_GA(self, fieldtracker)(fieldname)
|
||||
|
||||
|
||||
class WeakSharedMemoryModelBase(SharedMemoryModelBase):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue