Made almost the entire networking guts replaceable.

This commit is contained in:
Andrew Bastien 2020-04-12 10:24:31 -07:00
parent 373ac26805
commit d621239ff6
11 changed files with 106 additions and 42 deletions

View file

@ -10,6 +10,7 @@
- New `utils.format_grid` for easily displaying long lists of items in a block.
- Using `lunr` search indexing for better `help` matching and suggestions. Also improve
the main help command's default listing output.
- Made most of the networking classes such as Protocols and the SessionHandlers replaceable via `settings.py` for modding enthusiasts.
### Already in master

View file

@ -15,7 +15,7 @@ import zlib # Used in Compressed class
import pickle
from twisted.internet.defer import DeferredList, Deferred
from evennia.utils.utils import to_str, variable_from_module
from evennia.utils.utils import variable_from_module
# delayed import
_LOGGER = None

View file

@ -25,7 +25,7 @@ import evennia
evennia._init()
from evennia.utils.utils import get_evennia_version, mod_import, make_iter
from evennia.utils.utils import get_evennia_version, mod_import, make_iter, class_from_module
from evennia.server.portal.portalsessionhandler import PORTAL_SESSIONS
from evennia.utils import logger
from evennia.server.webserver import EvenniaReverseProxyResource
@ -261,6 +261,7 @@ if TELNET_ENABLED:
# Start telnet game connections
from evennia.server.portal import telnet
_telnet_protocol = class_from_module(settings.TELNET_PROTOCOL_CLASS)
for interface in TELNET_INTERFACES:
ifacestr = ""
@ -270,7 +271,7 @@ if TELNET_ENABLED:
pstring = "%s:%s" % (ifacestr, port)
factory = telnet.TelnetServerFactory()
factory.noisy = False
factory.protocol = telnet.TelnetProtocol
factory.protocol = _telnet_protocol
factory.sessionhandler = PORTAL_SESSIONS
telnet_service = internet.TCPServer(port, factory, interface=interface)
telnet_service.setName("EvenniaTelnet%s" % pstring)
@ -284,6 +285,7 @@ if SSL_ENABLED:
# Start Telnet+SSL game connection (requires PyOpenSSL).
from evennia.server.portal import telnet_ssl
_ssl_protocol = class_from_module(settings.SSL_PROTOCOL_CLASS)
for interface in SSL_INTERFACES:
ifacestr = ""
@ -294,7 +296,7 @@ if SSL_ENABLED:
factory = protocol.ServerFactory()
factory.noisy = False
factory.sessionhandler = PORTAL_SESSIONS
factory.protocol = telnet_ssl.SSLProtocol
factory.protocol = _ssl_protocol
ssl_context = telnet_ssl.getSSLContext()
if ssl_context:
@ -317,6 +319,7 @@ if SSH_ENABLED:
# evennia/game if necessary.
from evennia.server.portal import ssh
_ssh_protocol = class_from_module(settings.SSH_PROTOCOL_CLASS)
for interface in SSH_INTERFACES:
ifacestr = ""
@ -326,7 +329,7 @@ if SSH_ENABLED:
pstring = "%s:%s" % (ifacestr, port)
factory = ssh.makeFactory(
{
"protocolFactory": ssh.SshProtocol,
"protocolFactory": _ssh_protocol,
"protocolArgs": (),
"sessions": PORTAL_SESSIONS,
}
@ -345,6 +348,7 @@ if WEBSERVER_ENABLED:
# Start a reverse proxy to relay data to the Server-side webserver
websocket_started = False
_websocket_protocol = class_from_module(settings.WEBSOCKET_PROTOCOL_CLASS)
for interface in WEBSERVER_INTERFACES:
ifacestr = ""
if interface not in ("0.0.0.0", "::") or len(WEBSERVER_INTERFACES) > 1:
@ -379,7 +383,7 @@ if WEBSERVER_ENABLED:
factory = Websocket()
factory.noisy = False
factory.protocol = webclient.WebSocketClient
factory.protocol = _websocket_protocol
factory.sessionhandler = PORTAL_SESSIONS
websocket_service = internet.TCPServer(port, factory, interface=w_interface)
websocket_service.setName("EvenniaWebSocket%s:%s" % (w_ifacestr, port))

View file

@ -9,6 +9,7 @@ from twisted.internet import reactor
from django.conf import settings
from evennia.server.sessionhandler import SessionHandler, PCONN, PDISCONN, PCONNSYNC, PDISCONNALL
from evennia.utils.logger import log_trace
from evennia.utils.utils import class_from_module
# module import
_MOD_IMPORT = None
@ -32,9 +33,10 @@ DUMMYSESSION = namedtuple("DummySession", ["sessid"])(0)
# -------------------------------------------------------------
# Portal-SessionHandler class
# -------------------------------------------------------------
_BASE_HANDLER_CLASS = class_from_module(settings.SERVER_SESSION_HANDLER_CLASS)
class PortalSessionHandler(SessionHandler):
class PortalSessionHandler(_BASE_HANDLER_CLASS):
"""
This object holds the sessions connected to the portal at any time.
It is synced with the server's equivalent SessionHandler over the AMP
@ -463,4 +465,6 @@ class PortalSessionHandler(SessionHandler):
log_trace()
PORTAL_SESSIONS = PortalSessionHandler()
_portal_sessionhandler_class = class_from_module(settings.PORTAL_SESSION_HANDLER_CLASS)
PORTAL_SESSIONS = _portal_sessionhandler_class()

View file

@ -43,10 +43,9 @@ from twisted.conch import interfaces as iconch
from twisted.python import components
from django.conf import settings
from evennia.server import session
from evennia.accounts.models import AccountDB
from evennia.utils import ansi
from evennia.utils.utils import to_str
from evennia.utils.utils import to_str, class_from_module
_RE_N = re.compile(r"\|n$")
_RE_SCREENREADER_REGEX = re.compile(
@ -74,6 +73,8 @@ and put them here:
_PRIVATE_KEY_FILE, _PUBLIC_KEY_FILE
)
_BASE_SESSION = class_from_module(settings.BASE_SESSION_CLASS)
# not used atm
class SSHServerFactory(protocol.ServerFactory):
@ -84,7 +85,7 @@ class SSHServerFactory(protocol.ServerFactory):
return "SSH"
class SshProtocol(Manhole, session.Session):
class SshProtocol(Manhole, _BASE_SESSION):
"""
Each account connecting over ssh gets this protocol assigned to
them. All communication between game and account goes through

View file

@ -18,7 +18,7 @@ except ImportError as error:
raise ImportError(errstr.format(err=error))
from django.conf import settings
from evennia.server.portal.telnet import TelnetProtocol
from evennia.utils.utils import class_from_module
_GAME_DIR = settings.GAME_DIR
@ -43,8 +43,10 @@ example (linux, using the openssl program):
{exestring}
"""
_TELNET_PROTOCOL = class_from_module(settings.TELNET_PROTOCOL_CLASS)
class SSLProtocol(TelnetProtocol):
class SSLProtocol(_TELNET_PROTOCOL):
"""
Communication is the same as telnet, except data transfer
is done with encryption.

View file

@ -25,12 +25,11 @@ from twisted.conch.telnet import (
LINEMODE_TRAPSIG,
)
from django.conf import settings
from evennia.server.session import Session
from evennia.server.portal import ttype, mssp, telnet_oob, naws, suppress_ga
from evennia.server.portal.mccp import Mccp, mccp_compress, MCCP
from evennia.server.portal.mxp import Mxp, mxp_parse
from evennia.utils import ansi
from evennia.utils.utils import to_bytes
from evennia.utils.utils import to_bytes, class_from_module
_RE_N = re.compile(r"\|n$")
_RE_LEND = re.compile(br"\n$|\r$|\r\n$|\r\x00$|", re.MULTILINE)
@ -56,6 +55,10 @@ _HTTP_WARNING = bytes(
)
_BASE_SESSION = class_from_module(settings.BASE_SESSION_CLASS)
class TelnetServerFactory(protocol.ServerFactory):
"This is only to name this better in logs"
noisy = False
@ -64,7 +67,7 @@ class TelnetServerFactory(protocol.ServerFactory):
return "Telnet"
class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
class TelnetProtocol(Telnet, StatefulTelnetProtocol, _BASE_SESSION):
"""
Each player connecting over telnet (ie using most traditional mud
clients) gets a telnet protocol instance assigned to them. All

View file

@ -17,10 +17,8 @@ from the command line and interprets it as an Evennia Command: `["text", ["look"
import re
import json
import html
from twisted.internet.protocol import Protocol
from django.conf import settings
from evennia.server.session import Session
from evennia.utils.utils import to_str, mod_import
from evennia.utils.utils import mod_import, class_from_module
from evennia.utils.ansi import parse_ansi
from evennia.utils.text2html import parse_html
from autobahn.twisted.websocket import WebSocketServerProtocol
@ -39,8 +37,10 @@ CLOSE_NORMAL = WebSocketServerProtocol.CLOSE_STATUS_CODE_NORMAL
# called when the browser is navigating away from the page
GOING_AWAY = WebSocketServerProtocol.CLOSE_STATUS_CODE_GOING_AWAY
_BASE_SESSION = class_from_module(settings.BASE_SESSION_CLASS)
class WebSocketClient(WebSocketServerProtocol, Session):
class WebSocketClient(WebSocketServerProtocol, _BASE_SESSION):
"""
Implements the server-side of the Websocket connection.
"""

View file

@ -36,24 +36,7 @@ class Session(object):
"""
# names of attributes that should be affected by syncing.
_attrs_to_sync = (
"protocol_key",
"address",
"suid",
"sessid",
"uid",
"csessid",
"uname",
"logged_in",
"puid",
"conn_time",
"cmd_last",
"cmd_last_visible",
"cmd_total",
"protocol_flags",
"server_data",
"cmdset_storage_string",
)
_attrs_to_sync = settings.SESSION_SYNC_ATTRS
def init_session(self, protocol_key, address, sessionhandler):
"""

View file

@ -23,6 +23,7 @@ from evennia.utils.utils import (
make_iter,
delay,
callables_from_module,
class_from_module
)
from evennia.server.signals import SIGNAL_ACCOUNT_POST_LOGIN, SIGNAL_ACCOUNT_POST_LOGOUT
from evennia.server.signals import SIGNAL_ACCOUNT_POST_FIRST_LOGIN, SIGNAL_ACCOUNT_POST_LAST_LOGOUT
@ -857,5 +858,9 @@ class ServerSessionHandler(SessionHandler):
log_trace()
SESSION_HANDLER = ServerSessionHandler()
# import class from settings
_session_handler_class = class_from_module(settings.SERVER_SESSION_HANDLER_CLASS)
# Instantiate class. These globals are used to provide singleton-like behavior.
SESSION_HANDLER = _session_handler_class()
SESSIONS = SESSION_HANDLER # legacy

View file

@ -465,9 +465,6 @@ CHANNEL_COMMAND_CLASS = "evennia.comms.channelhandler.ChannelCommand"
# Typeclasses and other paths
######################################################################
# Server-side session class used.
SERVER_SESSION_CLASS = "evennia.server.serversession.ServerSession"
# These are paths that will be prefixed to the paths given if the
# immediately entered path fail to find a typeclass. It allows for
# shorter input strings. They must either base off the game directory
@ -511,6 +508,70 @@ START_LOCATION = "#2"
# issues.
TYPECLASS_AGGRESSIVE_CACHE = True
######################################################################
# Networking Replaceable Guts
######################################################################
# Modify the things below at your own risk. This is here be dragons territory.
# The Base Session Class is used as a parent class for all Protocols such as
# Telnet and SSH.) Changing this could be really dangerous. It will cascade
# to tons of classes. You generally shouldn't need to touch protocols.
BASE_SESSION_CLASS = "evennia.server.session.Session"
# Telnet Protocol inherits from whatever above BASE_SESSION_CLASS is specified.
# It is used for all telnet connections, and is also inherited by the SSL Protocol
# (which is just TLS + Telnet).
TELNET_PROTOCOL_CLASS = "evennia.server.portal.telnet.TelnetProtocol"
SSL_PROTOCOL_CLASS = "evennia.server.portal.ssl.SSLProtocol"
# Websocket Client Protocol. This inherits from BASE_SESSION_CLASS. It is used
# for all webclient connections.
WEBSOCKET_PROTOCOL_CLASS = "evennia.server.portal.webclient.WebSocketClient"
# Protocol for the SSH interface. This inherits from BASE_SESSION_CLASS.
SSH_PROTOCOL_CLASS = "evennia.server.portal.ssh.SshProtocol"
# Server-side session class used. This will inherit from BASE_SESSION_CLASS.
# This one isn't as dangerous to replace.
SERVER_SESSION_CLASS = "evennia.server.serversession.ServerSession"
# The Server SessionHandler manages all ServerSessions, handling logins,
# ensuring the login process happens smoothly, handling expected and
# unexpected disconnects. You shouldn't need to touch it, but you can.
# Replace it to implement altered game logic.
SERVER_SESSION_HANDLER_CLASS = "evennia.server.sessionhandler.ServerSessionHandler"
# The Portal SessionHandler manages all incoming connections regardless of
# the protocol in use. It is responsible for keeping them going and informing
# the Server Session Handler of the connections and synchronizing them across the
# AMP connection. You shouldn't ever need to change this. But you can.
PORTAL_SESSION_HANDLER_CLASS = "evennia.server.portal.portalsessionhandler.PortalSessionHandler"
# These are members / properties / attributes kept on both Server and
# Portal Sessions. They are sync'd at various points, such as logins and
# reloads. If you add to this, you may need to adjust the class __init__
# so the additions have somewhere to go. These must be simple things that
# can be pickled - stuff you could serialize to JSON is best.
SESSION_SYNC_ATTRS = (
"protocol_key",
"address",
"suid",
"sessid",
"uid",
"csessid",
"uname",
"logged_in",
"puid",
"conn_time",
"cmd_last",
"cmd_last_visible",
"cmd_total",
"protocol_flags",
"server_data",
"cmdset_storage_string"
)
######################################################################
# Options and validators
######################################################################