Refactored src/server, splitting out into a portal subdirectory to make it clearer what goes on which "side".
This commit is contained in:
parent
76fa0059ea
commit
812bdb0f73
13 changed files with 179 additions and 175 deletions
|
|
@ -50,7 +50,7 @@ from django.conf import settings
|
||||||
|
|
||||||
# Setup access of the evennia server itself
|
# Setup access of the evennia server itself
|
||||||
SERVER_PY_FILE = os.path.join(settings.SRC_DIR, 'server/server.py')
|
SERVER_PY_FILE = os.path.join(settings.SRC_DIR, 'server/server.py')
|
||||||
PORTAL_PY_FILE = os.path.join(settings.SRC_DIR, 'server/portal.py')
|
PORTAL_PY_FILE = os.path.join(settings.SRC_DIR, 'server/portal/portal.py')
|
||||||
|
|
||||||
# Get logfile names
|
# Get logfile names
|
||||||
SERVER_LOGFILE = settings.SERVER_LOG_FILE
|
SERVER_LOGFILE = settings.SERVER_LOG_FILE
|
||||||
|
|
|
||||||
0
src/server/portal/__init__.py
Normal file
0
src/server/portal/__init__.py
Normal file
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
MCCP - Mud Client Compression Protocol
|
MCCP - Mud Client Compression Protocol
|
||||||
|
|
||||||
The implements the MCCP v2 telnet protocol as per
|
This implements the MCCP v2 telnet protocol as per
|
||||||
http://tintin.sourceforge.net/mccp/. MCCP allows for the server to
|
http://tintin.sourceforge.net/mccp/. MCCP allows for the server to
|
||||||
compress data when sending to supporting clients, reducing bandwidth
|
compress data when sending to supporting clients, reducing bandwidth
|
||||||
by 70-90%.. The compression is done using Python's builtin zlib
|
by 70-90%.. The compression is done using Python's builtin zlib
|
||||||
|
|
@ -11,15 +11,15 @@ import sys
|
||||||
import os
|
import os
|
||||||
if os.name == 'nt':
|
if os.name == 'nt':
|
||||||
# For Windows batchfile we need an extra path insertion here.
|
# For Windows batchfile we need an extra path insertion here.
|
||||||
sys.path.insert(0, os.path.dirname(os.path.dirname(
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(
|
||||||
os.path.dirname(os.path.abspath(__file__)))))
|
os.path.dirname(os.path.abspath(__file__))))))
|
||||||
|
|
||||||
from twisted.application import internet, service
|
from twisted.application import internet, service
|
||||||
from twisted.internet import protocol, reactor
|
from twisted.internet import protocol, reactor
|
||||||
from twisted.web import server, static
|
from twisted.web import server, static
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from src.utils.utils import get_evennia_version, mod_import, make_iter
|
from src.utils.utils import get_evennia_version, mod_import, make_iter
|
||||||
from src.server.sessionhandler import PORTAL_SESSIONS
|
from src.server.portal.portalsessionhandler import PORTAL_SESSIONS
|
||||||
|
|
||||||
PORTAL_SERVICES_PLUGIN_MODULES = [mod_import(module) for module in make_iter(settings.PORTAL_SERVICES_PLUGIN_MODULES)]
|
PORTAL_SERVICES_PLUGIN_MODULES = [mod_import(module) for module in make_iter(settings.PORTAL_SERVICES_PLUGIN_MODULES)]
|
||||||
|
|
||||||
|
|
@ -168,7 +168,7 @@ if TELNET_ENABLED:
|
||||||
|
|
||||||
# Start telnet game connections
|
# Start telnet game connections
|
||||||
|
|
||||||
from src.server import telnet
|
from src.server.portal import telnet
|
||||||
|
|
||||||
for interface in TELNET_INTERFACES:
|
for interface in TELNET_INTERFACES:
|
||||||
if ":" in interface:
|
if ":" in interface:
|
||||||
|
|
@ -192,7 +192,7 @@ if SSL_ENABLED:
|
||||||
|
|
||||||
# Start SSL game connection (requires PyOpenSSL).
|
# Start SSL game connection (requires PyOpenSSL).
|
||||||
|
|
||||||
from src.server import ssl
|
from src.server.portal import ssl
|
||||||
|
|
||||||
for interface in SSL_INTERFACES:
|
for interface in SSL_INTERFACES:
|
||||||
if ":" in interface:
|
if ":" in interface:
|
||||||
|
|
@ -218,7 +218,7 @@ if SSH_ENABLED:
|
||||||
|
|
||||||
# Start SSH game connections. Will create a keypair in evennia/game if necessary.
|
# Start SSH game connections. Will create a keypair in evennia/game if necessary.
|
||||||
|
|
||||||
from src.server import ssh
|
from src.server.portal import ssh
|
||||||
|
|
||||||
for interface in SSH_INTERFACES:
|
for interface in SSH_INTERFACES:
|
||||||
if ":" in interface:
|
if ":" in interface:
|
||||||
|
|
@ -255,7 +255,7 @@ if WEBSERVER_ENABLED:
|
||||||
webclientstr = ""
|
webclientstr = ""
|
||||||
if WEBCLIENT_ENABLED:
|
if WEBCLIENT_ENABLED:
|
||||||
# create ajax client processes at /webclientdata
|
# create ajax client processes at /webclientdata
|
||||||
from src.server.webclient import WebClient
|
from src.server.portal.webclient import WebClient
|
||||||
webclient = WebClient()
|
webclient = WebClient()
|
||||||
webclient.sessionhandler = PORTAL_SESSIONS
|
webclient.sessionhandler = PORTAL_SESSIONS
|
||||||
web_root.putChild("webclientdata", webclient)
|
web_root.putChild("webclientdata", webclient)
|
||||||
167
src/server/portal/portalsessionhandler.py
Normal file
167
src/server/portal/portalsessionhandler.py
Normal file
|
|
@ -0,0 +1,167 @@
|
||||||
|
"""
|
||||||
|
Sessionhandler for portal sessions
|
||||||
|
"""
|
||||||
|
import time
|
||||||
|
from src.server.sessionhandler import SessionHandler, PCONN, PDISCONN
|
||||||
|
|
||||||
|
#------------------------------------------------------------
|
||||||
|
# Portal-SessionHandler class
|
||||||
|
#------------------------------------------------------------
|
||||||
|
class PortalSessionHandler(SessionHandler):
|
||||||
|
"""
|
||||||
|
This object holds the sessions connected to the portal at any time.
|
||||||
|
It is synced with the server's equivalent SessionHandler over the AMP
|
||||||
|
connection.
|
||||||
|
|
||||||
|
Sessions register with the handler using the connect() method. This
|
||||||
|
will assign a new unique sessionid to the session and send that sessid
|
||||||
|
to the server using the AMP connection.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Init the handler
|
||||||
|
"""
|
||||||
|
self.portal = None
|
||||||
|
self.sessions = {}
|
||||||
|
self.latest_sessid = 0
|
||||||
|
self.uptime = time.time()
|
||||||
|
self.connection_time = 0
|
||||||
|
|
||||||
|
def at_server_connection(self):
|
||||||
|
"""
|
||||||
|
Called when the Portal establishes connection with the
|
||||||
|
Server. At this point, the AMP connection is already
|
||||||
|
established.
|
||||||
|
"""
|
||||||
|
self.connection_time = time.time()
|
||||||
|
|
||||||
|
def connect(self, session):
|
||||||
|
"""
|
||||||
|
Called by protocol at first connect. This adds a not-yet authenticated session
|
||||||
|
using an ever-increasing counter for sessid.
|
||||||
|
"""
|
||||||
|
self.latest_sessid += 1
|
||||||
|
sessid = self.latest_sessid
|
||||||
|
session.sessid = sessid
|
||||||
|
sessdata = session.get_sync_data()
|
||||||
|
self.sessions[sessid] = session
|
||||||
|
# sync with server-side
|
||||||
|
self.portal.amp_protocol.call_remote_ServerAdmin(sessid,
|
||||||
|
operation=PCONN,
|
||||||
|
data=sessdata)
|
||||||
|
def disconnect(self, session):
|
||||||
|
"""
|
||||||
|
Called from portal side when the connection is closed from the portal side.
|
||||||
|
"""
|
||||||
|
sessid = session.sessid
|
||||||
|
if sessid in self.sessions:
|
||||||
|
del self.sessions[sessid]
|
||||||
|
del session
|
||||||
|
# tell server to also delete this session
|
||||||
|
self.portal.amp_protocol.call_remote_ServerAdmin(sessid,
|
||||||
|
operation=PDISCONN)
|
||||||
|
|
||||||
|
def server_disconnect(self, sessid, reason=""):
|
||||||
|
"""
|
||||||
|
Called by server to force a disconnect by sessid
|
||||||
|
"""
|
||||||
|
session = self.sessions.get(sessid, None)
|
||||||
|
if session:
|
||||||
|
session.disconnect(reason)
|
||||||
|
if sessid in self.sessions:
|
||||||
|
# in case sess.disconnect doesn't delete it
|
||||||
|
del self.sessions[sessid]
|
||||||
|
del session
|
||||||
|
|
||||||
|
def server_disconnect_all(self, reason=""):
|
||||||
|
"""
|
||||||
|
Called by server when forcing a clean disconnect for everyone.
|
||||||
|
"""
|
||||||
|
for session in self.sessions.values():
|
||||||
|
session.disconnect(reason)
|
||||||
|
del session
|
||||||
|
self.sessions = {}
|
||||||
|
|
||||||
|
def server_logged_in(self, sessid, data):
|
||||||
|
"The server tells us that the session has been authenticated. Updated it."
|
||||||
|
sess = self.get_session(sessid)
|
||||||
|
sess.load_sync_data(data)
|
||||||
|
|
||||||
|
def server_session_sync(self, serversessions):
|
||||||
|
"""
|
||||||
|
Server wants to save data to the portal, maybe because it's about to shut down.
|
||||||
|
We don't overwrite any sessions here, just update them in-place and remove
|
||||||
|
any that are out of sync (which should normally not be the case)
|
||||||
|
|
||||||
|
serversessions - dictionary {sessid:{property:value},...} describing the properties
|
||||||
|
to sync on all sessions
|
||||||
|
"""
|
||||||
|
to_save = [sessid for sessid in serversessions if sessid in self.sessions]
|
||||||
|
to_delete = [sessid for sessid in self.sessions if sessid not in to_save]
|
||||||
|
# save protocols
|
||||||
|
for sessid in to_save:
|
||||||
|
self.sessions[sessid].load_sync_data(serversessions[sessid])
|
||||||
|
# disconnect out-of-sync missing protocols
|
||||||
|
for sessid in to_delete:
|
||||||
|
self.server_disconnect(sessid)
|
||||||
|
|
||||||
|
def count_loggedin(self, include_unloggedin=False):
|
||||||
|
"""
|
||||||
|
Count loggedin connections, alternatively count all connections.
|
||||||
|
"""
|
||||||
|
return len(self.get_sessions(include_unloggedin=include_unloggedin))
|
||||||
|
|
||||||
|
def session_from_suid(self, suid):
|
||||||
|
"""
|
||||||
|
Given a session id, retrieve the session (this is primarily
|
||||||
|
intended to be called by web clients)
|
||||||
|
"""
|
||||||
|
return [sess for sess in self.get_sessions(include_unloggedin=True)
|
||||||
|
if hasattr(sess, 'suid') and sess.suid == suid]
|
||||||
|
|
||||||
|
def data_in(self, session, string="", data=""):
|
||||||
|
"""
|
||||||
|
Called by portal sessions for relaying data coming
|
||||||
|
in from the protocol to the server. data is
|
||||||
|
serialized before passed on.
|
||||||
|
"""
|
||||||
|
#print "portal_data_in:", string
|
||||||
|
self.portal.amp_protocol.call_remote_MsgPortal2Server(session.sessid,
|
||||||
|
msg=string,
|
||||||
|
data=data)
|
||||||
|
def announce_all(self, message):
|
||||||
|
"""
|
||||||
|
Send message to all connection sessions
|
||||||
|
"""
|
||||||
|
for session in self.sessions.values():
|
||||||
|
session.data_out(message)
|
||||||
|
|
||||||
|
def data_out(self, sessid, string="", data=""):
|
||||||
|
"""
|
||||||
|
Called by server for having the portal relay messages and data
|
||||||
|
to the correct session protocol.
|
||||||
|
"""
|
||||||
|
session = self.sessions.get(sessid, None)
|
||||||
|
if session:
|
||||||
|
session.data_out(string, data=data)
|
||||||
|
|
||||||
|
def oob_data_in(self, session, data):
|
||||||
|
"""
|
||||||
|
OOB (Out-of-band) data Portal -> Server
|
||||||
|
"""
|
||||||
|
print "portal_oob_data_in:", data
|
||||||
|
self.portal.amp_protocol.call_remote_OOBPortal2Server(session.sessid,
|
||||||
|
data=data)
|
||||||
|
|
||||||
|
def oob_data_out(self, sessid, data):
|
||||||
|
"""
|
||||||
|
OOB (Out-of-band) data Server -> Portal
|
||||||
|
"""
|
||||||
|
print "portal_oob_data_out:", data
|
||||||
|
session = self.sessions.get(sessid, None)
|
||||||
|
if session:
|
||||||
|
session.oob_data_out(data)
|
||||||
|
|
||||||
|
PORTAL_SESSIONS = PortalSessionHandler()
|
||||||
|
|
@ -11,7 +11,7 @@ except ImportError:
|
||||||
print " SSL_ENABLED requires PyOpenSSL."
|
print " SSL_ENABLED requires PyOpenSSL."
|
||||||
sys.exit(5)
|
sys.exit(5)
|
||||||
|
|
||||||
from src.server.telnet import TelnetProtocol
|
from src.server.portal.telnet import TelnetProtocol
|
||||||
|
|
||||||
class SSLProtocol(TelnetProtocol):
|
class SSLProtocol(TelnetProtocol):
|
||||||
"""
|
"""
|
||||||
|
|
@ -10,8 +10,8 @@ sessions etc.
|
||||||
import re
|
import re
|
||||||
from twisted.conch.telnet import Telnet, StatefulTelnetProtocol, IAC, LINEMODE
|
from twisted.conch.telnet import Telnet, StatefulTelnetProtocol, IAC, LINEMODE
|
||||||
from src.server.session import Session
|
from src.server.session import Session
|
||||||
from src.server import ttype, mssp
|
from src.server.portal import ttype, mssp
|
||||||
from src.server.mccp import Mccp, mccp_compress, MCCP
|
from src.server.portal.mccp import Mccp, mccp_compress, MCCP
|
||||||
from src.utils import utils, ansi, logger
|
from src.utils import utils, ansi, logger
|
||||||
|
|
||||||
_RE_N = re.compile(r"\{n$")
|
_RE_N = re.compile(r"\{n$")
|
||||||
|
|
@ -380,167 +380,4 @@ class ServerSessionHandler(SessionHandler):
|
||||||
"""
|
"""
|
||||||
self.server.amp_protocol.call_remote_OOBServer2Portal(session.sessid,
|
self.server.amp_protocol.call_remote_OOBServer2Portal(session.sessid,
|
||||||
data=data)
|
data=data)
|
||||||
|
|
||||||
#------------------------------------------------------------
|
|
||||||
# Portal-SessionHandler class
|
|
||||||
#------------------------------------------------------------
|
|
||||||
|
|
||||||
class PortalSessionHandler(SessionHandler):
|
|
||||||
"""
|
|
||||||
This object holds the sessions connected to the portal at any time.
|
|
||||||
It is synced with the server's equivalent SessionHandler over the AMP
|
|
||||||
connection.
|
|
||||||
|
|
||||||
Sessions register with the handler using the connect() method. This
|
|
||||||
will assign a new unique sessionid to the session and send that sessid
|
|
||||||
to the server using the AMP connection.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
"""
|
|
||||||
Init the handler
|
|
||||||
"""
|
|
||||||
self.portal = None
|
|
||||||
self.sessions = {}
|
|
||||||
self.latest_sessid = 0
|
|
||||||
self.uptime = time.time()
|
|
||||||
self.connection_time = 0
|
|
||||||
|
|
||||||
def at_server_connection(self):
|
|
||||||
"""
|
|
||||||
Called when the Portal establishes connection with the
|
|
||||||
Server. At this point, the AMP connection is already
|
|
||||||
established.
|
|
||||||
"""
|
|
||||||
self.connection_time = time.time()
|
|
||||||
|
|
||||||
def connect(self, session):
|
|
||||||
"""
|
|
||||||
Called by protocol at first connect. This adds a not-yet authenticated session
|
|
||||||
using an ever-increasing counter for sessid.
|
|
||||||
"""
|
|
||||||
self.latest_sessid += 1
|
|
||||||
sessid = self.latest_sessid
|
|
||||||
session.sessid = sessid
|
|
||||||
sessdata = session.get_sync_data()
|
|
||||||
self.sessions[sessid] = session
|
|
||||||
# sync with server-side
|
|
||||||
self.portal.amp_protocol.call_remote_ServerAdmin(sessid,
|
|
||||||
operation=PCONN,
|
|
||||||
data=sessdata)
|
|
||||||
def disconnect(self, session):
|
|
||||||
"""
|
|
||||||
Called from portal side when the connection is closed from the portal side.
|
|
||||||
"""
|
|
||||||
sessid = session.sessid
|
|
||||||
if sessid in self.sessions:
|
|
||||||
del self.sessions[sessid]
|
|
||||||
del session
|
|
||||||
# tell server to also delete this session
|
|
||||||
self.portal.amp_protocol.call_remote_ServerAdmin(sessid,
|
|
||||||
operation=PDISCONN)
|
|
||||||
|
|
||||||
def server_disconnect(self, sessid, reason=""):
|
|
||||||
"""
|
|
||||||
Called by server to force a disconnect by sessid
|
|
||||||
"""
|
|
||||||
session = self.sessions.get(sessid, None)
|
|
||||||
if session:
|
|
||||||
session.disconnect(reason)
|
|
||||||
if sessid in self.sessions:
|
|
||||||
# in case sess.disconnect doesn't delete it
|
|
||||||
del self.sessions[sessid]
|
|
||||||
del session
|
|
||||||
|
|
||||||
def server_disconnect_all(self, reason=""):
|
|
||||||
"""
|
|
||||||
Called by server when forcing a clean disconnect for everyone.
|
|
||||||
"""
|
|
||||||
for session in self.sessions.values():
|
|
||||||
session.disconnect(reason)
|
|
||||||
del session
|
|
||||||
self.sessions = {}
|
|
||||||
|
|
||||||
def server_logged_in(self, sessid, data):
|
|
||||||
"The server tells us that the session has been authenticated. Updated it."
|
|
||||||
sess = self.get_session(sessid)
|
|
||||||
sess.load_sync_data(data)
|
|
||||||
|
|
||||||
def server_session_sync(self, serversessions):
|
|
||||||
"""
|
|
||||||
Server wants to save data to the portal, maybe because it's about to shut down.
|
|
||||||
We don't overwrite any sessions here, just update them in-place and remove
|
|
||||||
any that are out of sync (which should normally not be the case)
|
|
||||||
|
|
||||||
serversessions - dictionary {sessid:{property:value},...} describing the properties
|
|
||||||
to sync on all sessions
|
|
||||||
"""
|
|
||||||
to_save = [sessid for sessid in serversessions if sessid in self.sessions]
|
|
||||||
to_delete = [sessid for sessid in self.sessions if sessid not in to_save]
|
|
||||||
# save protocols
|
|
||||||
for sessid in to_save:
|
|
||||||
self.sessions[sessid].load_sync_data(serversessions[sessid])
|
|
||||||
# disconnect out-of-sync missing protocols
|
|
||||||
for sessid in to_delete:
|
|
||||||
self.server_disconnect(sessid)
|
|
||||||
|
|
||||||
def count_loggedin(self, include_unloggedin=False):
|
|
||||||
"""
|
|
||||||
Count loggedin connections, alternatively count all connections.
|
|
||||||
"""
|
|
||||||
return len(self.get_sessions(include_unloggedin=include_unloggedin))
|
|
||||||
|
|
||||||
def session_from_suid(self, suid):
|
|
||||||
"""
|
|
||||||
Given a session id, retrieve the session (this is primarily
|
|
||||||
intended to be called by web clients)
|
|
||||||
"""
|
|
||||||
return [sess for sess in self.get_sessions(include_unloggedin=True)
|
|
||||||
if hasattr(sess, 'suid') and sess.suid == suid]
|
|
||||||
|
|
||||||
def data_in(self, session, string="", data=""):
|
|
||||||
"""
|
|
||||||
Called by portal sessions for relaying data coming
|
|
||||||
in from the protocol to the server. data is
|
|
||||||
serialized before passed on.
|
|
||||||
"""
|
|
||||||
#print "portal_data_in:", string
|
|
||||||
self.portal.amp_protocol.call_remote_MsgPortal2Server(session.sessid,
|
|
||||||
msg=string,
|
|
||||||
data=data)
|
|
||||||
def announce_all(self, message):
|
|
||||||
"""
|
|
||||||
Send message to all connection sessions
|
|
||||||
"""
|
|
||||||
for session in self.sessions.values():
|
|
||||||
session.data_out(message)
|
|
||||||
|
|
||||||
def data_out(self, sessid, string="", data=""):
|
|
||||||
"""
|
|
||||||
Called by server for having the portal relay messages and data
|
|
||||||
to the correct session protocol.
|
|
||||||
"""
|
|
||||||
session = self.sessions.get(sessid, None)
|
|
||||||
if session:
|
|
||||||
session.data_out(string, data=data)
|
|
||||||
|
|
||||||
def oob_data_in(self, session, data):
|
|
||||||
"""
|
|
||||||
OOB (Out-of-band) data Portal -> Server
|
|
||||||
"""
|
|
||||||
print "portal_oob_data_in:", data
|
|
||||||
self.portal.amp_protocol.call_remote_OOBPortal2Server(session.sessid,
|
|
||||||
data=data)
|
|
||||||
|
|
||||||
def oob_data_out(self, sessid, data):
|
|
||||||
"""
|
|
||||||
OOB (Out-of-band) data Server -> Portal
|
|
||||||
"""
|
|
||||||
print "portal_oob_data_out:", data
|
|
||||||
session = self.sessions.get(sessid, None)
|
|
||||||
if session:
|
|
||||||
session.oob_data_out(data)
|
|
||||||
|
|
||||||
SESSIONS = ServerSessionHandler()
|
SESSIONS = ServerSessionHandler()
|
||||||
PORTAL_SESSIONS = PortalSessionHandler()
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue