diff --git a/game/runner.py b/game/runner.py index f7408c74ce..3bc644065e 100644 --- a/game/runner.py +++ b/game/runner.py @@ -50,7 +50,7 @@ from django.conf import settings # Setup access of the evennia server itself 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 SERVER_LOGFILE = settings.SERVER_LOG_FILE diff --git a/src/server/portal/__init__.py b/src/server/portal/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/server/mccp.py b/src/server/portal/mccp.py similarity index 97% rename from src/server/mccp.py rename to src/server/portal/mccp.py index 9cf7b34d5d..2e6aa77cd3 100644 --- a/src/server/mccp.py +++ b/src/server/portal/mccp.py @@ -2,7 +2,7 @@ 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 compress data when sending to supporting clients, reducing bandwidth by 70-90%.. The compression is done using Python's builtin zlib diff --git a/src/server/msdp.py b/src/server/portal/msdp.py similarity index 100% rename from src/server/msdp.py rename to src/server/portal/msdp.py diff --git a/src/server/mssp.py b/src/server/portal/mssp.py similarity index 100% rename from src/server/mssp.py rename to src/server/portal/mssp.py diff --git a/src/server/portal.py b/src/server/portal/portal.py similarity index 96% rename from src/server/portal.py rename to src/server/portal/portal.py index b871bd434e..ca6bbc4d77 100644 --- a/src/server/portal.py +++ b/src/server/portal/portal.py @@ -11,15 +11,15 @@ 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__))))) + sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname( + os.path.dirname(os.path.abspath(__file__)))))) from twisted.application import internet, service from twisted.internet import protocol, reactor from twisted.web import server, static from django.conf import settings 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)] @@ -168,7 +168,7 @@ if TELNET_ENABLED: # Start telnet game connections - from src.server import telnet + from src.server.portal import telnet for interface in TELNET_INTERFACES: if ":" in interface: @@ -192,7 +192,7 @@ if SSL_ENABLED: # Start SSL game connection (requires PyOpenSSL). - from src.server import ssl + from src.server.portal import ssl for interface in SSL_INTERFACES: if ":" in interface: @@ -218,7 +218,7 @@ if SSH_ENABLED: # 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: if ":" in interface: @@ -255,7 +255,7 @@ if WEBSERVER_ENABLED: webclientstr = "" if WEBCLIENT_ENABLED: # create ajax client processes at /webclientdata - from src.server.webclient import WebClient + from src.server.portal.webclient import WebClient webclient = WebClient() webclient.sessionhandler = PORTAL_SESSIONS web_root.putChild("webclientdata", webclient) diff --git a/src/server/portal/portalsessionhandler.py b/src/server/portal/portalsessionhandler.py new file mode 100644 index 0000000000..04228933f8 --- /dev/null +++ b/src/server/portal/portalsessionhandler.py @@ -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() diff --git a/src/server/ssh.py b/src/server/portal/ssh.py similarity index 100% rename from src/server/ssh.py rename to src/server/portal/ssh.py diff --git a/src/server/ssl.py b/src/server/portal/ssl.py similarity index 98% rename from src/server/ssl.py rename to src/server/portal/ssl.py index b3064731e3..238d3bfd8b 100644 --- a/src/server/ssl.py +++ b/src/server/portal/ssl.py @@ -11,7 +11,7 @@ except ImportError: print " SSL_ENABLED requires PyOpenSSL." sys.exit(5) -from src.server.telnet import TelnetProtocol +from src.server.portal.telnet import TelnetProtocol class SSLProtocol(TelnetProtocol): """ diff --git a/src/server/telnet.py b/src/server/portal/telnet.py similarity index 98% rename from src/server/telnet.py rename to src/server/portal/telnet.py index 27e446e56d..0812d305f7 100644 --- a/src/server/telnet.py +++ b/src/server/portal/telnet.py @@ -10,8 +10,8 @@ sessions etc. import re from twisted.conch.telnet import Telnet, StatefulTelnetProtocol, IAC, LINEMODE from src.server.session import Session -from src.server import ttype, mssp -from src.server.mccp import Mccp, mccp_compress, MCCP +from src.server.portal import ttype, mssp +from src.server.portal.mccp import Mccp, mccp_compress, MCCP from src.utils import utils, ansi, logger _RE_N = re.compile(r"\{n$") diff --git a/src/server/ttype.py b/src/server/portal/ttype.py similarity index 100% rename from src/server/ttype.py rename to src/server/portal/ttype.py diff --git a/src/server/webclient.py b/src/server/portal/webclient.py similarity index 100% rename from src/server/webclient.py rename to src/server/portal/webclient.py diff --git a/src/server/sessionhandler.py b/src/server/sessionhandler.py index 34ca786ec8..6554279d31 100644 --- a/src/server/sessionhandler.py +++ b/src/server/sessionhandler.py @@ -380,167 +380,4 @@ class ServerSessionHandler(SessionHandler): """ self.server.amp_protocol.call_remote_OOBServer2Portal(session.sessid, 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() -PORTAL_SESSIONS = PortalSessionHandler()