2010-08-29 18:46:58 +00:00
|
|
|
"""
|
2010-12-07 02:34:59 +00:00
|
|
|
This module implements the main Evennia server process, the core of
|
|
|
|
|
the game engine. Only import this once!
|
|
|
|
|
|
|
|
|
|
This module should be started with the 'twistd' executable since it
|
|
|
|
|
sets up all the networking features. (this is done by
|
|
|
|
|
game/evennia.py).
|
|
|
|
|
|
2010-08-29 18:46:58 +00:00
|
|
|
"""
|
|
|
|
|
import time
|
|
|
|
|
import sys
|
|
|
|
|
import os
|
|
|
|
|
if os.name == 'nt':
|
|
|
|
|
# For Windows batchfile we need an extra path insertion here.
|
|
|
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(
|
|
|
|
|
os.path.dirname(os.path.abspath(__file__)))))
|
|
|
|
|
|
|
|
|
|
from twisted.application import internet, service
|
2010-12-12 10:54:33 +00:00
|
|
|
from twisted.internet import protocol, reactor, defer
|
2010-12-07 02:34:59 +00:00
|
|
|
from twisted.web import server, static
|
2010-08-29 18:46:58 +00:00
|
|
|
from django.db import connection
|
|
|
|
|
from django.conf import settings
|
2010-12-07 02:34:59 +00:00
|
|
|
from src.utils import reloads
|
2011-04-12 21:43:57 +00:00
|
|
|
from src.server.models import ServerConfig
|
2010-12-11 13:37:26 +00:00
|
|
|
from src.server.sessionhandler import SESSIONS
|
2010-08-29 18:46:58 +00:00
|
|
|
from src.server import initial_setup
|
2010-12-07 02:34:59 +00:00
|
|
|
|
2010-08-29 18:46:58 +00:00
|
|
|
from src.utils.utils import get_evennia_version
|
|
|
|
|
from src.comms import channelhandler
|
|
|
|
|
|
2010-12-07 02:34:59 +00:00
|
|
|
|
|
|
|
|
#------------------------------------------------------------
|
|
|
|
|
# Evennia Server settings
|
|
|
|
|
#------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
SERVERNAME = settings.SERVERNAME
|
|
|
|
|
VERSION = get_evennia_version()
|
|
|
|
|
|
|
|
|
|
TELNET_PORTS = settings.TELNET_PORTS
|
|
|
|
|
WEBSERVER_PORTS = settings.WEBSERVER_PORTS
|
|
|
|
|
|
|
|
|
|
TELNET_ENABLED = settings.TELNET_ENABLED and TELNET_PORTS
|
|
|
|
|
WEBSERVER_ENABLED = settings.WEBSERVER_ENABLED and WEBSERVER_PORTS
|
|
|
|
|
WEBCLIENT_ENABLED = settings.WEBCLIENT_ENABLED
|
|
|
|
|
IMC2_ENABLED = settings.IMC2_ENABLED
|
|
|
|
|
IRC_ENABLED = settings.IRC_ENABLED
|
|
|
|
|
|
|
|
|
|
#------------------------------------------------------------
|
|
|
|
|
# Evennia Main Server object
|
|
|
|
|
#------------------------------------------------------------
|
|
|
|
|
class Evennia(object):
|
|
|
|
|
|
2010-08-29 18:46:58 +00:00
|
|
|
"""
|
2010-12-07 02:34:59 +00:00
|
|
|
The main Evennia server handler. This object sets up the database and
|
|
|
|
|
tracks and interlinks all the twisted network services that make up
|
|
|
|
|
evennia.
|
2010-08-29 18:46:58 +00:00
|
|
|
"""
|
2010-12-07 02:34:59 +00:00
|
|
|
|
|
|
|
|
def __init__(self, application):
|
|
|
|
|
"""
|
|
|
|
|
Setup the server.
|
2010-08-29 18:46:58 +00:00
|
|
|
|
2010-12-07 02:34:59 +00:00
|
|
|
application - an instantiated Twisted application
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
sys.path.append('.')
|
2010-08-29 18:46:58 +00:00
|
|
|
|
2010-12-07 02:34:59 +00:00
|
|
|
# create a store of services
|
|
|
|
|
self.services = service.IServiceCollection(application)
|
2010-08-29 18:46:58 +00:00
|
|
|
|
2010-12-07 02:34:59 +00:00
|
|
|
print '\n' + '-'*50
|
2010-08-29 18:46:58 +00:00
|
|
|
|
2010-12-07 02:34:59 +00:00
|
|
|
# Database-specific startup optimizations.
|
|
|
|
|
self.sqlite3_prep()
|
|
|
|
|
|
|
|
|
|
# Run the initial setup if needed
|
|
|
|
|
self.run_initial_setup()
|
2010-08-29 18:46:58 +00:00
|
|
|
|
|
|
|
|
# we have to null this here.
|
2010-12-11 13:37:26 +00:00
|
|
|
SESSIONS.session_count(0)
|
2011-02-21 12:56:44 +00:00
|
|
|
# we link ourself to the sessionhandler so other modules don't have to
|
|
|
|
|
# re-import the server module itself (which would re-initialize it).
|
|
|
|
|
SESSIONS.server = self
|
2010-08-29 18:46:58 +00:00
|
|
|
|
|
|
|
|
self.start_time = time.time()
|
|
|
|
|
|
|
|
|
|
# initialize channelhandler
|
|
|
|
|
channelhandler.CHANNELHANDLER.update()
|
2010-12-07 02:34:59 +00:00
|
|
|
|
2010-08-29 18:46:58 +00:00
|
|
|
# init all global scripts
|
|
|
|
|
reloads.reload_scripts(init_mode=True)
|
|
|
|
|
|
2010-12-07 02:34:59 +00:00
|
|
|
# Make info output to the terminal.
|
|
|
|
|
self.terminal_output()
|
2010-08-29 18:46:58 +00:00
|
|
|
|
2010-12-07 02:34:59 +00:00
|
|
|
print '-'*50
|
|
|
|
|
|
2010-12-12 10:54:33 +00:00
|
|
|
# set a callback if the server is killed abruptly,
|
|
|
|
|
# by Ctrl-C, reboot etc.
|
|
|
|
|
reactor.addSystemEventTrigger('before', 'shutdown',self.shutdown, _abrupt=True)
|
|
|
|
|
|
2010-12-07 02:34:59 +00:00
|
|
|
self.game_running = True
|
2010-08-29 18:46:58 +00:00
|
|
|
|
|
|
|
|
# Server startup methods
|
|
|
|
|
|
|
|
|
|
def sqlite3_prep(self):
|
|
|
|
|
"""
|
|
|
|
|
Optimize some SQLite stuff at startup since we
|
|
|
|
|
can't save it to the database.
|
|
|
|
|
"""
|
2010-12-07 02:34:59 +00:00
|
|
|
if (settings.DATABASE_ENGINE == "sqlite3"
|
|
|
|
|
or hasattr(settings, 'DATABASE')
|
|
|
|
|
and settings.DATABASE.get('ENGINE', None)
|
|
|
|
|
== 'django.db.backends.sqlite3'):
|
|
|
|
|
cursor = connection.cursor()
|
|
|
|
|
cursor.execute("PRAGMA cache_size=10000")
|
|
|
|
|
cursor.execute("PRAGMA synchronous=OFF")
|
|
|
|
|
cursor.execute("PRAGMA count_changes=OFF")
|
|
|
|
|
cursor.execute("PRAGMA temp_store=2")
|
|
|
|
|
|
|
|
|
|
def run_initial_setup(self):
|
|
|
|
|
"""
|
|
|
|
|
This attempts to run the initial_setup script of the server.
|
|
|
|
|
It returns if this is not the first time the server starts.
|
|
|
|
|
"""
|
2011-04-12 21:43:57 +00:00
|
|
|
last_initial_setup_step = ServerConfig.objects.conf('last_initial_setup_step')
|
2010-12-07 02:34:59 +00:00
|
|
|
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.'
|
|
|
|
|
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 %s.' % \
|
|
|
|
|
last_initial_setup_step
|
|
|
|
|
initial_setup.handle_setup(int(last_initial_setup_step))
|
|
|
|
|
print '-'*50
|
2010-08-29 18:46:58 +00:00
|
|
|
|
2010-12-07 02:34:59 +00:00
|
|
|
def terminal_output(self):
|
|
|
|
|
"""
|
|
|
|
|
Outputs server startup info to the terminal.
|
|
|
|
|
"""
|
|
|
|
|
print ' %s (%s) started on port(s):' % (SERVERNAME, VERSION)
|
|
|
|
|
if TELNET_ENABLED:
|
|
|
|
|
print " telnet: " + ", ".join([str(port) for port in TELNET_PORTS])
|
|
|
|
|
if WEBSERVER_ENABLED:
|
|
|
|
|
clientstring = ""
|
|
|
|
|
if WEBCLIENT_ENABLED:
|
|
|
|
|
clientstring = '/client'
|
|
|
|
|
print " webserver%s: " % clientstring + ", ".join([str(port) for port in WEBSERVER_PORTS])
|
2010-12-12 10:54:33 +00:00
|
|
|
|
|
|
|
|
def shutdown(self, message="{rThe server has been shutdown. Disconnecting.{n", _abrupt=False):
|
2010-08-29 18:46:58 +00:00
|
|
|
"""
|
2010-12-12 10:54:33 +00:00
|
|
|
If called directly, this disconnects everyone cleanly and shuts down the
|
|
|
|
|
reactor. If the server is killed by other means (Ctrl-C, reboot etc), this
|
|
|
|
|
might be called as a callback, at which point the reactor is already dead
|
|
|
|
|
and should not be tried to stop again (_abrupt=True).
|
|
|
|
|
|
|
|
|
|
message - message to send to all connected sessions
|
|
|
|
|
_abrupt - only to be used by internal callback_mechanism.
|
2010-08-29 18:46:58 +00:00
|
|
|
"""
|
2010-12-11 13:37:26 +00:00
|
|
|
SESSIONS.disconnect_all_sessions(reason=message)
|
2010-12-12 10:54:33 +00:00
|
|
|
if not _abrupt:
|
|
|
|
|
reactor.callLater(0, reactor.stop)
|
|
|
|
|
|
2010-08-29 18:46:58 +00:00
|
|
|
|
2010-12-07 02:34:59 +00:00
|
|
|
#------------------------------------------------------------
|
|
|
|
|
#
|
|
|
|
|
# Start the Evennia game server and add all active services
|
|
|
|
|
#
|
|
|
|
|
#------------------------------------------------------------
|
|
|
|
|
|
2011-04-16 22:26:22 +00:00
|
|
|
# Tell the system the server is starting up; some things are not available yet
|
|
|
|
|
ServerConfig.objects.conf("server_starting_mode", True)
|
|
|
|
|
|
2010-12-07 02:34:59 +00:00
|
|
|
# twistd requires us to define the variable 'application' so it knows
|
|
|
|
|
# what to execute from.
|
|
|
|
|
application = service.Application('Evennia')
|
|
|
|
|
|
|
|
|
|
# The main evennia server program. This sets up the database
|
|
|
|
|
# and is where we store all the other services.
|
|
|
|
|
EVENNIA = Evennia(application)
|
|
|
|
|
|
|
|
|
|
# We group all the various services under the same twisted app.
|
|
|
|
|
# These will gradually be started as they are initialized below.
|
|
|
|
|
|
|
|
|
|
if TELNET_ENABLED:
|
|
|
|
|
|
|
|
|
|
# start telnet game connections
|
|
|
|
|
|
|
|
|
|
from src.server import telnet
|
|
|
|
|
|
|
|
|
|
for port in TELNET_PORTS:
|
|
|
|
|
factory = protocol.ServerFactory()
|
|
|
|
|
factory.protocol = telnet.TelnetProtocol
|
|
|
|
|
telnet_service = internet.TCPServer(port, factory)
|
2011-04-10 12:39:07 +00:00
|
|
|
telnet_service.setName('EvenniaTelnet%s' % port)
|
2010-12-07 02:34:59 +00:00
|
|
|
EVENNIA.services.addService(telnet_service)
|
|
|
|
|
|
|
|
|
|
if WEBSERVER_ENABLED:
|
|
|
|
|
|
|
|
|
|
# a django-compatible webserver.
|
|
|
|
|
|
2011-02-27 22:27:56 +00:00
|
|
|
from twisted.python import threadpool
|
|
|
|
|
from src.server.webserver import DjangoWebRoot, WSGIWebServer
|
2010-12-07 02:34:59 +00:00
|
|
|
|
2011-02-23 21:08:02 +00:00
|
|
|
# start a thread pool and define the root url (/) as a wsgi resource
|
|
|
|
|
# recognized by Django
|
|
|
|
|
threads = threadpool.ThreadPool()
|
|
|
|
|
web_root = DjangoWebRoot(threads)
|
2010-12-07 02:34:59 +00:00
|
|
|
# point our media resources to url /media
|
2011-02-23 21:08:02 +00:00
|
|
|
web_root.putChild("media", static.File(settings.MEDIA_ROOT))
|
2010-12-07 02:34:59 +00:00
|
|
|
|
|
|
|
|
if WEBCLIENT_ENABLED:
|
|
|
|
|
# create ajax client processes at /webclientdata
|
2010-12-12 10:54:33 +00:00
|
|
|
from src.server.webclient import WebClient
|
2010-12-07 02:34:59 +00:00
|
|
|
web_root.putChild("webclientdata", WebClient())
|
|
|
|
|
|
|
|
|
|
web_site = server.Site(web_root, logPath=settings.HTTP_LOG_FILE)
|
|
|
|
|
for port in WEBSERVER_PORTS:
|
|
|
|
|
# create the webserver
|
2011-02-23 21:08:02 +00:00
|
|
|
webserver = WSGIWebServer(threads, port, web_site)
|
2010-12-12 10:54:33 +00:00
|
|
|
#webserver = internet.SSLServer(port, web_site)
|
2010-12-07 02:34:59 +00:00
|
|
|
webserver.setName('EvenniaWebServer%s' % port)
|
|
|
|
|
EVENNIA.services.addService(webserver)
|
|
|
|
|
|
|
|
|
|
if IRC_ENABLED:
|
|
|
|
|
|
|
|
|
|
# IRC channel connections
|
|
|
|
|
|
2011-04-10 12:39:07 +00:00
|
|
|
from src.comms import irc
|
|
|
|
|
irc.connect_all()
|
2011-04-16 22:26:22 +00:00
|
|
|
|
|
|
|
|
if IMC2_ENABLED:
|
|
|
|
|
|
|
|
|
|
# IMC2 channel connections
|
|
|
|
|
|
|
|
|
|
from src.comms import imc2
|
|
|
|
|
imc2.connect_all()
|
|
|
|
|
|
|
|
|
|
# clear server startup mode
|
|
|
|
|
ServerConfig.objects.conf("server_starting_mode", delete=True)
|