Implemented working MCCP (data compression) and MSSP (mud-listing crawler support). Moved all user-level customization modules from gamesrc/world to gamesrc/conf to reduce clutter.
This commit is contained in:
parent
a4f8019c4a
commit
fb78758356
15 changed files with 465 additions and 48 deletions
|
|
@ -210,14 +210,14 @@ class AMPProtocol(amp.AMP):
|
|||
"""
|
||||
if hasattr(self.factory, "portal"):
|
||||
sessdata = self.factory.portal.sessions.get_all_sync_data()
|
||||
print sessdata
|
||||
#print sessdata
|
||||
self.call_remote_ServerAdmin(0,
|
||||
"PSYNC",
|
||||
data=sessdata)
|
||||
if get_restart_mode(SERVER_RESTART):
|
||||
msg = _(" ... Server restarted.")
|
||||
self.factory.portal.sessions.announce_all(msg)
|
||||
|
||||
self.factory.portal.sessions.at_server_connection()
|
||||
|
||||
# Error handling
|
||||
|
||||
|
|
|
|||
|
|
@ -7,10 +7,12 @@ 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
|
||||
library. If the client doesn't support MCCP, server sends uncompressed
|
||||
instead. Note: On modern hardware you are not likely to notice the
|
||||
as normal. Note: On modern hardware you are not likely to notice the
|
||||
effect of MCCP unless you have extremely heavy traffic or sits on a
|
||||
terribly slow connection.
|
||||
|
||||
This protocol is implemented by the telnet protocol importing
|
||||
mccp_compress and calling it from its write methods.
|
||||
"""
|
||||
import zlib
|
||||
|
||||
|
|
@ -21,8 +23,7 @@ FLUSH = zlib.Z_SYNC_FLUSH
|
|||
def mccp_compress(protocol, data):
|
||||
"Handles zlib compression, if applicable"
|
||||
if hasattr(protocol, 'zlib'):
|
||||
data = protocol.zlib.compress(data)
|
||||
data += protocol.zlib.flush(FLUSH)
|
||||
return protocol.zlib.compress(data) + protocol.zlib.flush(FLUSH)
|
||||
return data
|
||||
|
||||
class Mccp(object):
|
||||
|
|
@ -47,8 +48,7 @@ class Mccp(object):
|
|||
def no_mccp(self, option):
|
||||
"""
|
||||
If client doesn't support mccp, don't do anything.
|
||||
"""
|
||||
print "deactivating mccp ..."
|
||||
"""
|
||||
if hasattr(self.protocol, 'zlib'):
|
||||
del self.protocol.zlib
|
||||
self.protocol.protocol_flags['MCCP'] = False
|
||||
|
|
@ -58,7 +58,6 @@ class Mccp(object):
|
|||
The client supports MCCP. Set things up by
|
||||
creating a zlib compression stream.
|
||||
"""
|
||||
print "activating mccp ..."
|
||||
self.protocol.protocol_flags['MCCP'] = True
|
||||
self.protocol.requestNegotiation(MCCP, '')
|
||||
self.protocol.zlib = zlib.compressobj(9)
|
||||
|
|
|
|||
187
src/server/mssp.py
Normal file
187
src/server/mssp.py
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
"""
|
||||
|
||||
MSSP - Mud Server Status Protocol
|
||||
|
||||
This implements the MSSP telnet protocol as per
|
||||
http://tintin.sourceforge.net/mssp/. MSSP allows web portals and
|
||||
listings to have their crawlers find the mud and automatically
|
||||
extract relevant information about it, such as genre, how many
|
||||
active players and so on.
|
||||
|
||||
Most of these settings are de
|
||||
|
||||
"""
|
||||
|
||||
from src.utils import utils
|
||||
|
||||
MSSP = chr(70)
|
||||
MSSP_VAR = chr(1)
|
||||
MSSP_VAL = chr(2)
|
||||
|
||||
|
||||
# try to get the customized mssp info, if it exists.
|
||||
MSSPTable_CUSTOM = utils.variable_from_module("game.gamesrc.conf.mssp", "MSSPTable", default={})
|
||||
|
||||
class Mssp(object):
|
||||
"""
|
||||
Implements the MSSP protocol. Add this to a
|
||||
variable on the telnet protocol to set it up.
|
||||
"""
|
||||
def __init__(self, protocol):
|
||||
"""
|
||||
initialize MSSP by storing protocol on ourselves
|
||||
and calling the client to see if it supports
|
||||
MSSP.
|
||||
"""
|
||||
self.protocol = protocol
|
||||
self.protocol.will(MSSP).addCallbacks(self.do_mssp, self.no_mssp)
|
||||
|
||||
def get_player_count(self):
|
||||
"Get number of logged-in players"
|
||||
return str(self.protocol.sessionhandler.count_loggedin())
|
||||
|
||||
def get_uptime(self):
|
||||
"Get how long the portal has been online (reloads are not counted)"
|
||||
return str(self.protocol.sessionhandler.uptime)
|
||||
|
||||
def no_mssp(self, option):
|
||||
"""
|
||||
This is the normal operation.
|
||||
"""
|
||||
print "no mssp"
|
||||
pass
|
||||
|
||||
def do_mssp(self, option):
|
||||
"""
|
||||
Negotiate all the information.
|
||||
"""
|
||||
|
||||
self.mssp_table = {
|
||||
|
||||
# Required fields
|
||||
|
||||
"NAME": "Evennia",
|
||||
"PLAYERS": self.get_player_count,
|
||||
"UPTIME" : self.get_uptime,
|
||||
|
||||
# Generic
|
||||
|
||||
"CRAWL DELAY": "-1",
|
||||
|
||||
"HOSTNAME": "", # current or new hostname
|
||||
"PORT": ["4000"], # most important port should be last in list
|
||||
"CODEBASE": "Evennia",
|
||||
"CONTACT": "", # email for contacting the mud
|
||||
"CREATED": "", # year MUD was created
|
||||
"ICON": "", # url to icon 32x32 or larger; <32kb.
|
||||
"IP": "", # current or new IP address
|
||||
"LANGUAGE": "", # name of language used, e.g. English
|
||||
"LOCATION": "", # full English name of server country
|
||||
"MINIMUM AGE": "0", # set to 0 if not applicable
|
||||
"WEBSITE": "www.evennia.com",
|
||||
|
||||
# Categorisation
|
||||
|
||||
"FAMILY": "Custom", # evennia goes under 'Custom'
|
||||
"GENRE": "None", # Adult, Fantasy, Historical, Horror, Modern, None, or Science Fiction
|
||||
"GAMEPLAY": "None", # Adventure, Educational, Hack and Slash, None,
|
||||
# Player versus Player, Player versus Environment,
|
||||
# Roleplaying, Simulation, Social or Strategy
|
||||
"STATUS": "Open Beta", # Alpha, Closed Beta, Open Beta, Live
|
||||
"GAMESYSTEM": "Custom", # D&D, d20 System, World of Darkness, etc. Use Custom if homebrew
|
||||
"INTERMUD": "IMC2", # evennia supports IMC2.
|
||||
"SUBGENRE": "None", # LASG, Medieval Fantasy, World War II, Frankenstein,
|
||||
# Cyberpunk, Dragonlance, etc. Or None if not available.
|
||||
|
||||
# World
|
||||
|
||||
"AREAS": "0",
|
||||
"HELPFILES": "0",
|
||||
"MOBILES": "0",
|
||||
"OBJECTS": "0",
|
||||
"ROOMS": "0", # use 0 if room-less
|
||||
"CLASSES": "0", # use 0 if class-less
|
||||
"LEVELS": "0", # use 0 if level-less
|
||||
"RACES": "0", # use 0 if race-less
|
||||
"SKILLS": "0", # use 0 if skill-less
|
||||
|
||||
# Protocols set to 1 or 0)
|
||||
|
||||
"ANSI": "1",
|
||||
"GMCP": "0",
|
||||
"MCCP": "0",
|
||||
"MCP": "0",
|
||||
"MSDP": "0",
|
||||
"MSP": "0",
|
||||
"MXP": "0",
|
||||
"PUEBLO": "0",
|
||||
"UTF-8": "1",
|
||||
"VT100": "0",
|
||||
"XTERM 256 COLORS": "0",
|
||||
|
||||
# Commercial set to 1 or 0)
|
||||
|
||||
"PAY TO PLAY": "0",
|
||||
"PAY FOR PERKS": "0",
|
||||
|
||||
# Hiring set to 1 or 0)
|
||||
|
||||
"HIRING BUILDERS": "0",
|
||||
"HIRING CODERS": "0",
|
||||
|
||||
# Extended variables
|
||||
|
||||
# World
|
||||
|
||||
"DBSIZE": "0",
|
||||
"EXITS": "0",
|
||||
"EXTRA DESCRIPTIONS": "0",
|
||||
"MUDPROGS": "0",
|
||||
"MUDTRIGS": "0",
|
||||
"RESETS": "0",
|
||||
|
||||
# Game (set to 1, 0 or one of the given alternatives)
|
||||
|
||||
"ADULT MATERIAL": "0",
|
||||
"MULTICLASSING": "0",
|
||||
"NEWBIE FRIENDLY": "0",
|
||||
"PLAYER CITIES": "0",
|
||||
"PLAYER CLANS": "0",
|
||||
"PLAYER CRAFTING": "0",
|
||||
"PLAYER GUILDS": "0",
|
||||
"EQUIPMENT SYSTEM": "None", # "None", "Level", "Skill", "Both"
|
||||
"MULTIPLAYING": "None", # "None", "Restricted", "Full"
|
||||
"PLAYERKILLING": "None", # "None", "Restricted", "Full"
|
||||
"QUEST SYSTEM": "None", # "None", "Immortal Run", "Automated", "Integrated"
|
||||
"ROLEPLAYING": "None", # "None", "Accepted", "Encouraged", "Enforced"
|
||||
"TRAINING SYSTEM": "None", # "None", "Level", "Skill", "Both"
|
||||
"WORLD ORIGINALITY": "None", # "All Stock", "Mostly Stock", "Mostly Original", "All Original"
|
||||
|
||||
# Protocols (only change if you added/removed something manually)
|
||||
|
||||
"ATCP": "0",
|
||||
"MSDP": "0",
|
||||
"MCCP": "1",
|
||||
"SSL": "1",
|
||||
"UTF-8": "1",
|
||||
"ZMP": "0",
|
||||
"XTERM 256 COLORS": "0"}
|
||||
|
||||
# update the static table with the custom one
|
||||
self.mssp_table.update(MSSPTable_CUSTOM)
|
||||
|
||||
varlist = ''
|
||||
for variable, value in self.mssp_table.items():
|
||||
if callable(value):
|
||||
value = value()
|
||||
if utils.is_iter(value):
|
||||
for partval in value:
|
||||
varlist += MSSP_VAR + str(variable) + MSSP_VAL + str(partval)
|
||||
else:
|
||||
varlist += MSSP_VAR + str(variable) + MSSP_VAL + str(value)
|
||||
|
||||
# send to crawler by subnegotiation
|
||||
self.protocol.requestNegotiation(MSSP, varlist)
|
||||
|
||||
|
||||
|
||||
|
|
@ -35,7 +35,8 @@ class Session(object):
|
|||
# names of attributes that should be affected by syncing.
|
||||
_attrs_to_sync = ['protocol_key', 'address', 'suid', 'sessid', 'uid', 'uname',
|
||||
'logged_in', 'cid', 'encoding',
|
||||
'conn_time', 'cmd_last', 'cmd_last_visible', 'cmd_total', 'protocol_flags']
|
||||
'conn_time', 'cmd_last', 'cmd_last_visible', 'cmd_total',
|
||||
'protocol_flags', 'server_data']
|
||||
|
||||
def init_session(self, protocol_key, address, sessionhandler):
|
||||
"""
|
||||
|
|
@ -72,6 +73,7 @@ class Session(object):
|
|||
self.cmd_total = 0
|
||||
|
||||
self.protocol_flags = {}
|
||||
self.server_data = {}
|
||||
|
||||
# a back-reference to the relevant sessionhandler this
|
||||
# session is stored in.
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ class ServerSessionHandler(SessionHandler):
|
|||
"""
|
||||
self.sessions = {}
|
||||
self.server = None
|
||||
self.server_data = {"servername":settings.SERVERNAME}
|
||||
|
||||
def portal_connect(self, sessid, session):
|
||||
"""
|
||||
|
|
@ -333,6 +334,16 @@ class PortalSessionHandler(SessionHandler):
|
|||
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):
|
||||
"""
|
||||
|
|
@ -373,6 +384,14 @@ class PortalSessionHandler(SessionHandler):
|
|||
session.disconnect(reason)
|
||||
del session
|
||||
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ sessions etc.
|
|||
|
||||
from twisted.conch.telnet import Telnet, StatefulTelnetProtocol, IAC, LINEMODE, DO, DONT
|
||||
from src.server.session import Session
|
||||
from src.server import ttype, mccp
|
||||
from src.server import ttype, mssp
|
||||
from src.server.mccp import Mccp, mccp_compress, MCCP
|
||||
from src.utils import utils, ansi
|
||||
|
||||
class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||
|
|
@ -27,11 +28,14 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
|||
client_address = self.transport.client
|
||||
self.init_session("telnet", client_address, self.factory.sessionhandler)
|
||||
|
||||
# setup ttype (client info)
|
||||
#self.ttype = ttype.Ttype(self)
|
||||
# negotiate mccp (data compression)
|
||||
self.mccp = Mccp(self)
|
||||
|
||||
# negotiate ttype (client info)
|
||||
self.ttype = ttype.Ttype(self)
|
||||
|
||||
# setup mccp (data compression)
|
||||
# self.mccp = mccp.Mccp(self) #TODO: mccp doesn't work quite right yet.
|
||||
# negotiate mssp (crawler communication)
|
||||
self.mssp = mssp.Mssp(self)
|
||||
|
||||
# add us to sessionhandler
|
||||
self.sessionhandler.connect(self)
|
||||
|
|
@ -42,18 +46,17 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
|||
"""
|
||||
return (option == LINEMODE or
|
||||
option == ttype.TTYPE or
|
||||
option == mccp.MCCP)
|
||||
option == MCCP or
|
||||
option == mssp.MSSP)
|
||||
|
||||
def enableLocal(self, option):
|
||||
"""
|
||||
Allow certain options on this protocol
|
||||
"""
|
||||
if option == mccp.MCCP:
|
||||
#self.mccp.do_mccp(option)
|
||||
return True
|
||||
return option == MCCP
|
||||
|
||||
def disableLocal(self, option):
|
||||
if option == mccp.MCCP:
|
||||
if option == MCCP:
|
||||
self.mccp.no_mccp(option)
|
||||
return True
|
||||
else:
|
||||
|
|
@ -84,19 +87,26 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
|||
# print str(e) + ":", str(data)
|
||||
|
||||
if data and data[0] == IAC:
|
||||
super(TelnetProtocol, self).dataReceived(data)
|
||||
else:
|
||||
StatefulTelnetProtocol.dataReceived(self, data)
|
||||
try:
|
||||
super(TelnetProtocol, self).dataReceived(data)
|
||||
return
|
||||
except Exception:
|
||||
pass
|
||||
StatefulTelnetProtocol.dataReceived(self, data)
|
||||
|
||||
def _write(self, byt):
|
||||
def _write(self, data):
|
||||
"hook overloading the one used in plain telnet"
|
||||
#print "_write (%s): %s" % (self.state, " ".join(str(ord(c)) for c in byt))
|
||||
super(TelnetProtocol, self)._write(mccp.mccp_compress(self, byt))
|
||||
#print "_write (%s): %s" % (self.state, " ".join(str(ord(c)) for c in data))
|
||||
data = data.replace('\n', '\r\n')
|
||||
super(TelnetProtocol, self)._write(mccp_compress(self, data))
|
||||
|
||||
def sendLine(self, line):
|
||||
"hook overloading the one used linereceiver"
|
||||
"hook overloading the one used by linereceiver"
|
||||
#print "sendLine (%s):\n%s" % (self.state, line)
|
||||
super(TelnetProtocol, self).sendLine(mccp.mccp_compress(self, line))
|
||||
#escape IAC in line mode, and correctly add \r\n
|
||||
line += self.delimiter
|
||||
line = line.replace(IAC, IAC + IAC).replace('\n', '\r\n')
|
||||
return self.transport.write(mccp_compress(self, line))
|
||||
|
||||
def lineReceived(self, string):
|
||||
"""
|
||||
|
|
@ -105,6 +115,7 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
|||
"""
|
||||
self.sessionhandler.data_in(self, string)
|
||||
|
||||
|
||||
# Session hooks
|
||||
|
||||
def disconnect(self, reason=None):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue