mirror of
https://github.com/evennia/evennia.git
synced 2026-04-02 22:17:17 +02:00
Merge with develop and fix merge conflicts
This commit is contained in:
commit
72f4fedcbe
148 changed files with 20005 additions and 2718 deletions
|
|
@ -29,8 +29,8 @@ class Migration(migrations.Migration):
|
|||
('db_persistent', models.BooleanField(default=False, verbose_name=b'survive server reboot')),
|
||||
('db_is_active', models.BooleanField(default=False, verbose_name=b'script active')),
|
||||
('db_attributes', models.ManyToManyField(help_text=b'attributes on this object. An attribute can hold any pickle-able python object (see docs for special cases).', to='typeclasses.Attribute', null=True)),
|
||||
('db_obj', models.ForeignKey(blank=True, to='objects.ObjectDB', help_text=b'the object to store this script on, if not a global script.', null=True, verbose_name=b'scripted object')),
|
||||
('db_account', models.ForeignKey(blank=True, to=settings.AUTH_USER_MODEL, help_text=b'the account to store this script on (should not be set if obj is set)', null=True, verbose_name=b'scripted account')),
|
||||
('db_obj', models.ForeignKey(blank=True, to='objects.ObjectDB', on_delete=models.CASCADE, help_text=b'the object to store this script on, if not a global script.', null=True, verbose_name=b'scripted object')),
|
||||
('db_account', models.ForeignKey(blank=True, to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE, help_text=b'the account to store this script on (should not be set if obj is set)', null=True, verbose_name=b'scripted account')),
|
||||
('db_tags', models.ManyToManyField(help_text=b'tags on this object. Tags are simple string markers to identify, group and alias objects.', to='typeclasses.Tag', null=True)),
|
||||
],
|
||||
options={
|
||||
|
|
|
|||
|
|
@ -83,9 +83,11 @@ class ScriptDB(TypedObject):
|
|||
# optional description.
|
||||
db_desc = models.CharField('desc', max_length=255, blank=True)
|
||||
# A reference to the database object affected by this Script, if any.
|
||||
db_obj = models.ForeignKey("objects.ObjectDB", null=True, blank=True, verbose_name='scripted object',
|
||||
db_obj = models.ForeignKey("objects.ObjectDB", null=True, blank=True, on_delete=models.CASCADE,
|
||||
verbose_name='scripted object',
|
||||
help_text='the object to store this script on, if not a global script.')
|
||||
db_account = models.ForeignKey("accounts.AccountDB", null=True, blank=True, verbose_name="scripted account",
|
||||
db_account = models.ForeignKey("accounts.AccountDB", null=True, blank=True,
|
||||
on_delete=models.CASCADE, verbose_name="scripted account",
|
||||
help_text='the account to store this script on (should not be set if db_obj is set)')
|
||||
|
||||
# how often to run Script (secs). -1 means there is no timer
|
||||
|
|
|
|||
|
|
@ -18,6 +18,17 @@ from future.utils import with_metaclass
|
|||
__all__ = ["DefaultScript", "DoNothing", "Store"]
|
||||
|
||||
|
||||
FLUSHING_INSTANCES = False # whether we're in the process of flushing scripts from the cache
|
||||
SCRIPT_FLUSH_TIMERS = {} # stores timers for scripts that are currently being flushed
|
||||
|
||||
|
||||
def restart_scripts_after_flush():
|
||||
"""After instances are flushed, validate scripts so they're not dead for a long period of time"""
|
||||
global FLUSHING_INSTANCES
|
||||
ScriptDB.objects.validate()
|
||||
FLUSHING_INSTANCES = False
|
||||
|
||||
|
||||
class ExtendedLoopingCall(LoopingCall):
|
||||
"""
|
||||
LoopingCall that can start at a delay different
|
||||
|
|
@ -141,15 +152,6 @@ class ScriptBase(with_metaclass(TypeclassBase, ScriptDB)):
|
|||
"""
|
||||
objects = ScriptManager()
|
||||
|
||||
|
||||
class DefaultScript(ScriptBase):
|
||||
"""
|
||||
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.
|
||||
|
||||
"""
|
||||
|
||||
def __eq__(self, other):
|
||||
"""
|
||||
Compares two Scripts. Compares dbids.
|
||||
|
|
@ -239,7 +241,96 @@ class DefaultScript(ScriptBase):
|
|||
logger.log_trace()
|
||||
return None
|
||||
|
||||
# Public methods
|
||||
def at_script_creation(self):
|
||||
"""
|
||||
Should be overridden in child.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
def at_first_save(self, **kwargs):
|
||||
"""
|
||||
This is called after very first time this object is saved.
|
||||
Generally, you don't need to overload this, but only the hooks
|
||||
called by this method.
|
||||
|
||||
Args:
|
||||
**kwargs (dict): Arbitrary, optional arguments for users
|
||||
overriding the call (unused by default).
|
||||
|
||||
"""
|
||||
self.at_script_creation()
|
||||
|
||||
if hasattr(self, "_createdict"):
|
||||
# this will only be set if the utils.create_script
|
||||
# function was used to create the object. We want
|
||||
# the create call's kwargs to override the values
|
||||
# set by hooks.
|
||||
cdict = self._createdict
|
||||
updates = []
|
||||
if not cdict.get("key"):
|
||||
if not self.db_key:
|
||||
self.db_key = "#%i" % self.dbid
|
||||
updates.append("db_key")
|
||||
elif self.db_key != cdict["key"]:
|
||||
self.db_key = cdict["key"]
|
||||
updates.append("db_key")
|
||||
if cdict.get("interval") and self.interval != cdict["interval"]:
|
||||
self.db_interval = cdict["interval"]
|
||||
updates.append("db_interval")
|
||||
if cdict.get("start_delay") and self.start_delay != cdict["start_delay"]:
|
||||
self.db_start_delay = cdict["start_delay"]
|
||||
updates.append("db_start_delay")
|
||||
if cdict.get("repeats") and self.repeats != cdict["repeats"]:
|
||||
self.db_repeats = cdict["repeats"]
|
||||
updates.append("db_repeats")
|
||||
if cdict.get("persistent") and self.persistent != cdict["persistent"]:
|
||||
self.db_persistent = cdict["persistent"]
|
||||
updates.append("db_persistent")
|
||||
if cdict.get("desc") and self.desc != cdict["desc"]:
|
||||
self.db_desc = cdict["desc"]
|
||||
updates.append("db_desc")
|
||||
if updates:
|
||||
self.save(update_fields=updates)
|
||||
|
||||
if cdict.get("permissions"):
|
||||
self.permissions.batch_add(*cdict["permissions"])
|
||||
if cdict.get("locks"):
|
||||
self.locks.add(cdict["locks"])
|
||||
if cdict.get("tags"):
|
||||
# this should be a list of tags, tuples (key, category) or (key, category, data)
|
||||
self.tags.batch_add(*cdict["tags"])
|
||||
if cdict.get("attributes"):
|
||||
# this should be tuples (key, val, ...)
|
||||
self.attributes.batch_add(*cdict["attributes"])
|
||||
if cdict.get("nattributes"):
|
||||
# this should be a dict of nattrname:value
|
||||
for key, value in cdict["nattributes"]:
|
||||
self.nattributes.add(key, value)
|
||||
|
||||
if not cdict.get("autostart"):
|
||||
# don't auto-start the script
|
||||
return
|
||||
|
||||
# auto-start script (default)
|
||||
self.start()
|
||||
|
||||
|
||||
class DefaultScript(ScriptBase):
|
||||
"""
|
||||
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.
|
||||
|
||||
"""
|
||||
|
||||
def at_script_creation(self):
|
||||
"""
|
||||
Only called once, when script is first created.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def time_until_next_repeat(self):
|
||||
"""
|
||||
|
|
@ -278,6 +369,27 @@ class DefaultScript(ScriptBase):
|
|||
return max(0, self.db_repeats - task.callcount)
|
||||
return None
|
||||
|
||||
def at_idmapper_flush(self):
|
||||
"""If we're flushing this object, make sure the LoopingCall is gone too"""
|
||||
ret = super(DefaultScript, self).at_idmapper_flush()
|
||||
if ret and self.ndb._task:
|
||||
try:
|
||||
from twisted.internet import reactor
|
||||
global FLUSHING_INSTANCES
|
||||
# store the current timers for the _task and stop it to avoid duplicates after cache flush
|
||||
paused_time = self.ndb._task.next_call_time()
|
||||
callcount = self.ndb._task.callcount
|
||||
self._stop_task()
|
||||
SCRIPT_FLUSH_TIMERS[self.id] = (paused_time, callcount)
|
||||
# here we ensure that the restart call only happens once, not once per script
|
||||
if not FLUSHING_INSTANCES:
|
||||
FLUSHING_INSTANCES = True
|
||||
reactor.callLater(2, restart_scripts_after_flush)
|
||||
except Exception:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return ret
|
||||
|
||||
def start(self, force_restart=False):
|
||||
"""
|
||||
Called every time the script is started (for persistent
|
||||
|
|
@ -294,9 +406,19 @@ class DefaultScript(ScriptBase):
|
|||
started or not. Used in counting.
|
||||
|
||||
"""
|
||||
|
||||
if self.is_active and not force_restart:
|
||||
# script already runs and should not be restarted.
|
||||
# The script is already running, but make sure we have a _task if this is after a cache flush
|
||||
if not self.ndb._task and self.db_interval >= 0:
|
||||
self.ndb._task = ExtendedLoopingCall(self._step_task)
|
||||
try:
|
||||
start_delay, callcount = SCRIPT_FLUSH_TIMERS[self.id]
|
||||
del SCRIPT_FLUSH_TIMERS[self.id]
|
||||
now = False
|
||||
except (KeyError, ValueError, TypeError):
|
||||
now = not self.db_start_delay
|
||||
start_delay = None
|
||||
callcount = 0
|
||||
self.ndb._task.start(self.db_interval, now=now, start_delay=start_delay, count_start=callcount)
|
||||
return 0
|
||||
|
||||
obj = self.obj
|
||||
|
|
@ -472,61 +594,6 @@ class DefaultScript(ScriptBase):
|
|||
if task:
|
||||
task.force_repeat()
|
||||
|
||||
def at_first_save(self, **kwargs):
|
||||
"""
|
||||
This is called after very first time this object is saved.
|
||||
Generally, you don't need to overload this, but only the hooks
|
||||
called by this method.
|
||||
|
||||
Args:
|
||||
**kwargs (dict): Arbitrary, optional arguments for users
|
||||
overriding the call (unused by default).
|
||||
|
||||
"""
|
||||
self.at_script_creation()
|
||||
|
||||
if hasattr(self, "_createdict"):
|
||||
# this will only be set if the utils.create_script
|
||||
# function was used to create the object. We want
|
||||
# the create call's kwargs to override the values
|
||||
# set by hooks.
|
||||
cdict = self._createdict
|
||||
updates = []
|
||||
if not cdict.get("key"):
|
||||
if not self.db_key:
|
||||
self.db_key = "#%i" % self.dbid
|
||||
updates.append("db_key")
|
||||
elif self.db_key != cdict["key"]:
|
||||
self.db_key = cdict["key"]
|
||||
updates.append("db_key")
|
||||
if cdict.get("interval") and self.interval != cdict["interval"]:
|
||||
self.db_interval = cdict["interval"]
|
||||
updates.append("db_interval")
|
||||
if cdict.get("start_delay") and self.start_delay != cdict["start_delay"]:
|
||||
self.db_start_delay = cdict["start_delay"]
|
||||
updates.append("db_start_delay")
|
||||
if cdict.get("repeats") and self.repeats != cdict["repeats"]:
|
||||
self.db_repeats = cdict["repeats"]
|
||||
updates.append("db_repeats")
|
||||
if cdict.get("persistent") and self.persistent != cdict["persistent"]:
|
||||
self.db_persistent = cdict["persistent"]
|
||||
updates.append("db_persistent")
|
||||
if updates:
|
||||
self.save(update_fields=updates)
|
||||
if not cdict.get("autostart"):
|
||||
# don't auto-start the script
|
||||
return
|
||||
|
||||
# auto-start script (default)
|
||||
self.start()
|
||||
|
||||
def at_script_creation(self):
|
||||
"""
|
||||
Only called once, by the create function.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
def is_valid(self):
|
||||
"""
|
||||
Is called to check if the script is valid to run at this time.
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ Module containing the task handler for Evennia deferred tasks, persistent or not
|
|||
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from twisted.internet import reactor, task
|
||||
from twisted.internet import reactor
|
||||
from twisted.internet.task import deferLater
|
||||
from evennia.server.models import ServerConfig
|
||||
from evennia.utils.logger import log_err
|
||||
from evennia.utils.dbserialize import dbserialize, dbunserialize
|
||||
|
|
@ -143,7 +144,7 @@ class TaskHandler(object):
|
|||
args = [task_id]
|
||||
kwargs = {}
|
||||
|
||||
return task.deferLater(reactor, timedelay, callback, *args, **kwargs)
|
||||
return deferLater(reactor, timedelay, callback, *args, **kwargs)
|
||||
|
||||
def remove(self, task_id):
|
||||
"""Remove a persistent task without executing it.
|
||||
|
|
@ -189,7 +190,7 @@ class TaskHandler(object):
|
|||
now = datetime.now()
|
||||
for task_id, (date, callbac, args, kwargs) in self.tasks.items():
|
||||
seconds = max(0, (date - now).total_seconds())
|
||||
task.deferLater(reactor, seconds, self.do_task, task_id)
|
||||
deferLater(reactor, seconds, self.do_task, task_id)
|
||||
|
||||
|
||||
# Create the soft singleton
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue