Merge with develop and fix merge conflicts
This commit is contained in:
commit
72f4fedcbe
148 changed files with 20005 additions and 2718 deletions
|
|
@ -7,7 +7,6 @@ sets up all the networking features. (this is done automatically
|
|||
by evennia/server/server_runner.py).
|
||||
|
||||
"""
|
||||
|
||||
from builtins import object
|
||||
import time
|
||||
import sys
|
||||
|
|
@ -17,6 +16,7 @@ from twisted.web import static
|
|||
from twisted.application import internet, service
|
||||
from twisted.internet import reactor, defer
|
||||
from twisted.internet.task import LoopingCall
|
||||
from twisted.python.log import ILogObserver
|
||||
|
||||
import django
|
||||
django.setup()
|
||||
|
|
@ -33,6 +33,7 @@ from evennia.server.models import ServerConfig
|
|||
from evennia.server import initial_setup
|
||||
|
||||
from evennia.utils.utils import get_evennia_version, mod_import, make_iter
|
||||
from evennia.utils import logger
|
||||
from evennia.comms import channelhandler
|
||||
from evennia.server.sessionhandler import SESSIONS
|
||||
|
||||
|
|
@ -40,11 +41,6 @@ from django.utils.translation import ugettext as _
|
|||
|
||||
_SA = object.__setattr__
|
||||
|
||||
SERVER_PIDFILE = ""
|
||||
if os.name == 'nt':
|
||||
# For Windows we need to handle pid files manually.
|
||||
SERVER_PIDFILE = os.path.join(settings.GAME_DIR, "server", 'server.pid')
|
||||
|
||||
# a file with a flag telling the server to restart after shutdown or not.
|
||||
SERVER_RESTART = os.path.join(settings.GAME_DIR, "server", 'server.restart')
|
||||
|
||||
|
|
@ -53,16 +49,11 @@ SERVER_STARTSTOP_MODULE = mod_import(settings.AT_SERVER_STARTSTOP_MODULE)
|
|||
|
||||
# modules containing plugin services
|
||||
SERVER_SERVICES_PLUGIN_MODULES = [mod_import(module) for module in make_iter(settings.SERVER_SERVICES_PLUGIN_MODULES)]
|
||||
try:
|
||||
WEB_PLUGINS_MODULE = mod_import(settings.WEB_PLUGINS_MODULE)
|
||||
except ImportError:
|
||||
WEB_PLUGINS_MODULE = None
|
||||
print ("WARNING: settings.WEB_PLUGINS_MODULE not found - "
|
||||
"copy 'evennia/game_template/server/conf/web_plugins.py to mygame/server/conf.")
|
||||
|
||||
#------------------------------------------------------------
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Evennia Server settings
|
||||
#------------------------------------------------------------
|
||||
# ------------------------------------------------------------
|
||||
|
||||
SERVERNAME = settings.SERVERNAME
|
||||
VERSION = get_evennia_version()
|
||||
|
|
@ -83,6 +74,17 @@ IRC_ENABLED = settings.IRC_ENABLED
|
|||
RSS_ENABLED = settings.RSS_ENABLED
|
||||
WEBCLIENT_ENABLED = settings.WEBCLIENT_ENABLED
|
||||
|
||||
INFO_DICT = {"servername": SERVERNAME, "version": VERSION,
|
||||
"amp": "", "errors": "", "info": "", "webserver": "", "irc_rss": ""}
|
||||
|
||||
try:
|
||||
WEB_PLUGINS_MODULE = mod_import(settings.WEB_PLUGINS_MODULE)
|
||||
except ImportError:
|
||||
WEB_PLUGINS_MODULE = None
|
||||
INFO_DICT["errors"] = (
|
||||
"WARNING: settings.WEB_PLUGINS_MODULE not found - "
|
||||
"copy 'evennia/game_template/server/conf/web_plugins.py to mygame/server/conf.")
|
||||
|
||||
|
||||
# Maintenance function - this is called repeatedly by the server
|
||||
|
||||
|
|
@ -127,6 +129,10 @@ def _server_maintenance():
|
|||
if _MAINTENANCE_COUNT % 3700 == 0:
|
||||
# validate channels off-sync with scripts
|
||||
evennia.CHANNEL_HANDLER.update()
|
||||
if _MAINTENANCE_COUNT % (3600 * 7) == 0:
|
||||
# drop database connection every 7 hrs to avoid default timeouts on MySQL
|
||||
# (see https://github.com/evennia/evennia/issues/1376)
|
||||
connection.close()
|
||||
|
||||
# handle idle timeouts
|
||||
if _IDLE_TIMEOUT > 0:
|
||||
|
|
@ -137,11 +143,6 @@ def _server_maintenance():
|
|||
session.account.access(session.account, "noidletimeout", default=False):
|
||||
SESSIONS.disconnect(session, reason=reason)
|
||||
|
||||
# Commenting this out, it is probably not needed
|
||||
# with CONN_MAX_AGE set. Keeping it as a reminder
|
||||
# if database-gone-away errors appears again /Griatch
|
||||
# if _MAINTENANCE_COUNT % 18000 == 0:
|
||||
# connection.close()
|
||||
maintenance_task = LoopingCall(_server_maintenance)
|
||||
maintenance_task.start(60, now=True) # call every minute
|
||||
|
||||
|
|
@ -173,15 +174,13 @@ class Evennia(object):
|
|||
self.amp_protocol = None # set by amp factory
|
||||
self.sessions = SESSIONS
|
||||
self.sessions.server = self
|
||||
self.process_id = os.getpid()
|
||||
|
||||
# Database-specific startup optimizations.
|
||||
self.sqlite3_prep()
|
||||
|
||||
self.start_time = time.time()
|
||||
|
||||
# Run the initial setup if needed
|
||||
self.run_initial_setup()
|
||||
|
||||
# initialize channelhandler
|
||||
channelhandler.CHANNELHANDLER.update()
|
||||
|
||||
|
|
@ -193,18 +192,13 @@ class Evennia(object):
|
|||
from twisted.internet.defer import Deferred
|
||||
if hasattr(self, "web_root"):
|
||||
d = self.web_root.empty_threadpool()
|
||||
d.addCallback(lambda _: self.shutdown(_reactor_stopping=True))
|
||||
d.addCallback(lambda _: self.shutdown("reload", _reactor_stopping=True))
|
||||
else:
|
||||
d = Deferred(lambda _: self.shutdown(_reactor_stopping=True))
|
||||
d = Deferred(lambda _: self.shutdown("reload", _reactor_stopping=True))
|
||||
d.addCallback(lambda _: reactor.stop())
|
||||
reactor.callLater(1, d.callback, None)
|
||||
reactor.sigInt = _wrap_sigint_handler
|
||||
|
||||
self.game_running = True
|
||||
|
||||
# track the server time
|
||||
self.run_init_hooks()
|
||||
|
||||
# Server startup methods
|
||||
|
||||
def sqlite3_prep(self):
|
||||
|
|
@ -212,7 +206,8 @@ class Evennia(object):
|
|||
Optimize some SQLite stuff at startup since we
|
||||
can't save it to the database.
|
||||
"""
|
||||
if ((".".join(str(i) for i in django.VERSION) < "1.2" and settings.DATABASES.get('default', {}).get('ENGINE') == "sqlite3") or
|
||||
if ((".".join(str(i) for i in django.VERSION) < "1.2" and
|
||||
settings.DATABASES.get('default', {}).get('ENGINE') == "sqlite3") or
|
||||
(hasattr(settings, 'DATABASES') and
|
||||
settings.DATABASES.get("default", {}).get('ENGINE', None) ==
|
||||
'django.db.backends.sqlite3')):
|
||||
|
|
@ -230,6 +225,8 @@ class Evennia(object):
|
|||
typeclasses in the settings file and have them auto-update all
|
||||
already existing objects.
|
||||
"""
|
||||
global INFO_DICT
|
||||
|
||||
# setting names
|
||||
settings_names = ("CMDSET_CHARACTER", "CMDSET_ACCOUNT",
|
||||
"BASE_ACCOUNT_TYPECLASS", "BASE_OBJECT_TYPECLASS",
|
||||
|
|
@ -248,7 +245,7 @@ class Evennia(object):
|
|||
#from evennia.accounts.models import AccountDB
|
||||
for i, prev, curr in ((i, tup[0], tup[1]) for i, tup in enumerate(settings_compare) if i in mismatches):
|
||||
# update the database
|
||||
print(" %s:\n '%s' changed to '%s'. Updating unchanged entries in database ..." % (settings_names[i], prev, curr))
|
||||
INFO_DICT['info'] = " %s:\n '%s' changed to '%s'. Updating unchanged entries in database ..." % (settings_names[i], prev, curr)
|
||||
if i == 0:
|
||||
ObjectDB.objects.filter(db_cmdset_storage__exact=prev).update(db_cmdset_storage=curr)
|
||||
if i == 1:
|
||||
|
|
@ -274,33 +271,37 @@ class Evennia(object):
|
|||
|
||||
def run_initial_setup(self):
|
||||
"""
|
||||
This is triggered by the amp protocol when the connection
|
||||
to the portal has been established.
|
||||
This attempts to run the initial_setup script of the server.
|
||||
It returns if this is not the first time the server starts.
|
||||
Once finished the last_initial_setup_step is set to -1.
|
||||
"""
|
||||
global INFO_DICT
|
||||
last_initial_setup_step = ServerConfig.objects.conf('last_initial_setup_step')
|
||||
if not last_initial_setup_step:
|
||||
# None is only returned if the config does not exist,
|
||||
# i.e. this is an empty DB that needs populating.
|
||||
print(' Server started for the first time. Setting defaults.')
|
||||
INFO_DICT['info'] = ' Server started for the first time. Setting defaults.'
|
||||
initial_setup.handle_setup(0)
|
||||
print('-' * 50)
|
||||
elif int(last_initial_setup_step) >= 0:
|
||||
# a positive value means the setup crashed on one of its
|
||||
# modules and setup will resume from this step, retrying
|
||||
# the last failed module. When all are finished, the step
|
||||
# is set to -1 to show it does not need to be run again.
|
||||
print(' Resuming initial setup from step %(last)s.' %
|
||||
{'last': last_initial_setup_step})
|
||||
INFO_DICT['info'] = ' Resuming initial setup from step {last}.'.format(
|
||||
last=last_initial_setup_step)
|
||||
initial_setup.handle_setup(int(last_initial_setup_step))
|
||||
print('-' * 50)
|
||||
|
||||
def run_init_hooks(self):
|
||||
def run_init_hooks(self, mode):
|
||||
"""
|
||||
Called every server start
|
||||
Called by the amp client once receiving sync back from Portal
|
||||
|
||||
Args:
|
||||
mode (str): One of shutdown, reload or reset
|
||||
|
||||
"""
|
||||
from evennia.objects.models import ObjectDB
|
||||
#from evennia.accounts.models import AccountDB
|
||||
|
||||
# update eventual changed defaults
|
||||
self.update_defaults()
|
||||
|
|
@ -308,47 +309,24 @@ class Evennia(object):
|
|||
[o.at_init() for o in ObjectDB.get_all_cached_instances()]
|
||||
[p.at_init() for p in AccountDB.get_all_cached_instances()]
|
||||
|
||||
mode = self.getset_restart_mode()
|
||||
|
||||
# call correct server hook based on start file value
|
||||
if mode == 'reload':
|
||||
# True was the old reload flag, kept for compatibilty
|
||||
logger.log_msg("Server successfully reloaded.")
|
||||
self.at_server_reload_start()
|
||||
elif mode == 'reset':
|
||||
# only run hook, don't purge sessions
|
||||
self.at_server_cold_start()
|
||||
elif mode in ('reset', 'shutdown'):
|
||||
logger.log_msg("Evennia Server successfully restarted in 'reset' mode.")
|
||||
elif mode == 'shutdown':
|
||||
self.at_server_cold_start()
|
||||
# clear eventual lingering session storages
|
||||
ObjectDB.objects.clear_all_sessids()
|
||||
logger.log_msg("Evennia Server successfully started.")
|
||||
# always call this regardless of start type
|
||||
self.at_server_start()
|
||||
|
||||
def getset_restart_mode(self, mode=None):
|
||||
"""
|
||||
This manages the flag file that tells the runner if the server is
|
||||
reloading, resetting or shutting down.
|
||||
|
||||
Args:
|
||||
mode (string or None, optional): Valid values are
|
||||
'reload', 'reset', 'shutdown' and `None`. If mode is `None`,
|
||||
no change will be done to the flag file.
|
||||
Returns:
|
||||
mode (str): The currently active restart mode, either just
|
||||
set or previously set.
|
||||
|
||||
"""
|
||||
if mode is None:
|
||||
with open(SERVER_RESTART, 'r') as f:
|
||||
# mode is either shutdown, reset or reload
|
||||
mode = f.read()
|
||||
else:
|
||||
with open(SERVER_RESTART, 'w') as f:
|
||||
f.write(str(mode))
|
||||
return mode
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def shutdown(self, mode=None, _reactor_stopping=False):
|
||||
def shutdown(self, mode='reload', _reactor_stopping=False):
|
||||
"""
|
||||
Shuts down the server from inside it.
|
||||
|
||||
|
|
@ -359,7 +337,6 @@ class Evennia(object):
|
|||
at_shutdown hooks called but sessions will not
|
||||
be disconnected.
|
||||
'shutdown' - like reset, but server will not auto-restart.
|
||||
None - keep currently set flag from flag file.
|
||||
_reactor_stopping - this is set if server is stopped by a kill
|
||||
command OR this method was already called
|
||||
once - in both cases the reactor is
|
||||
|
|
@ -370,10 +347,7 @@ class Evennia(object):
|
|||
# once; we don't need to run the shutdown procedure again.
|
||||
defer.returnValue(None)
|
||||
|
||||
mode = self.getset_restart_mode(mode)
|
||||
|
||||
from evennia.objects.models import ObjectDB
|
||||
#from evennia.accounts.models import AccountDB
|
||||
from evennia.server.models import ServerConfig
|
||||
from evennia.utils import gametime as _GAMETIME_MODULE
|
||||
|
||||
|
|
@ -382,7 +356,8 @@ class Evennia(object):
|
|||
ServerConfig.objects.conf("server_restart_mode", "reload")
|
||||
yield [o.at_server_reload() for o in ObjectDB.get_all_cached_instances()]
|
||||
yield [p.at_server_reload() for p in AccountDB.get_all_cached_instances()]
|
||||
yield [(s.pause(manual_pause=False), s.at_server_reload()) for s in ScriptDB.get_all_cached_instances() if s.is_active]
|
||||
yield [(s.pause(manual_pause=False), s.at_server_reload())
|
||||
for s in ScriptDB.get_all_cached_instances() if s.is_active]
|
||||
yield self.sessions.all_sessions_portal_sync()
|
||||
self.at_server_reload_stop()
|
||||
# only save monitor state on reload, not on shutdown/reset
|
||||
|
|
@ -412,10 +387,6 @@ class Evennia(object):
|
|||
# always called, also for a reload
|
||||
self.at_server_stop()
|
||||
|
||||
if os.name == 'nt' and os.path.exists(SERVER_PIDFILE):
|
||||
# for Windows we need to remove pid files manually
|
||||
os.remove(SERVER_PIDFILE)
|
||||
|
||||
if hasattr(self, "web_root"): # not set very first start
|
||||
yield self.web_root.empty_threadpool()
|
||||
|
||||
|
|
@ -427,6 +398,10 @@ class Evennia(object):
|
|||
# we make sure the proper gametime is saved as late as possible
|
||||
ServerConfig.objects.conf("runtime", _GAMETIME_MODULE.runtime())
|
||||
|
||||
def get_info_dict(self):
|
||||
"Return the server info, for display."
|
||||
return INFO_DICT
|
||||
|
||||
# server start/stop hooks
|
||||
|
||||
def at_server_start(self):
|
||||
|
|
@ -452,13 +427,15 @@ class Evennia(object):
|
|||
if SERVER_STARTSTOP_MODULE:
|
||||
SERVER_STARTSTOP_MODULE.at_server_reload_start()
|
||||
|
||||
def at_post_portal_sync(self):
|
||||
def at_post_portal_sync(self, mode):
|
||||
"""
|
||||
This is called just after the portal has finished syncing back data to the server
|
||||
after reconnecting.
|
||||
|
||||
Args:
|
||||
mode (str): One of reload, reset or shutdown.
|
||||
|
||||
"""
|
||||
# one of reload, reset or shutdown
|
||||
mode = self.getset_restart_mode()
|
||||
|
||||
from evennia.scripts.monitorhandler import MONITOR_HANDLER
|
||||
MONITOR_HANDLER.restore(mode == 'reload')
|
||||
|
|
@ -530,13 +507,16 @@ ServerConfig.objects.conf("server_starting_mode", True)
|
|||
# what to execute from.
|
||||
application = service.Application('Evennia')
|
||||
|
||||
if "--nodaemon" not in sys.argv:
|
||||
# custom logging, but only if we are not running in interactive mode
|
||||
logfile = logger.WeeklyLogFile(os.path.basename(settings.SERVER_LOG_FILE),
|
||||
os.path.dirname(settings.SERVER_LOG_FILE))
|
||||
application.setComponent(ILogObserver, logger.ServerLogObserver(logfile).emit)
|
||||
|
||||
# The main evennia server program. This sets up the database
|
||||
# and is where we store all the other services.
|
||||
EVENNIA = Evennia(application)
|
||||
|
||||
print('-' * 50)
|
||||
print(' %(servername)s Server (%(version)s) started.' % {'servername': SERVERNAME, 'version': VERSION})
|
||||
|
||||
if AMP_ENABLED:
|
||||
|
||||
# The AMP protocol handles the communication between
|
||||
|
|
@ -546,20 +526,20 @@ if AMP_ENABLED:
|
|||
ifacestr = ""
|
||||
if AMP_INTERFACE != '127.0.0.1':
|
||||
ifacestr = "-%s" % AMP_INTERFACE
|
||||
print(' amp (to Portal)%s: %s (internal)' % (ifacestr, AMP_PORT))
|
||||
|
||||
from evennia.server import amp
|
||||
INFO_DICT["amp"] = 'amp %s: %s' % (ifacestr, AMP_PORT)
|
||||
|
||||
factory = amp.AmpServerFactory(EVENNIA)
|
||||
amp_service = internet.TCPServer(AMP_PORT, factory, interface=AMP_INTERFACE)
|
||||
amp_service.setName("EvenniaPortal")
|
||||
from evennia.server import amp_client
|
||||
|
||||
factory = amp_client.AMPClientFactory(EVENNIA)
|
||||
amp_service = internet.TCPClient(AMP_HOST, AMP_PORT, factory)
|
||||
amp_service.setName('ServerAMPClient')
|
||||
EVENNIA.services.addService(amp_service)
|
||||
|
||||
if WEBSERVER_ENABLED:
|
||||
|
||||
# Start a django-compatible webserver.
|
||||
|
||||
#from twisted.python import threadpool
|
||||
from evennia.server.webserver import DjangoWebRoot, WSGIWebServer, Website, LockableThreadPool
|
||||
|
||||
# start a thread pool and define the root url (/) as a wsgi resource
|
||||
|
|
@ -579,14 +559,16 @@ if WEBSERVER_ENABLED:
|
|||
web_root = WEB_PLUGINS_MODULE.at_webserver_root_creation(web_root)
|
||||
|
||||
web_site = Website(web_root, logPath=settings.HTTP_LOG_FILE)
|
||||
web_site.is_portal = False
|
||||
|
||||
INFO_DICT["webserver"] = ""
|
||||
for proxyport, serverport in WEBSERVER_PORTS:
|
||||
# create the webserver (we only need the port for this)
|
||||
webserver = WSGIWebServer(threads, serverport, web_site, interface='127.0.0.1')
|
||||
webserver.setName('EvenniaWebServer%s' % serverport)
|
||||
EVENNIA.services.addService(webserver)
|
||||
|
||||
print(" webserver: %s (internal)" % serverport)
|
||||
INFO_DICT["webserver"] += "webserver: %s" % serverport
|
||||
|
||||
ENABLED = []
|
||||
if IRC_ENABLED:
|
||||
|
|
@ -598,18 +580,11 @@ if RSS_ENABLED:
|
|||
ENABLED.append('rss')
|
||||
|
||||
if ENABLED:
|
||||
print(" " + ", ".join(ENABLED) + " enabled.")
|
||||
INFO_DICT["irc_rss"] = ", ".join(ENABLED) + " enabled."
|
||||
|
||||
for plugin_module in SERVER_SERVICES_PLUGIN_MODULES:
|
||||
# external plugin protocols
|
||||
plugin_module.start_plugin_services(EVENNIA)
|
||||
|
||||
print('-' * 50) # end of terminal output
|
||||
|
||||
# clear server startup mode
|
||||
ServerConfig.objects.conf("server_starting_mode", delete=True)
|
||||
|
||||
if os.name == 'nt':
|
||||
# Windows only: Set PID file manually
|
||||
with open(SERVER_PIDFILE, 'w') as f:
|
||||
f.write(str(os.getpid()))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue