Started removing the global maintenance scripts, but ran into strange AMP issues.

This commit is contained in:
Griatch 2015-03-06 15:59:58 +01:00
parent 17400ede6b
commit 71e9038c62
7 changed files with 112 additions and 195 deletions

View file

@ -540,71 +540,3 @@ class Store(DefaultScript):
"Setup the script"
self.key = "sys_storage"
self.desc = _("This is a generic storage container.")
class CheckSessions(DefaultScript):
"Check sessions regularly."
def at_script_creation(self):
"Setup the script"
self.key = "sys_session_check"
self.desc = _("Checks sessions so they are live.")
self.interval = 60 # repeat every 60 seconds
self.persistent = True
def at_repeat(self):
"called every 60 seconds"
global _SESSIONS
if not _SESSIONS:
from evennia.server.sessionhandler import SESSIONS as _SESSIONS
#print "session check!"
#print "ValidateSessions run"
_SESSIONS.validate_sessions()
_FLUSH_CACHE = None
_IDMAPPER_CACHE_MAX_MEMORY = settings.IDMAPPER_CACHE_MAXSIZE
class ValidateIdmapperCache(DefaultScript):
"""
Check memory use of idmapper cache
"""
def at_script_creation(self):
self.key = "sys_cache_validate"
self.desc = _("Restrains size of idmapper cache.")
self.interval = 61 * 5 # staggered compared to session check
self.persistent = True
def at_repeat(self):
"Called every ~5 mins"
global _FLUSH_CACHE
if not _FLUSH_CACHE:
from evennia.utils.idmapper.models import conditional_flush as _FLUSH_CACHE
_FLUSH_CACHE(_IDMAPPER_CACHE_MAX_MEMORY)
class ValidateScripts(DefaultScript):
"Check script validation regularly"
def at_script_creation(self):
"Setup the script"
self.key = "sys_scripts_validate"
self.desc = _("Validates all scripts regularly.")
self.interval = 3600 # validate every hour.
self.persistent = True
def at_repeat(self):
"called every hour"
#print "ValidateScripts run."
ScriptDB.objects.validate()
class ValidateChannelHandler(DefaultScript):
"Update the channelhandler to make sure it's in sync."
def at_script_creation(self):
"Setup the script"
self.key = "sys_channels_validate"
self.desc = _("Updates the channel handler")
self.interval = 3700 # validate a little later than ValidateScripts
self.persistent = True
def at_repeat(self):
"called every hour+"
#print "ValidateChannelHandler run."
channelhandler.CHANNELHANDLER.update()

View file

@ -130,39 +130,6 @@ def create_channels():
channel = create.create_channel(**channeldict)
channel.connect(goduser)
def create_system_scripts():
"""
Setup the system repeat scripts. They are automatically started
by the create_script function.
"""
from evennia.scripts import scripts
print " Creating and starting global scripts ..."
# check so that all sessions are alive.
script1 = create.create_script(scripts.CheckSessions)
# validate all scripts in script table.
script2 = create.create_script(scripts.ValidateScripts)
# update the channel handler to make sure it's in sync
script3 = create.create_script(scripts.ValidateChannelHandler)
# flush the idmapper cache
script4 = create.create_script(scripts.ValidateIdmapperCache)
if not script1 or not script2 or not script3 or not script4:
print " Error creating system scripts."
def start_game_time():
"""
This starts a persistent script that keeps track of the
in-game time (in whatever accelerated reference frame), but also
the total run time of the server as well as its current uptime
(the uptime can also be found directly from the server though).
"""
print " Starting in-game time ..."
from evennia.utils import gametime
gametime.init_gametime()
def at_initial_setup():
"""
@ -206,20 +173,15 @@ def handle_setup(last_step):
# this means we don't need to handle setup since
# it already ran sucessfully once.
return
elif last_step is None:
# config doesn't exist yet. First start of server
last_step = 0
# if None, set it to 0
last_step = last_step or 0
# setting up the list of functions to run
setup_queue = [
create_config_values,
create_objects,
create_channels,
create_system_scripts,
start_game_time,
at_initial_setup,
reset_server
]
setup_queue = [create_config_values,
create_objects,
create_channels,
at_initial_setup,
reset_server]
#print " Initial setup: %s steps." % (len(setup_queue))
@ -241,6 +203,7 @@ def handle_setup(last_step):
from evennia.comms.models import ChannelDB
ChannelDB.objects.all().delete()
raise
# save this step
ServerConfig.objects.conf("last_initial_setup_step", last_step + num + 1)
# We got through the entire list. Set last_step to -1 so we don't
# have to run this again.

View file

@ -8,11 +8,13 @@ by game/evennia.py).
"""
import time
import sys
import os
from twisted.application import internet, service
from twisted.internet import protocol, reactor
from twisted.internet.task import LoopingCall
from twisted.web import server
import django
django.setup()
@ -67,6 +69,28 @@ AMP_INTERFACE = settings.AMP_INTERFACE
AMP_ENABLED = AMP_HOST and AMP_PORT and AMP_INTERFACE
# Maintenance function - this is called repeatedly by the portal.
_IDLE_TIMEOUT = settings.IDLE_TIMEOUT
def _portal_maintenance():
"""
The maintenance function handles repeated checks and updates
that the server needs to do. It is called every minute.
"""
# check for idle sessions
now = time.time()
reason = "Idle timeout exceeded, disconnecting."
for session in [sess for sess in PORTAL_SESSIONS.sessions.values()
if (now - sess.cmd_last) > _IDLE_TIMEOUT]:
session.data_out(reason)
PORTAL_SESSIONS.disconnect(session)
if _IDLE_TIMEOUT > 0:
# only start the maintenance task if we care about idling.
_maintenance_task = LoopingCall(_portal_maintenance)
_maintenance_task.start(60) # called every minute
#------------------------------------------------------------
# Portal Service object
#------------------------------------------------------------

View file

@ -295,6 +295,7 @@ class PortalSessionHandler(SessionHandler):
serialized before passed on.
"""
self.cmd_last = time()
self.portal.amp_protocol.call_remote_MsgPortal2Server(session.sessid,
msg=text,
data=kwargs)

View file

@ -8,6 +8,7 @@ sessions etc.
"""
import re
from twisted.internet.task import LoopingCall
from twisted.conch.telnet import Telnet, StatefulTelnetProtocol, IAC, LINEMODE, GA, WILL, WONT, ECHO
from evennia.server.session import Session
from evennia.server.portal import ttype, mssp, telnet_oob, naws
@ -15,6 +16,8 @@ from evennia.server.portal.mccp import Mccp, mccp_compress, MCCP
from evennia.server.portal.mxp import Mxp, mxp_parse
from evennia.utils import utils, ansi, logger
NOP = chr(241)
_RE_N = re.compile(r"\{n$")
_RE_LEND = re.compile(r"\n$|\r$", re.MULTILINE)
@ -60,6 +63,11 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
from evennia.utils.utils import delay
delay(2, callback=self.handshake_done, retval=True)
# set up a keep-alive
self.keep_alive = LoopingCall(self._write, NOP)
self.keep_alive.start(30)
def handshake_done(self, force=False):
"""
This is called by all telnet extensions once they are finished.

View file

@ -14,6 +14,8 @@ import os
from twisted.web import server, static
from twisted.application import internet, service
from twisted.internet import reactor, defer
from twisted.internet.task import LoopingCall
import django
django.setup()
@ -72,6 +74,42 @@ RSS_ENABLED = settings.RSS_ENABLED
WEBCLIENT_ENABLED = settings.WEBCLIENT_ENABLED
# Maintenance function - this is called repeatedly by the server
_MAINTENANCE_COUNT = 0
_FLUSH_CACHE = None
_IDMAPPER_CACHE_MAXSIZE = settings.IDMAPPER_CACHE_MAXSIZE
def _server_maintenance():
"""
This maintenance function handles repeated checks and updates that
the server needs to do. It is called every 5 minutes.
"""
global EVENNIA, _MAINTENANCE_COUNT
global _FLUSH_CACHE
if not _FLUSH_CACHE:
from evennia.utils.idmapper.models import conditional_flush as _FLUSH_CACHE
_MAINTENANCE_COUNT += 1
# update game time
EVENNIA.runtime += 60.0
ServerConfig.objects.conf("runtime", EVENNIA.runtime)
EVENNIA.runtime_last_saved = time.time()
if _MAINTENANCE_COUNT % 300 == 0:
# check cache size every 5 minutes
print "maintenance: check flush cache..."
_FLUSH_CACHE(_IDMAPPER_CACHE_MAXSIZE)
if _MAINTENANCE_COUNT % 3600 == 0:
# validate scripts every hour
print "maintenance: validate scripts..."
evennia.ScriptDB.objects.validate()
if _MAINTENANCE_COUNT % 3700 == 0:
# validate channels off-sync with scripts
print "maintenance: validate channels..."
evennia.CHANNEL_HANDLER.update()
#------------------------------------------------------------
# Evennia Main Server object
#------------------------------------------------------------
@ -104,8 +142,6 @@ class Evennia(object):
# Run the initial setup if needed
self.run_initial_setup()
self.start_time = time.time()
# initialize channelhandler
channelhandler.CHANNELHANDLER.update()
@ -113,9 +149,13 @@ class Evennia(object):
# by Ctrl-C, reboot etc.
reactor.addSystemEventTrigger('before', 'shutdown',
self.shutdown, _reactor_stopping=True)
self.game_running = True
# track the server time
self.start_time = time.time()
self.runtime = ServerConfig.objects.conf("runtime", default=0.0)
self.runtime_last_saved = self.start_time
self.run_init_hooks()
# Server startup methods
@ -323,10 +363,6 @@ class Evennia(object):
self.at_server_cold_stop()
# stopping time
from evennia.utils import gametime
gametime.save()
self.at_server_stop()
# if _reactor_stopping is true, reactor does not need to
# be stopped again.
@ -486,6 +522,9 @@ ServerConfig.objects.conf("server_starting_mode", delete=True)
if os.name == 'nt':
# Windows only: Set PID file manually
f = open(os.path.join(settings.GAME_DIR, 'server.pid'), 'w')
f.write(str(os.getpid()))
f.close()
with open(os.path.join(settings.GAME_DIR, 'server.pid'), 'w') as f:
f.write(str(os.getpid()))
# start the maintenance task
maintenance_task = LoopingCall(_server_maintenance)
maintenance_task.start(60) # call every minute

View file

@ -8,10 +8,6 @@ total runtime of the server and the current uptime.
from time import time
from django.conf import settings
from evennia.scripts.scripts import DefaultScript
from evennia.utils.create import create_script
GAMETIME_SCRIPT_NAME = "sys_game_time"
# Speed-up factor of the in-game time compared
# to real time.
@ -35,67 +31,25 @@ WEEK = DAY * settings.TIME_DAY_PER_WEEK
MONTH = WEEK * settings.TIME_WEEK_PER_MONTH
YEAR = MONTH * settings.TIME_MONTH_PER_YEAR
# Cached time stamps
SERVER_STARTTIME = time()
SERVER_RUNTIME = 0.0
# link to the main Server
_EVENNIA = None
class GameTime(DefaultScript):
"""
This script repeatedly saves server times so
it can be retrieved after server downtime.
"""
def at_script_creation(self):
"""
Setup the script
"""
self.key = GAMETIME_SCRIPT_NAME
self.desc = "Saves uptime/runtime"
self.interval = 60
self.persistent = True
self.start_delay = True
self.attributes.add("run_time", 0.0) # OOC time
self.attributes.add("up_time", 0.0) # OOC time
def at_repeat(self):
"""
Called every minute to update the timers.
"""
self.attributes.add("run_time", runtime())
self.attributes.add("up_time", uptime())
def at_start(self):
"""
This is called once every server restart.
We reset the up time and load the relevant
times.
"""
global SERVER_RUNTIME
SERVER_RUNTIME = self.attributes.get("run_time")
def save():
"Force save of time. This is called by server when shutting down/reloading."
from evennia.scripts.models import ScriptDB
try:
script = ScriptDB.objects.get(db_key=GAMETIME_SCRIPT_NAME)
script.at_repeat()
except Exception:
from evennia.utils import logger
logger.log_trace()
def _format(seconds, *divisors) :
"""
Helper function. Creates a tuple of even dividends given
a range of divisors.
Helper function. Creates a tuple of even dividends given a range
of divisors.
Inputs
seconds - number of seconds to format
*divisors - a number of integer dividends. The number of seconds will be
integer-divided by the first number in this sequence, the remainder
will be divided with the second and so on.
Output:
A tuple of length len(*args)+1, with the last element being the last remaining
seconds not evenly divided by the supplied dividends.
Args:
seconds (int): Number of seconds to format
*divisors (int): a sequence of numbers of integer dividends. The
number of seconds will be integer-divided by the first number in
this sequence, the remainder will be divided with the second and
so on.
Returns:
time (tuple): This tuple has length len(*args)+1, with the
last element being the last remaining seconds not evenly
divided by the supplied dividends.
"""
results = []
@ -111,14 +65,20 @@ def _format(seconds, *divisors) :
def runtime(format=False):
"Get the total runtime of the server since first start (minus downtimes)"
runtime = SERVER_RUNTIME + (time() - SERVER_STARTTIME)
global _EVENNIA
if not _EVENNIA:
from evennia.server.server import EVENNIA as _EVENNIA
runtime = _EVENNIA.runtime + (time() - _EVENNIA.runtime_last_saved)
if format:
return _format(runtime, 31536000, 2628000, 604800, 86400, 3600, 60)
return runtime
def uptime(format=False):
"Get the current uptime of the server since last reload"
uptime = time() - SERVER_STARTTIME
global _EVENNIA
if not _EVENNIA:
from evennia.server.server import EVENNIA as _EVENNIA
uptime = time() - _EVENNIA.start_time
if format:
return _format(uptime, 31536000, 2628000, 604800, 86400, 3600, 60)
return uptime
@ -167,13 +127,3 @@ def realtime_to_gametime(secs=0, mins=0, hrs=0, days=0,
return _format(gametime, YEAR, MONTH, WEEK, DAY, HOUR, MIN)
return gametime
# Time administration routines
def init_gametime():
"""
This is called once, when the server starts for the very first time.
"""
# create the GameTime script and start it
game_time = create_script(GameTime)
game_time.start()