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
0
game/gamesrc/conf/__init__.py
Normal file
0
game/gamesrc/conf/__init__.py
Normal file
17
game/gamesrc/conf/at_initial_setup.py
Normal file
17
game/gamesrc/conf/at_initial_setup.py
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
"""
|
||||||
|
Custom at_initial_setup method. This allows you to hook special
|
||||||
|
modifications to the initial server startup process. Note that this
|
||||||
|
will only be run once - when the server starts up for the very first
|
||||||
|
time! It is called last in the startup process and can thus be used to
|
||||||
|
overload things that happened before it.
|
||||||
|
|
||||||
|
The module must contain a global function at_initial_setup(). This
|
||||||
|
will be called without arguments. Note that tracebacks in this module
|
||||||
|
will be QUIETLY ignored, so make sure to check it well to make sure it
|
||||||
|
does what you expect it to.
|
||||||
|
|
||||||
|
This module is selected by settings.AT_INITIAL_SETUP_HOOK_MODULE.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def at_initial_setup():
|
||||||
|
pass
|
||||||
|
|
@ -16,9 +16,9 @@
|
||||||
# reboot or reload the server to make them available.
|
# reboot or reload the server to make them available.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
from src.utils import utils
|
||||||
from src.commands.connection_screen import DEFAULT_SCREEN
|
from src.commands.connection_screen import DEFAULT_SCREEN
|
||||||
|
|
||||||
#from src.utils import utils
|
|
||||||
#
|
#
|
||||||
# CUSTOM_SCREEN = \
|
# CUSTOM_SCREEN = \
|
||||||
# """{b=============================================================={n
|
# """{b=============================================================={n
|
||||||
|
|
@ -32,10 +32,8 @@ from src.commands.connection_screen import DEFAULT_SCREEN
|
||||||
# Enter {whelp{n for more info. {wlook{n will re-load this screen.
|
# Enter {whelp{n for more info. {wlook{n will re-load this screen.
|
||||||
#{b=============================================================={n""" % utils.get_evennia_version()
|
#{b=============================================================={n""" % utils.get_evennia_version()
|
||||||
|
|
||||||
|
|
||||||
# # A suggested alternative screen for the Menu login system
|
# # A suggested alternative screen for the Menu login system
|
||||||
|
|
||||||
# from src.utils import utils
|
|
||||||
# MENU_SCREEN = \
|
# MENU_SCREEN = \
|
||||||
# """{b=============================================================={n
|
# """{b=============================================================={n
|
||||||
# Welcome to {gEvennnia{n, version %s!
|
# Welcome to {gEvennnia{n, version %s!
|
||||||
27
game/gamesrc/conf/lockfuncs.py
Normal file
27
game/gamesrc/conf/lockfuncs.py
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
"""
|
||||||
|
|
||||||
|
This is an example module for holding custom lock funcs, used in
|
||||||
|
in-game locks. The modules available to use as lockfuncs are defined
|
||||||
|
in the tuple settings.LOCK_FUNC_MODULES.
|
||||||
|
|
||||||
|
All functions defined globally in this module are assumed to be
|
||||||
|
available for use in lockstrings to determine access. See
|
||||||
|
http://code.google.com/p/evennia/wiki/Locks
|
||||||
|
|
||||||
|
A lock function is always called with two arguments, accessing_obj and
|
||||||
|
accessed_obj, followed by any number of arguments. All possible
|
||||||
|
arguments should be handled (excess ones calling magic (*args,
|
||||||
|
**kwargs) to avoid errors). The lock function should handle all
|
||||||
|
eventual tracebacks by logging the error and returning False.
|
||||||
|
|
||||||
|
See many more examples of lock functions in src.locks.lockfuncs.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def myfalse(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
called in lockstring with myfalse().
|
||||||
|
A simple logger that always returns false. Prints to stdout
|
||||||
|
for simplicity, should use utils.logger for real operation.
|
||||||
|
"""
|
||||||
|
print "%s tried to access %s. Access denied." % (accessing_obj, accessed_obj)
|
||||||
|
return False
|
||||||
121
game/gamesrc/conf/mssp.py
Normal file
121
game/gamesrc/conf/mssp.py
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
#
|
||||||
|
# MSSP (Mud Server Status Protocol) meta information
|
||||||
|
#
|
||||||
|
# MUD website listings (that you have registered with) can use this
|
||||||
|
# information to keep up-to-date with your game stats as you change
|
||||||
|
# them. Also number of currently active players and uptime will
|
||||||
|
# automatically be reported. You don't have to fill in everything
|
||||||
|
# (and most are not used by all crawlers); leave the default
|
||||||
|
# if so needed. You need to @reload the game before updated
|
||||||
|
# information is made available to crawlers (reloading does not
|
||||||
|
# affect uptime).
|
||||||
|
#
|
||||||
|
|
||||||
|
MSSPTable = {
|
||||||
|
|
||||||
|
# Required fieldss
|
||||||
|
|
||||||
|
"NAME": "Evennia",
|
||||||
|
|
||||||
|
# Generic
|
||||||
|
|
||||||
|
"CRAWL DELAY": "-1", # limit how often crawler updates the listing. -1 for no limit
|
||||||
|
|
||||||
|
"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": "", # 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 or 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"}
|
||||||
16
game/gamesrc/conf/oobfuncs.py
Normal file
16
game/gamesrc/conf/oobfuncs.py
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
#
|
||||||
|
# Example module holding functions for out-of-band protocols to
|
||||||
|
# import and map to given commands from the client. This module
|
||||||
|
# is selected by settings.OOB_FUNC_MODULE.
|
||||||
|
#
|
||||||
|
# All functions defined global in this module will be available
|
||||||
|
# for the oob system to call. They will be called with a session/character
|
||||||
|
# as first argument (depending on if the session is logged in or not),
|
||||||
|
# following by any number of extra arguments. The return value will
|
||||||
|
# be packed and returned to the oob protocol and can be on any form.
|
||||||
|
#
|
||||||
|
|
||||||
|
def testoob(character, *args, **kwargs):
|
||||||
|
"Simple test function"
|
||||||
|
print "Called testoob: %s" % val
|
||||||
|
return "testoob did stuff to the input string '%s'!" % val
|
||||||
|
|
@ -237,13 +237,13 @@ class CmdUnconnectedLook(MuxCommand):
|
||||||
string = ansi.parse_ansi(screen)
|
string = ansi.parse_ansi(screen)
|
||||||
self.caller.msg(string)
|
self.caller.msg(string)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
self.caller.msg(e)
|
self.caller.msg("Error in CONNECTION_SCREEN MODULE: " + str(e))
|
||||||
self.caller.msg("Connect screen not found. Enter 'help' for aid.")
|
self.caller.msg("Connect screen not found. Enter 'help' for aid.")
|
||||||
|
|
||||||
class CmdUnconnectedHelp(MuxCommand):
|
class CmdUnconnectedHelp(MuxCommand):
|
||||||
"""
|
"""
|
||||||
This is an unconnected version of the help command,
|
This is an unconnected version of the help command,
|
||||||
for simplicity. It shows a pane or info.
|
for simplicity. It shows a pane of info.
|
||||||
"""
|
"""
|
||||||
key = "help"
|
key = "help"
|
||||||
aliases = ["h", "?"]
|
aliases = ["h", "?"]
|
||||||
|
|
|
||||||
|
|
@ -210,14 +210,14 @@ class AMPProtocol(amp.AMP):
|
||||||
"""
|
"""
|
||||||
if hasattr(self.factory, "portal"):
|
if hasattr(self.factory, "portal"):
|
||||||
sessdata = self.factory.portal.sessions.get_all_sync_data()
|
sessdata = self.factory.portal.sessions.get_all_sync_data()
|
||||||
print sessdata
|
#print sessdata
|
||||||
self.call_remote_ServerAdmin(0,
|
self.call_remote_ServerAdmin(0,
|
||||||
"PSYNC",
|
"PSYNC",
|
||||||
data=sessdata)
|
data=sessdata)
|
||||||
if get_restart_mode(SERVER_RESTART):
|
if get_restart_mode(SERVER_RESTART):
|
||||||
msg = _(" ... Server restarted.")
|
msg = _(" ... Server restarted.")
|
||||||
self.factory.portal.sessions.announce_all(msg)
|
self.factory.portal.sessions.announce_all(msg)
|
||||||
|
self.factory.portal.sessions.at_server_connection()
|
||||||
|
|
||||||
# Error handling
|
# 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
|
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
|
||||||
library. If the client doesn't support MCCP, server sends uncompressed
|
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
|
effect of MCCP unless you have extremely heavy traffic or sits on a
|
||||||
terribly slow connection.
|
terribly slow connection.
|
||||||
|
|
||||||
|
This protocol is implemented by the telnet protocol importing
|
||||||
|
mccp_compress and calling it from its write methods.
|
||||||
"""
|
"""
|
||||||
import zlib
|
import zlib
|
||||||
|
|
||||||
|
|
@ -21,8 +23,7 @@ FLUSH = zlib.Z_SYNC_FLUSH
|
||||||
def mccp_compress(protocol, data):
|
def mccp_compress(protocol, data):
|
||||||
"Handles zlib compression, if applicable"
|
"Handles zlib compression, if applicable"
|
||||||
if hasattr(protocol, 'zlib'):
|
if hasattr(protocol, 'zlib'):
|
||||||
data = protocol.zlib.compress(data)
|
return protocol.zlib.compress(data) + protocol.zlib.flush(FLUSH)
|
||||||
data += protocol.zlib.flush(FLUSH)
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
class Mccp(object):
|
class Mccp(object):
|
||||||
|
|
@ -47,8 +48,7 @@ class Mccp(object):
|
||||||
def no_mccp(self, option):
|
def no_mccp(self, option):
|
||||||
"""
|
"""
|
||||||
If client doesn't support mccp, don't do anything.
|
If client doesn't support mccp, don't do anything.
|
||||||
"""
|
"""
|
||||||
print "deactivating mccp ..."
|
|
||||||
if hasattr(self.protocol, 'zlib'):
|
if hasattr(self.protocol, 'zlib'):
|
||||||
del self.protocol.zlib
|
del self.protocol.zlib
|
||||||
self.protocol.protocol_flags['MCCP'] = False
|
self.protocol.protocol_flags['MCCP'] = False
|
||||||
|
|
@ -58,7 +58,6 @@ class Mccp(object):
|
||||||
The client supports MCCP. Set things up by
|
The client supports MCCP. Set things up by
|
||||||
creating a zlib compression stream.
|
creating a zlib compression stream.
|
||||||
"""
|
"""
|
||||||
print "activating mccp ..."
|
|
||||||
self.protocol.protocol_flags['MCCP'] = True
|
self.protocol.protocol_flags['MCCP'] = True
|
||||||
self.protocol.requestNegotiation(MCCP, '')
|
self.protocol.requestNegotiation(MCCP, '')
|
||||||
self.protocol.zlib = zlib.compressobj(9)
|
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.
|
# names of attributes that should be affected by syncing.
|
||||||
_attrs_to_sync = ['protocol_key', 'address', 'suid', 'sessid', 'uid', 'uname',
|
_attrs_to_sync = ['protocol_key', 'address', 'suid', 'sessid', 'uid', 'uname',
|
||||||
'logged_in', 'cid', 'encoding',
|
'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):
|
def init_session(self, protocol_key, address, sessionhandler):
|
||||||
"""
|
"""
|
||||||
|
|
@ -72,6 +73,7 @@ class Session(object):
|
||||||
self.cmd_total = 0
|
self.cmd_total = 0
|
||||||
|
|
||||||
self.protocol_flags = {}
|
self.protocol_flags = {}
|
||||||
|
self.server_data = {}
|
||||||
|
|
||||||
# a back-reference to the relevant sessionhandler this
|
# a back-reference to the relevant sessionhandler this
|
||||||
# session is stored in.
|
# session is stored in.
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,7 @@ class ServerSessionHandler(SessionHandler):
|
||||||
"""
|
"""
|
||||||
self.sessions = {}
|
self.sessions = {}
|
||||||
self.server = None
|
self.server = None
|
||||||
|
self.server_data = {"servername":settings.SERVERNAME}
|
||||||
|
|
||||||
def portal_connect(self, sessid, session):
|
def portal_connect(self, sessid, session):
|
||||||
"""
|
"""
|
||||||
|
|
@ -333,6 +334,16 @@ class PortalSessionHandler(SessionHandler):
|
||||||
self.portal = None
|
self.portal = None
|
||||||
self.sessions = {}
|
self.sessions = {}
|
||||||
self.latest_sessid = 0
|
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):
|
def connect(self, session):
|
||||||
"""
|
"""
|
||||||
|
|
@ -373,6 +384,14 @@ class PortalSessionHandler(SessionHandler):
|
||||||
session.disconnect(reason)
|
session.disconnect(reason)
|
||||||
del session
|
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):
|
def session_from_suid(self, suid):
|
||||||
"""
|
"""
|
||||||
Given a session id, retrieve the session (this is primarily
|
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 twisted.conch.telnet import Telnet, StatefulTelnetProtocol, IAC, LINEMODE, DO, DONT
|
||||||
from src.server.session import Session
|
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
|
from src.utils import utils, ansi
|
||||||
|
|
||||||
class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
|
|
@ -27,11 +28,14 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
client_address = self.transport.client
|
client_address = self.transport.client
|
||||||
self.init_session("telnet", client_address, self.factory.sessionhandler)
|
self.init_session("telnet", client_address, self.factory.sessionhandler)
|
||||||
|
|
||||||
# setup ttype (client info)
|
# negotiate mccp (data compression)
|
||||||
#self.ttype = ttype.Ttype(self)
|
self.mccp = Mccp(self)
|
||||||
|
|
||||||
|
# negotiate ttype (client info)
|
||||||
|
self.ttype = ttype.Ttype(self)
|
||||||
|
|
||||||
# setup mccp (data compression)
|
# negotiate mssp (crawler communication)
|
||||||
# self.mccp = mccp.Mccp(self) #TODO: mccp doesn't work quite right yet.
|
self.mssp = mssp.Mssp(self)
|
||||||
|
|
||||||
# add us to sessionhandler
|
# add us to sessionhandler
|
||||||
self.sessionhandler.connect(self)
|
self.sessionhandler.connect(self)
|
||||||
|
|
@ -42,18 +46,17 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
"""
|
"""
|
||||||
return (option == LINEMODE or
|
return (option == LINEMODE or
|
||||||
option == ttype.TTYPE or
|
option == ttype.TTYPE or
|
||||||
option == mccp.MCCP)
|
option == MCCP or
|
||||||
|
option == mssp.MSSP)
|
||||||
|
|
||||||
def enableLocal(self, option):
|
def enableLocal(self, option):
|
||||||
"""
|
"""
|
||||||
Allow certain options on this protocol
|
Allow certain options on this protocol
|
||||||
"""
|
"""
|
||||||
if option == mccp.MCCP:
|
return option == MCCP
|
||||||
#self.mccp.do_mccp(option)
|
|
||||||
return True
|
|
||||||
|
|
||||||
def disableLocal(self, option):
|
def disableLocal(self, option):
|
||||||
if option == mccp.MCCP:
|
if option == MCCP:
|
||||||
self.mccp.no_mccp(option)
|
self.mccp.no_mccp(option)
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
|
|
@ -84,19 +87,26 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
# print str(e) + ":", str(data)
|
# print str(e) + ":", str(data)
|
||||||
|
|
||||||
if data and data[0] == IAC:
|
if data and data[0] == IAC:
|
||||||
super(TelnetProtocol, self).dataReceived(data)
|
try:
|
||||||
else:
|
super(TelnetProtocol, self).dataReceived(data)
|
||||||
StatefulTelnetProtocol.dataReceived(self, 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"
|
"hook overloading the one used in plain telnet"
|
||||||
#print "_write (%s): %s" % (self.state, " ".join(str(ord(c)) for c in byt))
|
#print "_write (%s): %s" % (self.state, " ".join(str(ord(c)) for c in data))
|
||||||
super(TelnetProtocol, self)._write(mccp.mccp_compress(self, byt))
|
data = data.replace('\n', '\r\n')
|
||||||
|
super(TelnetProtocol, self)._write(mccp_compress(self, data))
|
||||||
|
|
||||||
def sendLine(self, line):
|
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)
|
#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):
|
def lineReceived(self, string):
|
||||||
"""
|
"""
|
||||||
|
|
@ -105,6 +115,7 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
"""
|
"""
|
||||||
self.sessionhandler.data_in(self, string)
|
self.sessionhandler.data_in(self, string)
|
||||||
|
|
||||||
|
|
||||||
# Session hooks
|
# Session hooks
|
||||||
|
|
||||||
def disconnect(self, reason=None):
|
def disconnect(self, reason=None):
|
||||||
|
|
|
||||||
|
|
@ -157,12 +157,14 @@ SEARCH_AT_MULTIMATCH_INPUT = "src.commands.cmdparser.at_multimatch_input"
|
||||||
# The module holding text strings for the connection screen.
|
# The module holding text strings for the connection screen.
|
||||||
# This module should contain one or more variables
|
# This module should contain one or more variables
|
||||||
# with strings defining the look of the screen.
|
# with strings defining the look of the screen.
|
||||||
CONNECTION_SCREEN_MODULE = "game.gamesrc.world.connection_screens"
|
CONNECTION_SCREEN_MODULE = "game.gamesrc.conf.connection_screens"
|
||||||
# An option al module that, if existing, must hold a function
|
# An option al module that, if existing, must hold a function
|
||||||
# named at_initial_setup(). This hook method can be used to customize
|
# named at_initial_setup(). This hook method can be used to customize
|
||||||
# the server's initial setup sequence (the very first startup of the system).
|
# the server's initial setup sequence (the very first startup of the system).
|
||||||
# The check will fail quietly if module doesn't exist or fails to load.
|
# The check will fail quietly if module doesn't exist or fails to load.
|
||||||
AT_INITIAL_SETUP_HOOK_MODULE = "game.gamesrc.world.at_initial_setup"
|
AT_INITIAL_SETUP_HOOK_MODULE = "game.gamesrc.conf.at_initial_setup"
|
||||||
|
# Module holding server-side functions for out-of-band protocols to call.
|
||||||
|
OOB_FUNC_MODULE = "game.gamesrc.conf.oobfuncs"
|
||||||
|
|
||||||
###################################################
|
###################################################
|
||||||
# Default command sets
|
# Default command sets
|
||||||
|
|
@ -211,7 +213,7 @@ BASE_SCRIPT_TYPECLASS = "src.scripts.scripts.DoNothing"
|
||||||
CHARACTER_DEFAULT_HOME = "2"
|
CHARACTER_DEFAULT_HOME = "2"
|
||||||
|
|
||||||
###################################################
|
###################################################
|
||||||
# Batch processors
|
# Batch processors
|
||||||
###################################################
|
###################################################
|
||||||
|
|
||||||
# Python path to a directory to be searched for batch scripts
|
# Python path to a directory to be searched for batch scripts
|
||||||
|
|
@ -251,9 +253,7 @@ PERMISSION_HIERARCHY = ("Players","PlayerHelpers","Builders", "Wizards", "Immort
|
||||||
PERMISSION_PLAYER_DEFAULT = "Players"
|
PERMISSION_PLAYER_DEFAULT = "Players"
|
||||||
# Tuple of modules implementing lock functions. All callable functions
|
# Tuple of modules implementing lock functions. All callable functions
|
||||||
# inside these modules will be available as lock functions.
|
# inside these modules will be available as lock functions.
|
||||||
LOCK_FUNC_MODULES = ("src.locks.lockfuncs",)
|
LOCK_FUNC_MODULES = ("src.locks.lockfuncs","game.gamesrc.conf.lockfuncs")
|
||||||
# Module holding server-side functions for out-of-band protocols to call.
|
|
||||||
OOB_FUNC_MODULE = ""
|
|
||||||
|
|
||||||
|
|
||||||
###################################################
|
###################################################
|
||||||
|
|
|
||||||
|
|
@ -604,25 +604,45 @@ def mod_import(mod_path, propname=None):
|
||||||
return mod_prop
|
return mod_prop
|
||||||
return mod
|
return mod
|
||||||
|
|
||||||
def string_from_module(modpath, variable=None):
|
def variable_from_module(modpath, variable, default=None):
|
||||||
"""
|
"""
|
||||||
This obtains a string from a given module python path.
|
Retrieve a given variable from a module. The variable must be
|
||||||
The variable must be global within that module - that is, defined in
|
defined globally in the module. This can be used to implement
|
||||||
the outermost scope of the module. The value of the
|
arbitrary plugin imports in the server.
|
||||||
variable will be returned. If not found (or if it's not a string),
|
|
||||||
None is returned.
|
|
||||||
|
|
||||||
This is useful primarily for storing various game strings
|
If module cannot be imported or variable not found, default
|
||||||
in a module and extract them by name or randomly.
|
is returned.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
mod = __import__(modpath, fromlist=["None"])
|
||||||
|
return mod.__dict__.get(variable, default)
|
||||||
|
except ImportError:
|
||||||
|
return default
|
||||||
|
|
||||||
|
def string_from_module(modpath, variable=None, default=None):
|
||||||
|
"""
|
||||||
|
This is a variation used primarily to get login screens randomly
|
||||||
|
from a module.
|
||||||
|
|
||||||
|
This obtains a string from a given module python path. Using a
|
||||||
|
specific variable name will also retrieve non-strings.
|
||||||
|
|
||||||
|
The variable must be global within that module - that is, defined
|
||||||
|
in the outermost scope of the module. The value of the variable
|
||||||
|
will be returned. If not found, default is returned. If no variable is
|
||||||
|
given, a random string variable is returned.
|
||||||
|
|
||||||
|
This is useful primarily for storing various game strings in a
|
||||||
|
module and extract them by name or randomly.
|
||||||
"""
|
"""
|
||||||
mod = __import__(modpath, fromlist=[None])
|
mod = __import__(modpath, fromlist=[None])
|
||||||
if variable:
|
if variable:
|
||||||
return mod.__dict__.get(variable, None)
|
return mod.__dict__.get(variable, default)
|
||||||
else:
|
else:
|
||||||
mvars = [val for key, val in mod.__dict__.items()
|
mvars = [val for key, val in mod.__dict__.items()
|
||||||
if not key.startswith('_') and isinstance(val, basestring)]
|
if not key.startswith('_') and isinstance(val, basestring)]
|
||||||
if not mvars:
|
if not mvars:
|
||||||
return None
|
return default
|
||||||
return mvars[random.randint(0, len(mvars)-1)]
|
return mvars[random.randint(0, len(mvars)-1)]
|
||||||
|
|
||||||
def init_new_player(player):
|
def init_new_player(player):
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue