More whitespace cleanup.
This commit is contained in:
parent
c0322c9eae
commit
45c5be8468
43 changed files with 1116 additions and 1131 deletions
|
|
@ -1,6 +1,6 @@
|
|||
#
|
||||
# This sets up how models are displayed
|
||||
# in the web admin interface.
|
||||
# This sets up how models are displayed
|
||||
# in the web admin interface.
|
||||
#
|
||||
|
||||
from django.contrib import admin
|
||||
|
|
@ -12,7 +12,7 @@ class ServerConfigAdmin(admin.ModelAdmin):
|
|||
list_display_links = ('db_key',)
|
||||
ordering = ['db_key', 'db_value']
|
||||
search_fields = ['db_key']
|
||||
save_as = True
|
||||
save_on_top = True
|
||||
list_select_related = True
|
||||
save_as = True
|
||||
save_on_top = True
|
||||
list_select_related = True
|
||||
admin.site.register(ServerConfig, ServerConfigAdmin)
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@ to service the MUD portal proxy.
|
|||
|
||||
The separation works like this:
|
||||
|
||||
Portal - (AMP client) handles protocols. It contains a list of connected sessions in a
|
||||
Portal - (AMP client) handles protocols. It contains a list of connected sessions in a
|
||||
dictionary for identifying the respective player connected. If it looses the AMP connection
|
||||
it will automatically try to reconnect.
|
||||
|
||||
Server - (AMP server) Handles all mud operations. The server holds its own list
|
||||
it will automatically try to reconnect.
|
||||
|
||||
Server - (AMP server) Handles all mud operations. The server holds its own list
|
||||
of sessions tied to player objects. This is synced against the portal at startup
|
||||
and when a session connects/disconnects
|
||||
|
||||
|
|
@ -32,15 +32,15 @@ from src.server.serversession import ServerSession
|
|||
PORTAL_RESTART = os.path.join(settings.GAME_DIR, "portal.restart")
|
||||
SERVER_RESTART = os.path.join(settings.GAME_DIR, "server.restart")
|
||||
|
||||
# communication bits
|
||||
# communication bits
|
||||
|
||||
PCONN = chr(1) # portal session connect
|
||||
PDISCONN = chr(2) # portal session disconnect
|
||||
PSYNC = chr(3) # portal session sync
|
||||
SLOGIN = chr(4) # server session login
|
||||
SDISCONN = chr(5) # server session disconnect
|
||||
SDISCONN = chr(5) # server session disconnect
|
||||
SDISCONNALL = chr(6) # server session disconnect all
|
||||
SSHUTD = chr(7) # server shutdown
|
||||
SSHUTD = chr(7) # server shutdown
|
||||
SSYNC = chr(8) # server session sync
|
||||
|
||||
# i18n
|
||||
|
|
@ -54,7 +54,7 @@ def get_restart_mode(restart_file):
|
|||
if os.path.exists(restart_file):
|
||||
flag = open(restart_file, 'r').read()
|
||||
return flag == "True"
|
||||
return False
|
||||
return False
|
||||
|
||||
class AmpServerFactory(protocol.ServerFactory):
|
||||
"""
|
||||
|
|
@ -66,8 +66,8 @@ class AmpServerFactory(protocol.ServerFactory):
|
|||
server: The Evennia server service instance
|
||||
protocol: The protocol the factory creates instances of.
|
||||
"""
|
||||
self.server = server
|
||||
self.protocol = AMPProtocol
|
||||
self.server = server
|
||||
self.protocol = AMPProtocol
|
||||
|
||||
def buildProtocol(self, addr):
|
||||
"""
|
||||
|
|
@ -91,7 +91,7 @@ class AmpClientFactory(protocol.ReconnectingClientFactory):
|
|||
maxDelay = 1
|
||||
|
||||
def __init__(self, portal):
|
||||
self.portal = portal
|
||||
self.portal = portal
|
||||
self.protocol = AMPProtocol
|
||||
|
||||
def startedConnecting(self, connector):
|
||||
|
|
@ -100,10 +100,10 @@ class AmpClientFactory(protocol.ReconnectingClientFactory):
|
|||
"""
|
||||
pass
|
||||
#print 'AMP started to connect:', connector
|
||||
|
||||
|
||||
def buildProtocol(self, addr):
|
||||
"""
|
||||
Creates an AMPProtocol instance when connecting to the server.
|
||||
Creates an AMPProtocol instance when connecting to the server.
|
||||
"""
|
||||
#print "Portal connected to Evennia server at %s." % addr
|
||||
self.resetDelay()
|
||||
|
|
@ -114,7 +114,7 @@ class AmpClientFactory(protocol.ReconnectingClientFactory):
|
|||
def clientConnectionLost(self, connector, reason):
|
||||
"""
|
||||
Called when the AMP connection to the MUD server is lost.
|
||||
"""
|
||||
"""
|
||||
if not get_restart_mode(SERVER_RESTART):
|
||||
self.portal.sessions.announce_all(_(" Portal lost connection to Server."))
|
||||
protocol.ReconnectingClientFactory.clientConnectionLost(self, connector, reason)
|
||||
|
|
@ -128,7 +128,7 @@ class AmpClientFactory(protocol.ReconnectingClientFactory):
|
|||
|
||||
|
||||
class MsgPortal2Server(amp.Command):
|
||||
"""
|
||||
"""
|
||||
Message portal -> server
|
||||
"""
|
||||
arguments = [('sessid', amp.Integer()),
|
||||
|
|
@ -138,7 +138,7 @@ class MsgPortal2Server(amp.Command):
|
|||
response = []
|
||||
|
||||
class MsgServer2Portal(amp.Command):
|
||||
"""
|
||||
"""
|
||||
Message server -> portal
|
||||
"""
|
||||
arguments = [('sessid', amp.Integer()),
|
||||
|
|
@ -155,7 +155,7 @@ class OOBPortal2Server(amp.Command):
|
|||
('data', amp.String())]
|
||||
errors = [(Exception, "EXCEPTION")]
|
||||
response = []
|
||||
|
||||
|
||||
class OOBServer2Portal(amp.Command):
|
||||
"""
|
||||
OOB data server -> portal
|
||||
|
|
@ -164,13 +164,13 @@ class OOBServer2Portal(amp.Command):
|
|||
('data', amp.String())]
|
||||
errors = [(Exception, "EXCEPTION")]
|
||||
response = []
|
||||
|
||||
|
||||
class ServerAdmin(amp.Command):
|
||||
"""
|
||||
Portal -> Server
|
||||
|
||||
Sent when the portal needs to perform admin
|
||||
operations on the server, such as when a new
|
||||
operations on the server, such as when a new
|
||||
session connects or resyncs
|
||||
"""
|
||||
arguments = [('sessid', amp.Integer()),
|
||||
|
|
@ -178,13 +178,13 @@ class ServerAdmin(amp.Command):
|
|||
('data', amp.String())]
|
||||
errors = [(Exception, 'EXCEPTION')]
|
||||
response = []
|
||||
|
||||
|
||||
class PortalAdmin(amp.Command):
|
||||
"""
|
||||
Server -> Portal
|
||||
|
||||
Sent when the server needs to perform admin
|
||||
operations on the portal.
|
||||
operations on the portal.
|
||||
"""
|
||||
arguments = [('sessid', amp.Integer()),
|
||||
('operation', amp.String()),
|
||||
|
|
@ -209,27 +209,27 @@ class AMPProtocol(amp.AMP):
|
|||
subclasses that specify the datatypes of the input/output of these methods.
|
||||
"""
|
||||
|
||||
# helper methods
|
||||
# helper methods
|
||||
|
||||
def connectionMade(self):
|
||||
"""
|
||||
This is called when a connection is established
|
||||
between server and portal. It is called on both sides,
|
||||
so we need to make sure to only trigger resync from the
|
||||
server side.
|
||||
server side.
|
||||
"""
|
||||
if hasattr(self.factory, "portal"):
|
||||
sessdata = self.factory.portal.sessions.get_all_sync_data()
|
||||
#print sessdata
|
||||
self.call_remote_ServerAdmin(0,
|
||||
PSYNC,
|
||||
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
|
||||
|
||||
# Error handling
|
||||
|
||||
def errback(self, e, info):
|
||||
"error handler, to avoid dropping connections on server tracebacks."
|
||||
|
|
@ -240,8 +240,8 @@ class AMPProtocol(amp.AMP):
|
|||
# Message definition + helper methods to call/create each message type
|
||||
|
||||
# Portal -> Server Msg
|
||||
|
||||
def amp_msg_portal2server(self, sessid, msg, data):
|
||||
|
||||
def amp_msg_portal2server(self, sessid, msg, data):
|
||||
"""
|
||||
Relays message to server. This method is executed on the Server.
|
||||
"""
|
||||
|
|
@ -253,14 +253,14 @@ class AMPProtocol(amp.AMP):
|
|||
def call_remote_MsgPortal2Server(self, sessid, msg, data=""):
|
||||
"""
|
||||
Access method called by the Portal and executed on the Portal.
|
||||
"""
|
||||
"""
|
||||
#print "msg portal->server (portal side):", sessid, msg
|
||||
self.callRemote(MsgPortal2Server,
|
||||
sessid=sessid,
|
||||
msg=msg,
|
||||
data=dumps(data)).addErrback(self.errback, "MsgPortal2Server")
|
||||
|
||||
# Server -> Portal message
|
||||
# Server -> Portal message
|
||||
|
||||
def amp_msg_server2portal(self, sessid, msg, data):
|
||||
"""
|
||||
|
|
@ -281,11 +281,11 @@ class AMPProtocol(amp.AMP):
|
|||
msg=to_str(msg),
|
||||
data=dumps(data)).addErrback(self.errback, "OOBServer2Portal")
|
||||
|
||||
# OOB Portal -> Server
|
||||
|
||||
# OOB Portal -> Server
|
||||
|
||||
# Portal -> Server Msg
|
||||
|
||||
def amp_oob_portal2server(self, sessid, data):
|
||||
|
||||
def amp_oob_portal2server(self, sessid, data):
|
||||
"""
|
||||
Relays out-of-band data to server. This method is executed on the Server.
|
||||
"""
|
||||
|
|
@ -297,13 +297,13 @@ class AMPProtocol(amp.AMP):
|
|||
def call_remote_OOBPortal2Server(self, sessid, data=""):
|
||||
"""
|
||||
Access method called by the Portal and executed on the Portal.
|
||||
"""
|
||||
"""
|
||||
#print "oob portal->server (portal side):", sessid, data
|
||||
self.callRemote(OOBPortal2Server,
|
||||
sessid=sessid,
|
||||
sessid=sessid,
|
||||
data=dumps(data)).addErrback(self.errback, "OOBPortal2Server")
|
||||
|
||||
# Server -> Portal message
|
||||
# Server -> Portal message
|
||||
|
||||
def amp_oob_server2portal(self, sessid, data):
|
||||
"""
|
||||
|
|
@ -318,13 +318,13 @@ class AMPProtocol(amp.AMP):
|
|||
"""
|
||||
Access method called by the Server and executed on the Server.
|
||||
"""
|
||||
#print "oob server->portal (server side):", sessid, data
|
||||
#print "oob server->portal (server side):", sessid, data
|
||||
self.callRemote(OOBServer2Portal,
|
||||
sessid=sessid,
|
||||
sessid=sessid,
|
||||
data=dumps(data)).addErrback(self.errback, "OOBServer2Portal")
|
||||
|
||||
|
||||
# Server administration from the Portal side
|
||||
|
||||
# Server administration from the Portal side
|
||||
def amp_server_admin(self, sessid, operation, data):
|
||||
"""
|
||||
This allows the portal to perform admin
|
||||
|
|
@ -334,12 +334,12 @@ class AMPProtocol(amp.AMP):
|
|||
data = loads(data)
|
||||
|
||||
#print "serveradmin (server side):", sessid, operation, data
|
||||
|
||||
|
||||
if operation == PCONN: #portal_session_connect
|
||||
# create a new session and sync it
|
||||
sess = ServerSession()
|
||||
sess.sessionhandler = self.factory.server.sessions
|
||||
sess.load_sync_data(data)
|
||||
sess.load_sync_data(data)
|
||||
if sess.logged_in and sess.uid:
|
||||
# this can happen in the case of auto-authenticating protocols like SSH
|
||||
sess.player = PlayerDB.objects.get_player_from_uid(sess.uid)
|
||||
|
|
@ -348,24 +348,24 @@ class AMPProtocol(amp.AMP):
|
|||
self.factory.server.sessions.portal_connect(sessid, sess)
|
||||
|
||||
elif operation == PDISCONN: #'portal_session_disconnect'
|
||||
# session closed from portal side
|
||||
# session closed from portal side
|
||||
self.factory.server.sessions.portal_disconnect(sessid)
|
||||
|
||||
elif operation == PSYNC: #'portal_session_sync'
|
||||
# force a resync of sessions when portal reconnects to server (e.g. after a server reboot)
|
||||
# force a resync of sessions when portal reconnects to server (e.g. after a server reboot)
|
||||
# the data kwarg contains a dict {sessid: {arg1:val1,...}} representing the attributes
|
||||
# to sync for each session.
|
||||
sesslist = []
|
||||
server_sessionhandler = self.factory.server.sessions
|
||||
for sessid, sessdict in data.items():
|
||||
for sessid, sessdict in data.items():
|
||||
sess = ServerSession()
|
||||
sess.sessionhandler = server_sessionhandler
|
||||
sess.load_sync_data(sessdict)
|
||||
if sess.uid:
|
||||
sess.player = PlayerDB.objects.get_player_from_uid(sess.uid)
|
||||
sesslist.append(sess)
|
||||
sesslist.append(sess)
|
||||
# replace sessions on server
|
||||
server_sessionhandler.portal_session_sync(sesslist)
|
||||
server_sessionhandler.portal_session_sync(sesslist)
|
||||
# after sync is complete we force-validate all scripts (this starts everything)
|
||||
init_mode = ServerConfig.objects.conf("server_restart_mode", default=None)
|
||||
ScriptDB.objects.validate(init_mode=init_mode)
|
||||
|
|
@ -373,7 +373,7 @@ class AMPProtocol(amp.AMP):
|
|||
|
||||
else:
|
||||
raise Exception(_("operation %(op)s not recognized.") % {'op': operation})
|
||||
|
||||
|
||||
return {}
|
||||
ServerAdmin.responder(amp_server_admin)
|
||||
|
||||
|
|
@ -393,7 +393,7 @@ class AMPProtocol(amp.AMP):
|
|||
|
||||
def amp_portal_admin(self, sessid, operation, data):
|
||||
"""
|
||||
This allows the server to perform admin
|
||||
This allows the server to perform admin
|
||||
operations on the portal. This is executed on the Portal.
|
||||
"""
|
||||
data = loads(data)
|
||||
|
|
@ -401,7 +401,7 @@ class AMPProtocol(amp.AMP):
|
|||
#print "portaladmin (portal side):", sessid, operation, data
|
||||
if operation == SLOGIN: # 'server_session_login'
|
||||
# a session has authenticated; sync it.
|
||||
sess = self.factory.portal.sessions.get_session(sessid)
|
||||
sess = self.factory.portal.sessions.get_session(sessid)
|
||||
sess.load_sync_data(data)
|
||||
|
||||
elif operation == SDISCONN: #'server_session_disconnect'
|
||||
|
|
@ -415,14 +415,14 @@ class AMPProtocol(amp.AMP):
|
|||
elif operation == SSHUTD: #server_shutdown'
|
||||
# the server orders the portal to shut down
|
||||
self.factory.portal.shutdown(restart=False)
|
||||
|
||||
|
||||
elif operation == SSYNC: #'server_session_sync'
|
||||
# server wants to save session data to the portal, maybe because
|
||||
# it's about to shut down. We don't overwrite any sessions,
|
||||
# just update data on them and remove eventual ones that are
|
||||
# out of sync (shouldn't happen normally).
|
||||
# it's about to shut down. We don't overwrite any sessions,
|
||||
# just update data on them and remove eventual ones that are
|
||||
# out of sync (shouldn't happen normally).
|
||||
|
||||
portal_sessionhandler = self.factory.portal.sessions.sessions
|
||||
portal_sessionhandler = self.factory.portal.sessions.sessions
|
||||
|
||||
to_save = [sessid for sessid in data if sessid in portal_sessionhandler.sessions]
|
||||
to_delete = [sessid for sessid in data if sessid not in to_save]
|
||||
|
|
@ -449,10 +449,3 @@ class AMPProtocol(amp.AMP):
|
|||
sessid=sessid,
|
||||
operation=operation,
|
||||
data=data).addErrback(self.errback, "PortalAdmin")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ from django.core import management
|
|||
from django.conf import settings
|
||||
from src.server.models import ServerConfig
|
||||
from src.help.models import HelpEntry
|
||||
from src.utils import create
|
||||
from src.utils import create
|
||||
|
||||
# i18n
|
||||
from django.utils.translation import ugettext as _
|
||||
|
|
@ -19,7 +19,7 @@ from django.utils.translation import ugettext as _
|
|||
def create_config_values():
|
||||
"""
|
||||
Creates the initial config values.
|
||||
"""
|
||||
"""
|
||||
ServerConfig.objects.conf("site_name", settings.SERVERNAME)
|
||||
ServerConfig.objects.conf("idle_timeout", settings.IDLE_TIMEOUT)
|
||||
|
||||
|
|
@ -33,28 +33,28 @@ def create_objects():
|
|||
"""
|
||||
Creates the #1 player and Limbo room.
|
||||
"""
|
||||
|
||||
|
||||
print _(" Creating objects (Player #1 and Limbo room) ...")
|
||||
|
||||
# Set the initial User's account object's username on the #1 object.
|
||||
# This object is pure django and only holds name, email and password.
|
||||
# This object is pure django and only holds name, email and password.
|
||||
god_user = get_god_user()
|
||||
|
||||
# Create a Player 'user profile' object to hold eventual
|
||||
# mud-specific settings for the bog standard User object. This is
|
||||
# accessed by user.get_profile() and can also store attributes.
|
||||
# It also holds mud permissions, but for a superuser these
|
||||
# have no effect anyhow.
|
||||
# have no effect anyhow.
|
||||
|
||||
character_typeclass = settings.BASE_CHARACTER_TYPECLASS
|
||||
|
||||
# Create the Player object as well as the in-game god-character
|
||||
# for user #1. We can't set location and home yet since nothing
|
||||
# exists. Also, all properties (name, email, password, is_superuser)
|
||||
# is inherited from the user so we don't specify it again here.
|
||||
# is inherited from the user so we don't specify it again here.
|
||||
|
||||
god_character = create.create_player(god_user.username, None, None,
|
||||
user=god_user,
|
||||
god_character = create.create_player(god_user.username, None, None,
|
||||
user=god_user,
|
||||
create_character=True,
|
||||
character_typeclass=character_typeclass)
|
||||
|
||||
|
|
@ -66,7 +66,7 @@ def create_objects():
|
|||
god_character.locks.add("examine:perm(Immortals);edit:false();delete:false();boot:false();msg:all();puppet:false()")
|
||||
|
||||
god_character.save()
|
||||
|
||||
|
||||
# Limbo is the default "nowhere" starting room
|
||||
|
||||
room_typeclass = settings.BASE_ROOM_TYPECLASS
|
||||
|
|
@ -83,11 +83,11 @@ def create_objects():
|
|||
|
||||
# Now that Limbo exists, try to set the user up in Limbo (unless
|
||||
# the creation hooks already fixed this).
|
||||
if not god_character.location:
|
||||
if not god_character.location:
|
||||
god_character.location = limbo_obj
|
||||
if not god_character.home:
|
||||
god_character.home = limbo_obj
|
||||
|
||||
|
||||
def create_channels():
|
||||
"""
|
||||
Creates some sensible default channels.
|
||||
|
|
@ -97,7 +97,7 @@ def create_channels():
|
|||
# public channel
|
||||
key, aliases, desc, locks = settings.CHANNEL_PUBLIC
|
||||
pchan = create.create_channel(key, aliases, desc, locks=locks)
|
||||
# mudinfo channel
|
||||
# mudinfo channel
|
||||
key, aliases, desc, locks = settings.CHANNEL_MUDINFO
|
||||
ichan = create.create_channel(key, aliases, desc, locks=locks)
|
||||
# connectinfo channel
|
||||
|
|
@ -110,29 +110,29 @@ def create_channels():
|
|||
PlayerChannelConnection.objects.create_connection(goduser, pchan)
|
||||
PlayerChannelConnection.objects.create_connection(goduser, ichan)
|
||||
PlayerChannelConnection.objects.create_connection(goduser, cchan)
|
||||
|
||||
|
||||
def import_MUX_help_files():
|
||||
"""
|
||||
Imports the MUX help files.
|
||||
"""
|
||||
"""
|
||||
print _(" Importing MUX help database (devel reference only) ...")
|
||||
management.call_command('loaddata', '../src/help/mux_help_db.json', verbosity=0)
|
||||
management.call_command('loaddata', '../src/help/mux_help_db.json', verbosity=0)
|
||||
# categorize the MUX help files into its own category.
|
||||
default_category = "MUX"
|
||||
print _(" Moving imported help db to help category '%(default)s'." \
|
||||
% {'default': default_category})
|
||||
HelpEntry.objects.all_to_category(default_category)
|
||||
|
||||
|
||||
def create_system_scripts():
|
||||
"""
|
||||
Setup the system repeat scripts. They are automatically started
|
||||
by the create_script function.
|
||||
by the create_script function.
|
||||
"""
|
||||
from src.scripts import scripts
|
||||
|
||||
print _(" Creating and starting global scripts ...")
|
||||
|
||||
# check so that all sessions are alive.
|
||||
# check so that all sessions are alive.
|
||||
script1 = create.create_script(scripts.CheckSessions)
|
||||
# validate all scripts in script table.
|
||||
script2 = create.create_script(scripts.ValidateScripts)
|
||||
|
|
@ -140,7 +140,7 @@ def create_system_scripts():
|
|||
script3 = create.create_script(scripts.ValidateChannelHandler)
|
||||
if not script1 or not script2 or not script3:
|
||||
print _(" Error creating system scripts.")
|
||||
|
||||
|
||||
def start_game_time():
|
||||
"""
|
||||
This starts a persistent script that keeps track of the
|
||||
|
|
@ -155,7 +155,7 @@ def start_game_time():
|
|||
def create_admin_media_links():
|
||||
"""
|
||||
This traverses to src/web/media and tries to create a symbolic
|
||||
link to the django media files from within the MEDIA_ROOT.
|
||||
link to the django media files from within the MEDIA_ROOT.
|
||||
These are files we normally don't
|
||||
want to mess with (use templates to customize the admin
|
||||
look). Linking is needed since the Twisted webserver otherwise has no
|
||||
|
|
@ -168,8 +168,8 @@ def create_admin_media_links():
|
|||
apath = os.path.join(settings.ADMIN_MEDIA_ROOT)
|
||||
if os.path.isdir(apath):
|
||||
print _(" ADMIN_MEDIA_ROOT already exists. Ignored.")
|
||||
return
|
||||
if os.name == 'nt':
|
||||
return
|
||||
if os.name == 'nt':
|
||||
print _(" Admin-media files copied to ADMIN_MEDIA_ROOT (Windows mode).")
|
||||
os.mkdir(apath)
|
||||
os.system('xcopy "%s" "%s" /e /q /c' % (dpath, apath))
|
||||
|
|
@ -188,34 +188,34 @@ def at_initial_setup():
|
|||
"""
|
||||
modname = settings.AT_INITIAL_SETUP_HOOK_MODULE
|
||||
if not modname:
|
||||
return
|
||||
try:
|
||||
return
|
||||
try:
|
||||
mod = __import__(modname, fromlist=[None])
|
||||
except ImportError, ValueError:
|
||||
return
|
||||
return
|
||||
print _(" Running at_initial_setup() hook.")
|
||||
if mod.__dict__.get("at_initial_setup", None):
|
||||
mod.at_initial_setup()
|
||||
|
||||
mod.at_initial_setup()
|
||||
|
||||
def handle_setup(last_step):
|
||||
"""
|
||||
Main logic for the module. It allows for restarting
|
||||
the initialization at any point if one of the modules
|
||||
should crash.
|
||||
the initialization at any point if one of the modules
|
||||
should crash.
|
||||
"""
|
||||
|
||||
if last_step < 0:
|
||||
# this means we don't need to handle setup since
|
||||
# it already ran sucessfully once.
|
||||
# it already ran sucessfully once.
|
||||
return
|
||||
elif last_step == None:
|
||||
# config doesn't exist yet. First start of server
|
||||
last_step = 0
|
||||
|
||||
# setting up the list of functions to run
|
||||
|
||||
# setting up the list of functions to run
|
||||
setup_queue = [
|
||||
create_config_values,
|
||||
create_objects,
|
||||
create_config_values,
|
||||
create_objects,
|
||||
create_channels,
|
||||
create_system_scripts,
|
||||
start_game_time,
|
||||
|
|
@ -224,17 +224,17 @@ def handle_setup(last_step):
|
|||
at_initial_setup]
|
||||
|
||||
if not settings.IMPORT_MUX_HELP:
|
||||
# skip importing of the MUX helpfiles, they are
|
||||
# skip importing of the MUX helpfiles, they are
|
||||
# not interesting except for developers.
|
||||
del setup_queue[-2]
|
||||
|
||||
#print " Initial setup: %s steps." % (len(setup_queue))
|
||||
#print " Initial setup: %s steps." % (len(setup_queue))
|
||||
|
||||
# step through queue, from last completed function
|
||||
for num, setup_func in enumerate(setup_queue[last_step:]):
|
||||
for num, setup_func in enumerate(setup_queue[last_step:]):
|
||||
# run the setup function. Note that if there is a
|
||||
# traceback we let it stop the system so the config
|
||||
# step is not saved.
|
||||
# step is not saved.
|
||||
#print "%s..." % num
|
||||
|
||||
try:
|
||||
|
|
@ -255,10 +255,10 @@ def handle_setup(last_step):
|
|||
chan.delete()
|
||||
for conn in PlayerChannelConnection.objects.all():
|
||||
conn.delete()
|
||||
|
||||
|
||||
raise
|
||||
ServerConfig.objects.conf("last_initial_setup_step", last_step + num + 1)
|
||||
|
||||
raise
|
||||
ServerConfig.objects.conf("last_initial_setup_step", last_step + num + 1)
|
||||
# We got through the entire list. Set last_step to -1 so we don't
|
||||
# have to run this again.
|
||||
ServerConfig.objects.conf("last_initial_setup_step", -1)
|
||||
ServerConfig.objects.conf("last_initial_setup_step", -1)
|
||||
|
|
|
|||
|
|
@ -5,17 +5,17 @@ from django.db import models
|
|||
|
||||
class ServerConfigManager(models.Manager):
|
||||
"""
|
||||
This ServerConfigManager implements methods for searching
|
||||
This ServerConfigManager implements methods for searching
|
||||
and manipulating ServerConfigs directly from the database.
|
||||
|
||||
These methods will all return database objects
|
||||
These methods will all return database objects
|
||||
(or QuerySets) directly.
|
||||
|
||||
ServerConfigs are used to store certain persistent settings for the
|
||||
ServerConfigs are used to store certain persistent settings for the
|
||||
server at run-time.
|
||||
|
||||
Evennia-specific:
|
||||
conf
|
||||
conf
|
||||
|
||||
"""
|
||||
def conf(self, key=None, value=None, delete=False, default=None):
|
||||
|
|
@ -27,13 +27,13 @@ class ServerConfigManager(models.Manager):
|
|||
elif delete == True:
|
||||
for conf in self.filter(db_key=key):
|
||||
conf.delete()
|
||||
elif value != None:
|
||||
elif value != None:
|
||||
conf = self.filter(db_key=key)
|
||||
if conf:
|
||||
conf = conf[0]
|
||||
else:
|
||||
conf = self.model(db_key=key)
|
||||
conf.value = value # this will pickle
|
||||
conf = self.model(db_key=key)
|
||||
conf.value = value # this will pickle
|
||||
else:
|
||||
conf = self.filter(db_key=key)
|
||||
if not conf:
|
||||
|
|
|
|||
|
|
@ -12,52 +12,52 @@ 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.
|
||||
mccp_compress and calling it from its write methods.
|
||||
"""
|
||||
import zlib
|
||||
import zlib
|
||||
|
||||
# negotiations for v1 and v2 of the protocol
|
||||
MCCP = chr(86)
|
||||
FLUSH = zlib.Z_SYNC_FLUSH
|
||||
|
||||
def mccp_compress(protocol, data):
|
||||
"Handles zlib compression, if applicable"
|
||||
"Handles zlib compression, if applicable"
|
||||
if hasattr(protocol, 'zlib'):
|
||||
return protocol.zlib.compress(data) + protocol.zlib.flush(FLUSH)
|
||||
return data
|
||||
return data
|
||||
|
||||
class Mccp(object):
|
||||
"""
|
||||
Implements the MCCP protocol. Add this to a
|
||||
Implements the MCCP protocol. Add this to a
|
||||
variable on the telnet protocol to set it up.
|
||||
"""
|
||||
|
||||
def __init__(self, protocol):
|
||||
"""
|
||||
initialize MCCP by storing protocol on
|
||||
ourselves and calling the client to see if
|
||||
it supports MCCP. Sets callbacks to
|
||||
start zlib compression in that case.
|
||||
initialize MCCP by storing protocol on
|
||||
ourselves and calling the client to see if
|
||||
it supports MCCP. Sets callbacks to
|
||||
start zlib compression in that case.
|
||||
"""
|
||||
|
||||
|
||||
self.protocol = protocol
|
||||
self.protocol.protocol_flags['MCCP'] = False
|
||||
self.protocol.protocol_flags['MCCP'] = False
|
||||
# ask if client will mccp, connect callbacks to handle answer
|
||||
self.protocol.will(MCCP).addCallbacks(self.do_mccp, self.no_mccp)
|
||||
|
||||
def no_mccp(self, option):
|
||||
"""
|
||||
If client doesn't support mccp, don't do anything.
|
||||
"""
|
||||
"""
|
||||
if hasattr(self.protocol, 'zlib'):
|
||||
del self.protocol.zlib
|
||||
self.protocol.protocol_flags['MCCP'] = False
|
||||
self.protocol.protocol_flags['MCCP'] = False
|
||||
|
||||
def do_mccp(self, option):
|
||||
"""
|
||||
The client supports MCCP. Set things up by
|
||||
creating a zlib compression stream.
|
||||
"""
|
||||
self.protocol.protocol_flags['MCCP'] = True
|
||||
The client supports MCCP. Set things up by
|
||||
creating a zlib compression stream.
|
||||
"""
|
||||
self.protocol.protocol_flags['MCCP'] = True
|
||||
self.protocol.requestNegotiation(MCCP, '')
|
||||
self.protocol.zlib = zlib.compressobj(9)
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ Server Configuration flags
|
|||
|
||||
This holds persistent server configuration flags.
|
||||
|
||||
Config values should usually be set through the
|
||||
manager's conf() method.
|
||||
Config values should usually be set through the
|
||||
manager's conf() method.
|
||||
|
||||
"""
|
||||
try:
|
||||
|
|
@ -26,10 +26,10 @@ from django.utils.translation import ugettext as _
|
|||
# ServerConfig
|
||||
#
|
||||
#------------------------------------------------------------
|
||||
|
||||
|
||||
class ServerConfig(SharedMemoryModel):
|
||||
"""
|
||||
On-the fly storage of global settings.
|
||||
On-the fly storage of global settings.
|
||||
|
||||
Properties defined on ServerConfig:
|
||||
key - main identifier
|
||||
|
|
@ -50,15 +50,15 @@ class ServerConfig(SharedMemoryModel):
|
|||
db_value = models.TextField(blank=True)
|
||||
|
||||
objects = ServerConfigManager()
|
||||
|
||||
|
||||
# Wrapper properties to easily set database fields. These are
|
||||
# @property decorators that allows to access these fields using
|
||||
# normal python operations (without having to remember to save()
|
||||
# etc). So e.g. a property 'attr' has a get/set/del decorator
|
||||
# defined that allows the user to do self.attr = value,
|
||||
# value = self.attr and del self.attr respectively (where self
|
||||
# defined that allows the user to do self.attr = value,
|
||||
# value = self.attr and del self.attr respectively (where self
|
||||
# is the object in question).
|
||||
|
||||
|
||||
# key property (wraps db_key)
|
||||
#@property
|
||||
def key_get(self):
|
||||
|
|
@ -86,12 +86,12 @@ class ServerConfig(SharedMemoryModel):
|
|||
if utils.has_parent('django.db.models.base.Model', value):
|
||||
# we have to protect against storing db objects.
|
||||
logger.log_errmsg(_("ServerConfig cannot store db objects! (%s)" % value))
|
||||
return
|
||||
return
|
||||
self.db_value = pickle.dumps(value)
|
||||
self.save()
|
||||
#@value.deleter
|
||||
def value_del(self):
|
||||
"Deleter. Allows for del self.value. Deletes entry."
|
||||
"Deleter. Allows for del self.value. Deletes entry."
|
||||
self.delete()
|
||||
value = property(value_get, value_set, value_del)
|
||||
|
||||
|
|
@ -100,8 +100,8 @@ class ServerConfig(SharedMemoryModel):
|
|||
verbose_name = "Server Config value"
|
||||
verbose_name_plural = "Server Config values"
|
||||
|
||||
#
|
||||
# ServerConfig other methods
|
||||
#
|
||||
# ServerConfig other methods
|
||||
#
|
||||
|
||||
def __unicode__(self):
|
||||
|
|
|
|||
|
|
@ -28,14 +28,14 @@ regex_varval = re.compile(r"%s(.*?)%s(.*?)[%s]" % (MSDP_VAR, MSDP_VAL, ENDING))
|
|||
|
||||
class Msdp(object):
|
||||
"""
|
||||
Implements the MSDP protocol.
|
||||
Implements the MSDP protocol.
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self, protocol):
|
||||
"""
|
||||
Initiates by storing the protocol
|
||||
on itself and trying to determine
|
||||
if the client supports MSDP.
|
||||
if the client supports MSDP.
|
||||
"""
|
||||
self.protocol = protocol
|
||||
self.protocol.protocol_FLAGS['MSDP'] = False
|
||||
|
|
@ -45,39 +45,39 @@ class Msdp(object):
|
|||
def no_msdp(self, option):
|
||||
"No msdp"
|
||||
pass
|
||||
|
||||
|
||||
def do_msdp(self, option):
|
||||
"""
|
||||
Called when client confirms that it can do MSDP.
|
||||
Called when client confirms that it can do MSDP.
|
||||
"""
|
||||
self.protocol.protocol_flags['MSDP'] = True
|
||||
|
||||
self.protocol.protocol_flags['MSDP'] = True
|
||||
|
||||
|
||||
def func_to_msdp(self, cmdname, data):
|
||||
"""
|
||||
handle return data from cmdname by converting it to
|
||||
a proper msdp structure. data can either be a single value (will be
|
||||
converted to a string), a list (will be converted to an MSDP_ARRAY),
|
||||
or a dictionary (will be converted to MSDP_TABLE).
|
||||
handle return data from cmdname by converting it to
|
||||
a proper msdp structure. data can either be a single value (will be
|
||||
converted to a string), a list (will be converted to an MSDP_ARRAY),
|
||||
or a dictionary (will be converted to MSDP_TABLE).
|
||||
|
||||
OBS - this supports nested tables and even arrays nested
|
||||
inside tables, as opposed to the receive method. Arrays
|
||||
OBS - this supports nested tables and even arrays nested
|
||||
inside tables, as opposed to the receive method. Arrays
|
||||
cannot hold tables by definition (the table must be named
|
||||
with MSDP_VAR, and an array can only contain MSDP_VALs).
|
||||
with MSDP_VAR, and an array can only contain MSDP_VALs).
|
||||
"""
|
||||
|
||||
def make_table(name, datadict, string):
|
||||
|
||||
def make_table(name, datadict, string):
|
||||
"build a table that may be nested with other tables or arrays."
|
||||
string += MSDP_VAR + name + MSDP_VAL + MSDP_TABLE_OPEN
|
||||
for key, val in datadict.items():
|
||||
for key, val in datadict.items():
|
||||
if type(val) == type({}):
|
||||
string += make_table(key, val, string)
|
||||
elif hasattr(val, '__iter__'):
|
||||
string += make_array(key, val, string)
|
||||
elif hasattr(val, '__iter__'):
|
||||
string += make_array(key, val, string)
|
||||
else:
|
||||
string += MSDP_VAR + key + MSDP_VAL + val
|
||||
string += MSDP_TABLE_CLOSE
|
||||
return string
|
||||
string += MSDP_TABLE_CLOSE
|
||||
return string
|
||||
|
||||
def make_array(name, string, datalist):
|
||||
"build a simple array. Arrays may not nest tables by definition."
|
||||
|
|
@ -85,23 +85,23 @@ class Msdp(object):
|
|||
for val in datalist:
|
||||
string += MSDP_VAL + val
|
||||
string += MSDP_ARRAY_CLOSE
|
||||
return string
|
||||
return string
|
||||
|
||||
if type(data) == type({}):
|
||||
if type(data) == type({}):
|
||||
msdp_string = make_table(cmdname, data, "")
|
||||
elif hasattr(data, '__iter__'):
|
||||
msdp_string = make_array(cmdname, data, "")
|
||||
else:
|
||||
msdp_string = MSDP_VAR + cmdname + MSDP_VAL + data
|
||||
return msdp_string
|
||||
return msdp_string
|
||||
|
||||
def msdp_to_func(self, data):
|
||||
"""
|
||||
Handle a client's requested negotiation, converting
|
||||
it into a function mapping
|
||||
|
||||
OBS-this does not support receiving nested tables
|
||||
from the client at this point!
|
||||
OBS-this does not support receiving nested tables
|
||||
from the client at this point!
|
||||
"""
|
||||
tables = {}
|
||||
arrays = {}
|
||||
|
|
@ -112,23 +112,23 @@ class Msdp(object):
|
|||
for array in regex_array.findall(data):
|
||||
arrays[array[0]] = dict(regex_varval(array[1]))
|
||||
variables = dict(regex._varval(regex_array.sub("", regex_table.sub("", data))))
|
||||
|
||||
|
||||
|
||||
# MSDP Commands
|
||||
# Some given MSDP (varname, value) pairs can also be treated as command + argument.
|
||||
|
||||
# MSDP Commands
|
||||
# Some given MSDP (varname, value) pairs can also be treated as command + argument.
|
||||
# Generic msdp command map. The argument will be sent to the given command.
|
||||
# See http://tintin.sourceforge.net/msdp/ for definitions of each command.
|
||||
# These are client->server commands.
|
||||
# See http://tintin.sourceforge.net/msdp/ for definitions of each command.
|
||||
# These are client->server commands.
|
||||
def msdp_cmd_list(self, arg):
|
||||
"""
|
||||
The List command allows for retrieving various info about the server/client
|
||||
"""
|
||||
"""
|
||||
if arg == 'COMMANDS':
|
||||
return self.func_to_msdp(arg, MSDP_COMMANDS.keys())
|
||||
elif arg == 'LISTS':
|
||||
return self.func_to_msdp(arg, ("COMMANDS", "LISTS",
|
||||
"CONFIGURABLE_VARIABLES",
|
||||
return self.func_to_msdp(arg, ("COMMANDS", "LISTS",
|
||||
"CONFIGURABLE_VARIABLES",
|
||||
"REPORTED_VARIABLES", "SENDABLE_VARIABLES"))
|
||||
elif arg == 'CONFIGURABLE_VARIABLES':
|
||||
return self.func_to_msdp(arg, ("CLIENT_NAME", "CLIENT_VERSION", "PLUGIN_ID"))
|
||||
|
|
@ -149,7 +149,7 @@ class Msdp(object):
|
|||
try:
|
||||
MSDP_REPORTABLE[arg](report=True)
|
||||
except Exception:
|
||||
logger.log_trace()
|
||||
logger.log_trace()
|
||||
|
||||
def msdp_cmd_unreport(self, arg):
|
||||
"""
|
||||
|
|
@ -159,7 +159,7 @@ class Msdp(object):
|
|||
MSDP_REPORTABLE[arg](eport=False)
|
||||
except Exception:
|
||||
logger.log_trace()
|
||||
|
||||
|
||||
def msdp_cmd_reset(self, arg):
|
||||
"""
|
||||
The reset command resets a variable to its initial state.
|
||||
|
|
@ -167,12 +167,12 @@ class Msdp(object):
|
|||
try:
|
||||
MSDP_REPORTABLE[arg](reset=True)
|
||||
except Exception:
|
||||
logger.log_trace()
|
||||
logger.log_trace()
|
||||
|
||||
def msdp_cmd_send(self, arg):
|
||||
"""
|
||||
Request the server to send a particular variable
|
||||
to the client.
|
||||
to the client.
|
||||
|
||||
arg - this is a list of variables the client wants.
|
||||
"""
|
||||
|
|
@ -181,8 +181,8 @@ class Msdp(object):
|
|||
try:
|
||||
ret.append(MSDP_REPORTABLE[arg](send=True))
|
||||
except Exception:
|
||||
logger.log_trace()
|
||||
return ret
|
||||
logger.log_trace()
|
||||
return ret
|
||||
|
||||
MSDP_COMMANDS = {
|
||||
"LIST": self.msdp_list,
|
||||
|
|
@ -192,57 +192,57 @@ class Msdp(object):
|
|||
"UNREPORT":"mspd_unreport"
|
||||
}
|
||||
|
||||
# MSDP_MAP is a standard suggestions for making it easy to create generic guis.
|
||||
# MSDP_MAP is a standard suggestions for making it easy to create generic guis.
|
||||
# this maps MSDP command names to Evennia commands found in OOB_FUNC_MODULE. It
|
||||
# is up to these commands to return data on proper form.
|
||||
# is up to these commands to return data on proper form.
|
||||
MSDP_REPORTABLE = {
|
||||
# General
|
||||
"CHARACTER_NAME": "get_character_name",
|
||||
"SERVER_ID": "get_server_id",
|
||||
"SERVER_TIME": "get_server_time",
|
||||
|
||||
# Character
|
||||
|
||||
# Character
|
||||
"AFFECTS": "char_affects",
|
||||
"ALIGNMENT": "char_alignment",
|
||||
"EXPERIENCE": "char_experience",
|
||||
"EXPERIENCE_MAX": "char_experience_max",
|
||||
"EXPERIENCE_TNL": "char_experience_tnl",
|
||||
"HEALTH": "char_health",
|
||||
"HEALTH_MAX": "char_health_max",
|
||||
"LEVEL": "char_level",
|
||||
"HEALTH": "char_health",
|
||||
"HEALTH_MAX": "char_health_max",
|
||||
"LEVEL": "char_level",
|
||||
"RACE": "char_race",
|
||||
"CLASS": "char_class",
|
||||
"MANA": "char_mana",
|
||||
"CLASS": "char_class",
|
||||
"MANA": "char_mana",
|
||||
"MANA_MAX": "char_mana_max",
|
||||
"WIMPY": "char_wimpy",
|
||||
"PRACTICE": "char_practice",
|
||||
"MONEY": "char_money",
|
||||
"WIMPY": "char_wimpy",
|
||||
"PRACTICE": "char_practice",
|
||||
"MONEY": "char_money",
|
||||
"MOVEMENT": "char_movement",
|
||||
"MOVEMENT_MAX": "char_movement_max",
|
||||
"HITROLL": "char_hitroll",
|
||||
"DAMROLL": "char_damroll",
|
||||
"HITROLL": "char_hitroll",
|
||||
"DAMROLL": "char_damroll",
|
||||
"AC": "char_ac",
|
||||
"STR": "char_str",
|
||||
"INT": "char_int",
|
||||
"STR": "char_str",
|
||||
"INT": "char_int",
|
||||
"WIS": "char_wis",
|
||||
"DEX": "char_dex",
|
||||
"CON": "char_con",
|
||||
|
||||
# Combat
|
||||
"DEX": "char_dex",
|
||||
"CON": "char_con",
|
||||
|
||||
# Combat
|
||||
"OPPONENT_HEALTH": "opponent_health",
|
||||
"OPPONENT_HEALTH_MAX":"opponent_health_max",
|
||||
"OPPONENT_LEVEL": "opponent_level",
|
||||
"OPPONENT_NAME": "opponent_name",
|
||||
|
||||
# World
|
||||
# World
|
||||
"AREA_NAME": "area_name",
|
||||
"ROOM_EXITS": "area_room_exits",
|
||||
"ROOM_NAME": "room_name",
|
||||
"ROOM_VNUM": "room_dbref",
|
||||
"ROOM_NAME": "room_name",
|
||||
"ROOM_VNUM": "room_dbref",
|
||||
"WORLD_TIME": "world_time",
|
||||
|
||||
# Configurable variables
|
||||
"CLIENT_ID": "client_id",
|
||||
|
||||
# Configurable variables
|
||||
"CLIENT_ID": "client_id",
|
||||
"CLIENT_VERSION": "client_version",
|
||||
"PLUGIN_ID": "plugin_id",
|
||||
"ANSI_COLORS": "ansi_colours",
|
||||
|
|
@ -250,13 +250,13 @@ class Msdp(object):
|
|||
"UTF_8": "utf_8",
|
||||
"SOUND": "sound",
|
||||
"MXP": "mxp",
|
||||
|
||||
# GUI variables
|
||||
|
||||
# GUI variables
|
||||
"BUTTON_1": "button1",
|
||||
"BUTTON_2": "button2",
|
||||
"BUTTON_3": "button3",
|
||||
"BUTTON_4": "button4",
|
||||
"BUTTON_5": "button5",
|
||||
"BUTTON_5": "button5",
|
||||
"GAUGE_1": "gauge1",
|
||||
"GAUGE_2": "gauge2",
|
||||
"GAUGE_3": "gauge3",
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ 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.
|
||||
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
|
||||
|
||||
|
|
@ -19,23 +19,23 @@ MSSP_VAR = chr(1)
|
|||
MSSP_VAL = chr(2)
|
||||
|
||||
|
||||
# try to get the customized mssp info, if it exists.
|
||||
MSSPTable_CUSTOM = utils.variable_from_module(settings.MSSP_META_MODULE, "MSSPTable", default={})
|
||||
# try to get the customized mssp info, if it exists.
|
||||
MSSPTable_CUSTOM = utils.variable_from_module(settings.MSSP_META_MODULE, "MSSPTable", default={})
|
||||
|
||||
class Mssp(object):
|
||||
"""
|
||||
Implements the MSSP protocol. Add this to a
|
||||
variable on the telnet protocol to set it up.
|
||||
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.
|
||||
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())
|
||||
|
|
@ -52,31 +52,31 @@ class Mssp(object):
|
|||
|
||||
def do_mssp(self, option):
|
||||
"""
|
||||
Negotiate all the information.
|
||||
Negotiate all the information.
|
||||
"""
|
||||
|
||||
self.mssp_table = {
|
||||
|
||||
# Required fields
|
||||
# Required fields
|
||||
|
||||
"NAME": "Evennia",
|
||||
"PLAYERS": self.get_player_count,
|
||||
"UPTIME" : self.get_uptime,
|
||||
"PLAYERS": self.get_player_count,
|
||||
"UPTIME" : self.get_uptime,
|
||||
|
||||
# Generic
|
||||
|
||||
"CRAWL DELAY": "-1",
|
||||
|
||||
"HOSTNAME": "", # current or new hostname
|
||||
"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.
|
||||
"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
|
||||
"MINIMUM AGE": "0", # set to 0 if not applicable
|
||||
"WEBSITE": "www.evennia.com",
|
||||
|
||||
# Categorisation
|
||||
|
|
@ -88,14 +88,14 @@ class Mssp(object):
|
|||
# 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,
|
||||
"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",
|
||||
"AREAS": "0",
|
||||
"HELPFILES": "0",
|
||||
"MOBILES": "0",
|
||||
"OBJECTS": "0",
|
||||
"ROOMS": "0", # use 0 if room-less
|
||||
|
|
@ -128,7 +128,7 @@ class Mssp(object):
|
|||
"HIRING BUILDERS": "0",
|
||||
"HIRING CODERS": "0",
|
||||
|
||||
# Extended variables
|
||||
# Extended variables
|
||||
|
||||
# World
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
"""
|
||||
This module implements the main Evennia server process, the core of
|
||||
the game engine.
|
||||
the game engine.
|
||||
|
||||
This module should be started with the 'twistd' executable since it
|
||||
sets up all the networking features. (this is done automatically
|
||||
|
|
@ -30,7 +30,7 @@ if os.name == 'nt':
|
|||
from django.utils.translation import ugettext as _
|
||||
|
||||
#------------------------------------------------------------
|
||||
# Evennia Portal settings
|
||||
# Evennia Portal settings
|
||||
#------------------------------------------------------------
|
||||
|
||||
VERSION = get_evennia_version()
|
||||
|
|
@ -53,15 +53,15 @@ TELNET_ENABLED = settings.TELNET_ENABLED and TELNET_PORTS and TELNET_INTERFACES
|
|||
SSL_ENABLED = settings.SSL_ENABLED and SSL_PORTS and SSL_INTERFACES
|
||||
SSH_ENABLED = settings.SSH_ENABLED and SSH_PORTS and SSH_INTERFACES
|
||||
WEBSERVER_ENABLED = settings.WEBSERVER_ENABLED and WEBSERVER_PORTS and WEBSERVER_INTERFACES
|
||||
WEBCLIENT_ENABLED = settings.WEBCLIENT_ENABLED
|
||||
WEBCLIENT_ENABLED = settings.WEBCLIENT_ENABLED
|
||||
|
||||
AMP_HOST = settings.AMP_HOST
|
||||
AMP_PORT = settings.AMP_PORT
|
||||
AMP_ENABLED = AMP_HOST and AMP_PORT
|
||||
AMP_ENABLED = AMP_HOST and AMP_PORT
|
||||
|
||||
|
||||
#------------------------------------------------------------
|
||||
# Portal Service object
|
||||
# Portal Service object
|
||||
#------------------------------------------------------------
|
||||
class Portal(object):
|
||||
|
||||
|
|
@ -69,17 +69,17 @@ class Portal(object):
|
|||
The main Portal server handler. This object sets up the database and
|
||||
tracks and interlinks all the twisted network services that make up
|
||||
Portal.
|
||||
"""
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, application):
|
||||
"""
|
||||
Setup the server.
|
||||
Setup the server.
|
||||
|
||||
application - an instantiated Twisted application
|
||||
|
||||
"""
|
||||
"""
|
||||
sys.path.append('.')
|
||||
|
||||
|
||||
# create a store of services
|
||||
self.services = service.IServiceCollection(application)
|
||||
self.amp_protocol = None # set by amp factory
|
||||
|
|
@ -88,25 +88,25 @@ class Portal(object):
|
|||
|
||||
print '\n' + '-'*50
|
||||
|
||||
# Make info output to the terminal.
|
||||
# Make info output to the terminal.
|
||||
self.terminal_output()
|
||||
|
||||
print '-'*50
|
||||
print '-'*50
|
||||
|
||||
# set a callback if the server is killed abruptly,
|
||||
# set a callback if the server is killed abruptly,
|
||||
# by Ctrl-C, reboot etc.
|
||||
reactor.addSystemEventTrigger('before', 'shutdown', self.shutdown, _abrupt=True)
|
||||
|
||||
self.game_running = False
|
||||
|
||||
|
||||
def terminal_output(self):
|
||||
"""
|
||||
Outputs server startup info to the terminal.
|
||||
"""
|
||||
print _(' %(servername)s Portal (%(version)s) started.') % {'servername': SERVERNAME, 'version': VERSION}
|
||||
print _(' %(servername)s Portal (%(version)s) started.') % {'servername': SERVERNAME, 'version': VERSION}
|
||||
if AMP_ENABLED:
|
||||
print " amp (Server): %s" % AMP_PORT
|
||||
if TELNET_ENABLED:
|
||||
if TELNET_ENABLED:
|
||||
ports = ", ".join([str(port) for port in TELNET_PORTS])
|
||||
ifaces = ",".join([" %s" % iface for iface in TELNET_INTERFACES if iface != '0.0.0.0'])
|
||||
print " telnet%s: %s" % (ifaces, ports)
|
||||
|
|
@ -129,11 +129,11 @@ class Portal(object):
|
|||
def set_restart_mode(self, mode=None):
|
||||
"""
|
||||
This manages the flag file that tells the runner if the server should
|
||||
be restarted or is shutting down. Valid modes are True/False and None.
|
||||
be restarted or is shutting down. Valid modes are True/False and None.
|
||||
If mode is None, no change will be done to the flag file.
|
||||
"""
|
||||
if mode == None:
|
||||
return
|
||||
return
|
||||
f = open(PORTAL_RESTART, 'w')
|
||||
print _("writing mode=%(mode)s to %(portal_restart)s") % {'mode': mode, 'portal_restart': PORTAL_RESTART}
|
||||
f.write(str(mode))
|
||||
|
|
@ -141,13 +141,13 @@ class Portal(object):
|
|||
|
||||
def shutdown(self, restart=None, _abrupt=False):
|
||||
"""
|
||||
Shuts down the server from inside it.
|
||||
Shuts down the server from inside it.
|
||||
|
||||
restart - True/False sets the flags so the server will be
|
||||
restarted or not. If None, the current flag setting
|
||||
(set at initialization or previous runs) is used.
|
||||
_abrupt - this is set if server is stopped by a kill command,
|
||||
in which case the reactor is dead anyway.
|
||||
in which case the reactor is dead anyway.
|
||||
|
||||
Note that restarting (regardless of the setting) will not work
|
||||
if the Portal is currently running in daemon mode. In that
|
||||
|
|
@ -157,7 +157,7 @@ class Portal(object):
|
|||
if not _abrupt:
|
||||
reactor.callLater(0, reactor.stop)
|
||||
if os.name == 'nt' and os.path.exists(PORTAL_PIDFILE):
|
||||
# for Windows we need to remove pid files manually
|
||||
# for Windows we need to remove pid files manually
|
||||
os.remove(PORTAL_PIDFILE)
|
||||
|
||||
#------------------------------------------------------------
|
||||
|
|
@ -170,15 +170,15 @@ class Portal(object):
|
|||
# what to execute from.
|
||||
application = service.Application('Portal')
|
||||
|
||||
# The main Portal server program. This sets up the database
|
||||
# The main Portal server program. This sets up the database
|
||||
# and is where we store all the other services.
|
||||
PORTAL = Portal(application)
|
||||
|
||||
if AMP_ENABLED:
|
||||
if AMP_ENABLED:
|
||||
|
||||
# The AMP protocol handles the communication between
|
||||
# the portal and the mud server. Only reason to ever deactivate
|
||||
# it would be during testing and debugging.
|
||||
# it would be during testing and debugging.
|
||||
|
||||
from src.server import amp
|
||||
|
||||
|
|
@ -188,7 +188,7 @@ if AMP_ENABLED:
|
|||
PORTAL.services.addService(amp_client)
|
||||
|
||||
# We group all the various services under the same twisted app.
|
||||
# These will gradually be started as they are initialized below.
|
||||
# These will gradually be started as they are initialized below.
|
||||
|
||||
if TELNET_ENABLED:
|
||||
|
||||
|
|
@ -200,7 +200,7 @@ if TELNET_ENABLED:
|
|||
ifacestr = ""
|
||||
if interface != '0.0.0.0' or len(TELNET_INTERFACES) > 1:
|
||||
ifacestr = "-%s" % interface
|
||||
for port in TELNET_PORTS:
|
||||
for port in TELNET_PORTS:
|
||||
pstring = "%s:%s" % (ifacestr, port)
|
||||
factory = protocol.ServerFactory()
|
||||
factory.protocol = telnet.TelnetProtocol
|
||||
|
|
@ -219,7 +219,7 @@ if SSL_ENABLED:
|
|||
ifacestr = ""
|
||||
if interface != '0.0.0.0' or len(SSL_INTERFACES) > 1:
|
||||
ifacestr = "-%s" % interface
|
||||
for port in SSL_PORTS:
|
||||
for port in SSL_PORTS:
|
||||
pstring = "%s:%s" % (ifacestr, port)
|
||||
factory = protocol.ServerFactory()
|
||||
factory.sessionhandler = PORTAL_SESSIONS
|
||||
|
|
@ -231,7 +231,7 @@ if SSL_ENABLED:
|
|||
if SSH_ENABLED:
|
||||
|
||||
# Start SSH game connections. Will create a keypair in evennia/game if necessary.
|
||||
|
||||
|
||||
from src.server import ssh
|
||||
|
||||
for interface in SSH_INTERFACES:
|
||||
|
|
@ -242,7 +242,7 @@ if SSH_ENABLED:
|
|||
pstring = "%s:%s" % (ifacestr, port)
|
||||
factory = ssh.makeFactory({'protocolFactory':ssh.SshProtocol,
|
||||
'protocolArgs':(),
|
||||
'sessions':PORTAL_SESSIONS})
|
||||
'sessions':PORTAL_SESSIONS})
|
||||
ssh_service = internet.TCPServer(port, factory, interface=interface)
|
||||
ssh_service.setName('EvenniaSSH%s' % pstring)
|
||||
PORTAL.services.addService(ssh_service)
|
||||
|
|
@ -254,14 +254,14 @@ if WEBSERVER_ENABLED:
|
|||
from twisted.python import threadpool
|
||||
from src.server.webserver import DjangoWebRoot, WSGIWebServer
|
||||
|
||||
# start a thread pool and define the root url (/) as a wsgi resource
|
||||
# start a thread pool and define the root url (/) as a wsgi resource
|
||||
# recognized by Django
|
||||
threads = threadpool.ThreadPool()
|
||||
web_root = DjangoWebRoot(threads)
|
||||
# point our media resources to url /media
|
||||
web_root.putChild("media", static.File(settings.MEDIA_ROOT))
|
||||
# point our media resources to url /media
|
||||
web_root.putChild("media", static.File(settings.MEDIA_ROOT))
|
||||
|
||||
if WEBCLIENT_ENABLED:
|
||||
if WEBCLIENT_ENABLED:
|
||||
# create ajax client processes at /webclientdata
|
||||
from src.server.webclient import WebClient
|
||||
webclient = WebClient()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
"""
|
||||
This module implements the main Evennia server process, the core of
|
||||
the game engine.
|
||||
the game engine.
|
||||
|
||||
This module should be started with the 'twistd' executable since it
|
||||
sets up all the networking features. (this is done automatically
|
||||
|
|
@ -19,7 +19,7 @@ if os.name == 'nt':
|
|||
from twisted.application import internet, service
|
||||
from twisted.internet import protocol, reactor, defer
|
||||
from twisted.web import server, static
|
||||
import django
|
||||
import django
|
||||
from django.db import connection
|
||||
from django.conf import settings
|
||||
|
||||
|
|
@ -38,20 +38,20 @@ if os.name == 'nt':
|
|||
# a file with a flag telling the server to restart after shutdown or not.
|
||||
SERVER_RESTART = os.path.join(settings.GAME_DIR, 'server.restart')
|
||||
|
||||
# module containing hook methods
|
||||
# module containing hook methods
|
||||
SERVER_HOOK_MODULE = mod_import(settings.AT_SERVER_STARTSTOP_MODULE)
|
||||
|
||||
# i18n
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
#------------------------------------------------------------
|
||||
# Evennia Server settings
|
||||
# Evennia Server settings
|
||||
#------------------------------------------------------------
|
||||
|
||||
SERVERNAME = settings.SERVERNAME
|
||||
VERSION = get_evennia_version()
|
||||
|
||||
AMP_ENABLED = True
|
||||
AMP_ENABLED = True
|
||||
AMP_HOST = settings.AMP_HOST
|
||||
AMP_PORT = settings.AMP_PORT
|
||||
|
||||
|
|
@ -61,7 +61,7 @@ IRC_ENABLED = settings.IRC_ENABLED
|
|||
RSS_ENABLED = settings.RSS_ENABLED
|
||||
|
||||
#------------------------------------------------------------
|
||||
# Evennia Main Server object
|
||||
# Evennia Main Server object
|
||||
#------------------------------------------------------------
|
||||
class Evennia(object):
|
||||
|
||||
|
|
@ -69,15 +69,15 @@ class Evennia(object):
|
|||
The main Evennia server handler. This object sets up the database and
|
||||
tracks and interlinks all the twisted network services that make up
|
||||
evennia.
|
||||
"""
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, application):
|
||||
"""
|
||||
Setup the server.
|
||||
Setup the server.
|
||||
|
||||
application - an instantiated Twisted application
|
||||
|
||||
"""
|
||||
"""
|
||||
sys.path.append('.')
|
||||
|
||||
# create a store of services
|
||||
|
|
@ -85,44 +85,44 @@ class Evennia(object):
|
|||
self.amp_protocol = None # set by amp factory
|
||||
self.sessions = SESSIONS
|
||||
self.sessions.server = self
|
||||
|
||||
|
||||
print '\n' + '-'*50
|
||||
|
||||
# Database-specific startup optimizations.
|
||||
self.sqlite3_prep()
|
||||
|
||||
# Run the initial setup if needed
|
||||
|
||||
# Run the initial setup if needed
|
||||
self.run_initial_setup()
|
||||
|
||||
self.start_time = time.time()
|
||||
|
||||
# initialize channelhandler
|
||||
channelhandler.CHANNELHANDLER.update()
|
||||
|
||||
# Make info output to the terminal.
|
||||
|
||||
# Make info output to the terminal.
|
||||
self.terminal_output()
|
||||
|
||||
print '-'*50
|
||||
print '-'*50
|
||||
|
||||
# set a callback if the server is killed abruptly,
|
||||
# set a callback if the server is killed abruptly,
|
||||
# by Ctrl-C, reboot etc.
|
||||
reactor.addSystemEventTrigger('before', 'shutdown', self.shutdown, _abrupt=True)
|
||||
|
||||
self.game_running = True
|
||||
|
||||
self.run_init_hooks()
|
||||
|
||||
|
||||
# Server startup methods
|
||||
|
||||
def sqlite3_prep(self):
|
||||
"""
|
||||
Optimize some SQLite stuff at startup since we
|
||||
can't save it to the database.
|
||||
"""
|
||||
"""
|
||||
if ((".".join(str(i) for i in django.VERSION) < "1.2" and settings.DATABASE_ENGINE == "sqlite3")
|
||||
or (hasattr(settings, 'DATABASES')
|
||||
and settings.DATABASES.get("default", {}).get('ENGINE', None)
|
||||
== 'django.db.backends.sqlite3')):
|
||||
or (hasattr(settings, 'DATABASES')
|
||||
and settings.DATABASES.get("default", {}).get('ENGINE', None)
|
||||
== 'django.db.backends.sqlite3')):
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("PRAGMA cache_size=10000")
|
||||
cursor.execute("PRAGMA synchronous=OFF")
|
||||
|
|
@ -147,7 +147,7 @@ class Evennia(object):
|
|||
# the last failed module. When all are finished, the step
|
||||
# is set to -1 to show it does not need to be run again.
|
||||
print _(' Resuming initial setup from step %(last)s.' % \
|
||||
{'last': last_initial_setup_step})
|
||||
{'last': last_initial_setup_step})
|
||||
initial_setup.handle_setup(int(last_initial_setup_step))
|
||||
print '-'*50
|
||||
|
||||
|
|
@ -164,25 +164,25 @@ class Evennia(object):
|
|||
|
||||
# call server hook.
|
||||
if SERVER_HOOK_MODULE:
|
||||
SERVER_HOOK_MODULE.at_server_start()
|
||||
SERVER_HOOK_MODULE.at_server_start()
|
||||
|
||||
def terminal_output(self):
|
||||
"""
|
||||
Outputs server startup info to the terminal.
|
||||
"""
|
||||
print _(' %(servername)s Server (%(version)s) started.') % {'servername': SERVERNAME, 'version': VERSION}
|
||||
print _(' %(servername)s Server (%(version)s) started.') % {'servername': SERVERNAME, 'version': VERSION}
|
||||
print ' amp (Portal): %s' % AMP_PORT
|
||||
|
||||
def set_restart_mode(self, mode=None):
|
||||
"""
|
||||
This manages the flag file that tells the runner if the server is
|
||||
reloading, resetting or shutting down. Valid modes are
|
||||
'reload', 'reset', 'shutdown' and None.
|
||||
reloading, resetting or shutting down. Valid modes are
|
||||
'reload', 'reset', 'shutdown' and None.
|
||||
If mode is None, no change will be done to the flag file.
|
||||
|
||||
Either way, the active restart setting (Restart=True/False) is
|
||||
Either way, the active restart setting (Restart=True/False) is
|
||||
returned so the server knows which more it's in.
|
||||
"""
|
||||
"""
|
||||
if mode == None:
|
||||
if os.path.exists(SERVER_RESTART) and 'True' == open(SERVER_RESTART, 'r').read():
|
||||
mode = 'reload'
|
||||
|
|
@ -197,17 +197,17 @@ class Evennia(object):
|
|||
|
||||
def shutdown(self, mode=None, _abrupt=False):
|
||||
"""
|
||||
Shuts down the server from inside it.
|
||||
Shuts down the server from inside it.
|
||||
|
||||
mode - sets the server restart mode.
|
||||
mode - sets the server restart mode.
|
||||
'reload' - server restarts, no "persistent" scripts are stopped, at_reload hooks called.
|
||||
'reset' - server restarts, non-persistent scripts stopped, at_shutdown hooks called.
|
||||
'shutdown' - like reset, but server will not auto-restart.
|
||||
None - keep currently set flag from flag file.
|
||||
None - keep currently set flag from flag file.
|
||||
_abrupt - this is set if server is stopped by a kill command,
|
||||
in which case the reactor is dead anyway.
|
||||
in which case the reactor is dead anyway.
|
||||
"""
|
||||
mode = self.set_restart_mode(mode)
|
||||
mode = self.set_restart_mode(mode)
|
||||
|
||||
# call shutdown hooks on all cached objects
|
||||
|
||||
|
|
@ -217,7 +217,7 @@ class Evennia(object):
|
|||
|
||||
if mode == 'reload':
|
||||
# call restart hooks
|
||||
[(o.typeclass, o.at_server_reload()) for o in ObjectDB.get_all_cached_instances()]
|
||||
[(o.typeclass, o.at_server_reload()) for o in ObjectDB.get_all_cached_instances()]
|
||||
[(p.typeclass, p.at_server_reload()) for p in PlayerDB.get_all_cached_instances()]
|
||||
[(s.typeclass, s.pause(), s.at_server_reload()) for s in ScriptDB.get_all_cached_instances()]
|
||||
|
||||
|
|
@ -226,23 +226,23 @@ class Evennia(object):
|
|||
else:
|
||||
if mode == 'reset':
|
||||
# don't call disconnect hooks on reset
|
||||
[(o.typeclass, o.at_server_shutdown()) for o in ObjectDB.get_all_cached_instances()]
|
||||
[(o.typeclass, o.at_server_shutdown()) for o in ObjectDB.get_all_cached_instances()]
|
||||
else: # shutdown
|
||||
[(o.typeclass, o.at_disconnect(), o.at_server_shutdown()) for o in ObjectDB.get_all_cached_instances()]
|
||||
[(o.typeclass, o.at_disconnect(), o.at_server_shutdown()) for o in ObjectDB.get_all_cached_instances()]
|
||||
|
||||
[(p.typeclass, p.at_server_shutdown()) for p in PlayerDB.get_all_cached_instances()]
|
||||
[(s.typeclass, s.at_server_shutdown()) for s in ScriptDB.get_all_cached_instances()]
|
||||
|
||||
[(s.typeclass, s.at_server_shutdown()) for s in ScriptDB.get_all_cached_instances()]
|
||||
|
||||
ServerConfig.objects.conf("server_restart_mode", "reset")
|
||||
|
||||
|
||||
if not _abrupt:
|
||||
if SERVER_HOOK_MODULE:
|
||||
SERVER_HOOK_MODULE.at_server_stop()
|
||||
reactor.callLater(0, reactor.stop)
|
||||
if os.name == 'nt' and os.path.exists(SERVER_PIDFILE):
|
||||
# for Windows we need to remove pid files manually
|
||||
# for Windows we need to remove pid files manually
|
||||
os.remove(SERVER_PIDFILE)
|
||||
|
||||
|
||||
#------------------------------------------------------------
|
||||
#
|
||||
# Start the Evennia game server and add all active services
|
||||
|
|
@ -250,21 +250,21 @@ class Evennia(object):
|
|||
#------------------------------------------------------------
|
||||
|
||||
# Tell the system the server is starting up; some things are not available yet
|
||||
ServerConfig.objects.conf("server_starting_mode", True)
|
||||
ServerConfig.objects.conf("server_starting_mode", True)
|
||||
|
||||
# twistd requires us to define the variable 'application' so it knows
|
||||
# what to execute from.
|
||||
application = service.Application('Evennia')
|
||||
|
||||
# The main evennia server program. This sets up the database
|
||||
# The main evennia server program. This sets up the database
|
||||
# and is where we store all the other services.
|
||||
EVENNIA = Evennia(application)
|
||||
|
||||
# The AMP protocol handles the communication between
|
||||
# the portal and the mud server. Only reason to ever deactivate
|
||||
# it would be during testing and debugging.
|
||||
# it would be during testing and debugging.
|
||||
|
||||
if AMP_ENABLED:
|
||||
if AMP_ENABLED:
|
||||
|
||||
from src.server import amp
|
||||
|
||||
|
|
@ -278,7 +278,7 @@ if IRC_ENABLED:
|
|||
|
||||
# IRC channel connections
|
||||
|
||||
from src.comms import irc
|
||||
from src.comms import irc
|
||||
irc.connect_all()
|
||||
|
||||
if IMC2_ENABLED:
|
||||
|
|
@ -289,10 +289,10 @@ if IMC2_ENABLED:
|
|||
imc2.connect_all()
|
||||
|
||||
if RSS_ENABLED:
|
||||
|
||||
# RSS feed channel connections
|
||||
|
||||
# RSS feed channel connections
|
||||
from src.comms import rss
|
||||
rss.connect_all()
|
||||
rss.connect_all()
|
||||
|
||||
# clear server startup mode
|
||||
ServerConfig.objects.conf("server_starting_mode", delete=True)
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
"""
|
||||
This defines a the Server's generic session object. This object represents
|
||||
a connection to the outside world but don't know any details about how the
|
||||
This defines a the Server's generic session object. This object represents
|
||||
a connection to the outside world but don't know any details about how the
|
||||
connection actually happens (so it's the same for telnet, web, ssh etc).
|
||||
|
||||
It is stored on the Server side (as opposed to protocol-specific sessions which
|
||||
are stored on the Portal side)
|
||||
"""
|
||||
|
||||
import time
|
||||
import time
|
||||
from datetime import datetime
|
||||
from django.conf import settings
|
||||
from src.scripts.models import ScriptDB
|
||||
|
|
@ -16,7 +16,7 @@ from src.utils import logger, utils
|
|||
from src.commands import cmdhandler, cmdsethandler
|
||||
from src.server.session import Session
|
||||
|
||||
IDLE_COMMAND = settings.IDLE_COMMAND
|
||||
IDLE_COMMAND = settings.IDLE_COMMAND
|
||||
|
||||
# load optional out-of-band function module
|
||||
OOB_FUNC_MODULE = settings.OOB_FUNC_MODULE
|
||||
|
|
@ -33,14 +33,14 @@ from django.utils.translation import ugettext as _
|
|||
|
||||
class ServerSession(Session):
|
||||
"""
|
||||
This class represents a player's session and is a template for
|
||||
individual protocols to communicate with Evennia.
|
||||
This class represents a player's session and is a template for
|
||||
individual protocols to communicate with Evennia.
|
||||
|
||||
Each player gets a session assigned to them whenever they connect
|
||||
to the game server. All communication between game and player goes
|
||||
through their session.
|
||||
|
||||
"""
|
||||
"""
|
||||
def at_sync(self):
|
||||
"""
|
||||
This is called whenever a session has been resynced with the portal.
|
||||
|
|
@ -48,13 +48,13 @@ class ServerSession(Session):
|
|||
been assigned (if applicable).
|
||||
|
||||
Since this is often called after a server restart we need to set up
|
||||
the session as it was.
|
||||
the session as it was.
|
||||
"""
|
||||
if not self.logged_in:
|
||||
# assign the unloggedin-command set.
|
||||
self.cmdset = cmdsethandler.CmdSetHandler(self)
|
||||
self.cmdset_storage = [settings.CMDSET_UNLOGGEDIN]
|
||||
self.cmdset.update(init_mode=True)
|
||||
self.cmdset.update(init_mode=True)
|
||||
self.cmdset.update(init_mode=True)
|
||||
return
|
||||
|
||||
|
|
@ -80,29 +80,29 @@ class ServerSession(Session):
|
|||
self.uname = self.user.username
|
||||
self.logged_in = True
|
||||
self.conn_time = time.time()
|
||||
|
||||
|
||||
# Update account's last login time.
|
||||
self.user.last_login = datetime.now()
|
||||
self.user.save()
|
||||
|
||||
self.user.last_login = datetime.now()
|
||||
self.user.save()
|
||||
|
||||
# player init
|
||||
#print "at_init() - player"
|
||||
player.at_init()
|
||||
|
||||
# Check if this is the first time the *player* logs in
|
||||
|
||||
# Check if this is the first time the *player* logs in
|
||||
if player.db.FIRST_LOGIN:
|
||||
player.at_first_login()
|
||||
del player.db.FIRST_LOGIN
|
||||
player.at_pre_login()
|
||||
player.at_pre_login()
|
||||
|
||||
character = player.character
|
||||
if character:
|
||||
if character:
|
||||
# this player has a character. Check if it's the
|
||||
# first time *this character* logs in
|
||||
character.at_init()
|
||||
if character.db.FIRST_LOGIN:
|
||||
character.at_first_login()
|
||||
del character.db.FIRST_LOGIN
|
||||
del character.db.FIRST_LOGIN
|
||||
# run character login hook
|
||||
character.at_pre_login()
|
||||
|
||||
|
|
@ -110,12 +110,12 @@ class ServerSession(Session):
|
|||
|
||||
# start (persistent) scripts on this object
|
||||
ScriptDB.objects.validate(obj=self.player.character)
|
||||
|
||||
|
||||
#add session to connected list
|
||||
self.sessionhandler.login(self)
|
||||
|
||||
# post-login hooks
|
||||
player.at_post_login()
|
||||
# post-login hooks
|
||||
player.at_post_login()
|
||||
if character:
|
||||
character.at_post_login()
|
||||
|
||||
|
|
@ -125,15 +125,15 @@ class ServerSession(Session):
|
|||
accounting. This method is used also for non-loggedin
|
||||
accounts.
|
||||
"""
|
||||
if self.logged_in:
|
||||
if self.logged_in:
|
||||
player = self.get_player()
|
||||
character = self.get_character()
|
||||
if character:
|
||||
character.at_disconnect()
|
||||
uaccount = player.user
|
||||
uaccount.last_login = datetime.now()
|
||||
uaccount.save()
|
||||
self.logged_in = False
|
||||
uaccount.save()
|
||||
self.logged_in = False
|
||||
self.sessionhandler.disconnect(self)
|
||||
|
||||
def get_player(self):
|
||||
|
|
@ -143,8 +143,8 @@ class ServerSession(Session):
|
|||
if self.logged_in:
|
||||
return self.player
|
||||
else:
|
||||
return None
|
||||
|
||||
return None
|
||||
|
||||
def get_character(self):
|
||||
"""
|
||||
Returns the in-game character associated with this session.
|
||||
|
|
@ -153,12 +153,12 @@ class ServerSession(Session):
|
|||
player = self.get_player()
|
||||
if player:
|
||||
return player.character
|
||||
return None
|
||||
return None
|
||||
|
||||
def log(self, message, channel=True):
|
||||
"""
|
||||
Emits session info to the appropriate outputs and info channels.
|
||||
"""
|
||||
"""
|
||||
if channel:
|
||||
try:
|
||||
cchan = settings.CHANNEL_CONNECTINFO
|
||||
|
|
@ -171,7 +171,7 @@ class ServerSession(Session):
|
|||
def update_session_counters(self, idle=False):
|
||||
"""
|
||||
Hit this when the user enters a command in order to update idle timers
|
||||
and command counters.
|
||||
and command counters.
|
||||
"""
|
||||
# Store the timestamp of the user's last command.
|
||||
self.cmd_last = time.time()
|
||||
|
|
@ -187,24 +187,24 @@ class ServerSession(Session):
|
|||
"""
|
||||
# handle the 'idle' command
|
||||
if str(command_string).strip() == IDLE_COMMAND:
|
||||
self.update_session_counters(idle=True)
|
||||
return
|
||||
|
||||
self.update_session_counters(idle=True)
|
||||
return
|
||||
|
||||
# all other inputs, including empty inputs
|
||||
character = self.get_character()
|
||||
|
||||
character = self.get_character()
|
||||
|
||||
|
||||
if character:
|
||||
character.execute_cmd(command_string)
|
||||
else:
|
||||
if self.logged_in:
|
||||
# there is no character, but we are logged in. Use player instead.
|
||||
self.get_player().execute_cmd(command_string)
|
||||
else:
|
||||
# we are not logged in. Use the session directly
|
||||
self.get_player().execute_cmd(command_string)
|
||||
else:
|
||||
# we are not logged in. Use the session directly
|
||||
# (it uses the settings.UNLOGGEDIN cmdset)
|
||||
cmdhandler.cmdhandler(self, command_string)
|
||||
self.update_session_counters()
|
||||
self.update_session_counters()
|
||||
|
||||
def data_out(self, msg, data=None):
|
||||
"""
|
||||
|
|
@ -218,25 +218,25 @@ class ServerSession(Session):
|
|||
This receives out-of-band data from the Portal.
|
||||
|
||||
This method parses the data input (a dict) and uses
|
||||
it to launch correct methods from those plugged into
|
||||
the system.
|
||||
|
||||
it to launch correct methods from those plugged into
|
||||
the system.
|
||||
|
||||
data = {funcname: ( [args], {kwargs]),
|
||||
funcname: ( [args], {kwargs}), ...}
|
||||
|
||||
example:
|
||||
example:
|
||||
data = {"get_hp": ([], {}),
|
||||
"update_counter", (["counter1"], {"now":True}) }
|
||||
"""
|
||||
|
||||
print "server: "
|
||||
outdata = {}
|
||||
|
||||
|
||||
entity = self.get_character()
|
||||
if not entity:
|
||||
entity = self.get_player()
|
||||
if not entity:
|
||||
entity = self
|
||||
entity = self
|
||||
|
||||
for funcname, argtuple in data.items():
|
||||
# loop through the data, calling available functions.
|
||||
|
|
@ -262,19 +262,19 @@ class ServerSession(Session):
|
|||
def __eq__(self, other):
|
||||
return self.address == other.address
|
||||
|
||||
|
||||
|
||||
def __str__(self):
|
||||
"""
|
||||
String representation of the user session class. We use
|
||||
this a lot in the server logs.
|
||||
"""
|
||||
symbol = ""
|
||||
if self.logged_in and hasattr(self, "player") and self.player:
|
||||
symbol = "(#%s)" % self.player.id
|
||||
if self.logged_in and hasattr(self, "player") and self.player:
|
||||
symbol = "(#%s)" % self.player.id
|
||||
try:
|
||||
address = ":".join([str(part) for part in self.address])
|
||||
address = ":".join([str(part) for part in self.address])
|
||||
except Exception:
|
||||
address = self.address
|
||||
address = self.address
|
||||
return "%s%s@%s" % (self.uname, symbol, address)
|
||||
|
||||
def __unicode__(self):
|
||||
|
|
@ -298,7 +298,7 @@ class ServerSession(Session):
|
|||
|
||||
|
||||
# Dummy API hooks for use a non-loggedin operation
|
||||
|
||||
|
||||
def at_cmdset_get(self):
|
||||
"dummy hook all objects with cmdsets need to have"
|
||||
pass
|
||||
|
|
@ -306,11 +306,11 @@ class ServerSession(Session):
|
|||
# Mock db/ndb properties for allowing easy storage on the session
|
||||
# (note that no databse is involved at all here. session.db.attr =
|
||||
# value just saves a normal property in memory, just like ndb).
|
||||
|
||||
|
||||
#@property
|
||||
def ndb_get(self):
|
||||
"""
|
||||
A non-persistent store (ndb: NonDataBase). Everything stored
|
||||
A non-persistent store (ndb: NonDataBase). Everything stored
|
||||
to this is guaranteed to be cleared when a server is shutdown.
|
||||
Syntax is same as for the _get_db_holder() method and
|
||||
property, e.g. obj.ndb.attr = value etc.
|
||||
|
|
@ -321,14 +321,14 @@ class ServerSession(Session):
|
|||
class NdbHolder(object):
|
||||
"Holder for storing non-persistent attributes."
|
||||
def all(self):
|
||||
return [val for val in self.__dict__.keys()
|
||||
if not val.startswith['_']]
|
||||
return [val for val in self.__dict__.keys()
|
||||
if not val.startswith['_']]
|
||||
def __getattribute__(self, key):
|
||||
# return None if no matching attribute was found.
|
||||
# return None if no matching attribute was found.
|
||||
try:
|
||||
return object.__getattribute__(self, key)
|
||||
except AttributeError:
|
||||
return None
|
||||
return None
|
||||
self._ndb_holder = NdbHolder()
|
||||
return self._ndb_holder
|
||||
#@ndb.setter
|
||||
|
|
@ -348,4 +348,4 @@ class ServerSession(Session):
|
|||
# at this stage, so we just present a uniform API)
|
||||
def access(self, *args, **kwargs):
|
||||
"Dummy method."
|
||||
return True
|
||||
return True
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
"""
|
||||
This defines a generic session class. All connection instances (both
|
||||
on Portal and Server side) should inherit from this class.
|
||||
This defines a generic session class. All connection instances (both
|
||||
on Portal and Server side) should inherit from this class.
|
||||
|
||||
"""
|
||||
|
||||
import time
|
||||
|
||||
import time
|
||||
|
||||
#------------------------------------------------------------
|
||||
# Server Session
|
||||
|
|
@ -21,27 +21,27 @@ class Session(object):
|
|||
protocols that Evennia supports, like Telnet, SSH etc. The Portal session
|
||||
must call init_session() as part of its initialization. The respective
|
||||
hook methods should be connected to the methods unique for the respective
|
||||
protocol so that there is a unified interface to Evennia.
|
||||
protocol so that there is a unified interface to Evennia.
|
||||
2) A Server session. This is the same for all connected players, regardless
|
||||
of how they connect.
|
||||
of how they connect.
|
||||
|
||||
The Portal and Server have their own respective sessionhandlers. These are synced
|
||||
whenever new connections happen or the Server restarts etc, which means much of the
|
||||
same information must be stored in both places e.g. the portal can re-sync with the
|
||||
server when the server reboots.
|
||||
server when the server reboots.
|
||||
|
||||
"""
|
||||
|
||||
# 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',
|
||||
'server_data']
|
||||
|
||||
_attrs_to_sync = ['protocol_key', 'address', 'suid', 'sessid', 'uid', 'uname',
|
||||
'logged_in', 'cid', 'encoding',
|
||||
'conn_time', 'cmd_last', 'cmd_last_visible', 'cmd_total',
|
||||
'server_data']
|
||||
|
||||
def init_session(self, protocol_key, address, sessionhandler):
|
||||
"""
|
||||
Initialize the Session. This should be called by the protocol when
|
||||
a new session is established.
|
||||
a new session is established.
|
||||
protocol_key - telnet, ssh, ssl or web
|
||||
address - client address
|
||||
sessionhandler - reference to the sessionhandler instance
|
||||
|
|
@ -50,24 +50,24 @@ class Session(object):
|
|||
self.protocol_key = protocol_key
|
||||
# Protocol address tied to this session
|
||||
self.address = address
|
||||
|
||||
|
||||
# suid is used by some protocols, it's a hex key.
|
||||
self.suid = None
|
||||
|
||||
# unique id for this session
|
||||
self.suid = None
|
||||
|
||||
# unique id for this session
|
||||
self.sessid = 0 # no sessid yet
|
||||
# database id for the user connected to this session
|
||||
self.uid = None
|
||||
# user name, for easier tracking of sessions
|
||||
self.uname = None
|
||||
self.uname = None
|
||||
# if user has authenticated already or not
|
||||
self.logged_in = False
|
||||
|
||||
# database id of character/object connected to this player session (if any)
|
||||
self.cid = None
|
||||
self.cid = None
|
||||
self.encoding = "utf-8"
|
||||
|
||||
# session time statistics
|
||||
|
||||
# session time statistics
|
||||
self.conn_time = time.time()
|
||||
self.cmd_last_visible = self.conn_time
|
||||
self.cmd_last = self.conn_time
|
||||
|
|
@ -76,10 +76,10 @@ class Session(object):
|
|||
self.protocol_flags = {}
|
||||
self.server_data = {}
|
||||
|
||||
# a back-reference to the relevant sessionhandler this
|
||||
# session is stored in.
|
||||
# a back-reference to the relevant sessionhandler this
|
||||
# session is stored in.
|
||||
self.sessionhandler = sessionhandler
|
||||
|
||||
|
||||
def get_sync_data(self):
|
||||
"""
|
||||
Return all data relevant to sync the session
|
||||
|
|
@ -93,15 +93,15 @@ class Session(object):
|
|||
"""
|
||||
Takes a session dictionary, as created by get_sync_data,
|
||||
and loads it into the correct attributes of the session.
|
||||
"""
|
||||
"""
|
||||
for attrname, value in sessdata.items():
|
||||
self.__dict__[attrname] = value
|
||||
|
||||
self.__dict__[attrname] = value
|
||||
|
||||
def at_sync(self):
|
||||
"""
|
||||
Called after a session has been fully synced (including
|
||||
secondary operations such as setting self.player based
|
||||
on uid etc).
|
||||
secondary operations such as setting self.player based
|
||||
on uid etc).
|
||||
"""
|
||||
pass
|
||||
|
||||
|
|
@ -112,13 +112,13 @@ class Session(object):
|
|||
generic hook called from the outside to disconnect this session
|
||||
should be connected to the protocols actual disconnect mechanism.
|
||||
"""
|
||||
pass
|
||||
pass
|
||||
|
||||
def data_out(self, msg, data=None):
|
||||
"""
|
||||
generic hook for sending data out through the protocol. Server
|
||||
protocols can use this right away. Portal sessions
|
||||
should overload this to format/handle the outgoing data as needed.
|
||||
generic hook for sending data out through the protocol. Server
|
||||
protocols can use this right away. Portal sessions
|
||||
should overload this to format/handle the outgoing data as needed.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
|
@ -126,8 +126,8 @@ class Session(object):
|
|||
"""
|
||||
hook for protocols to send incoming data to the engine.
|
||||
"""
|
||||
pass
|
||||
|
||||
pass
|
||||
|
||||
def oob_data_out(self, data):
|
||||
"""
|
||||
for Portal, this receives out-of-band data from Server across the AMP.
|
||||
|
|
@ -141,7 +141,7 @@ class Session(object):
|
|||
"""
|
||||
for Portal, this sends out-of-band requests to Server over the AMP.
|
||||
for Server, this receives data from Portal.
|
||||
|
||||
|
||||
data is a dictionary
|
||||
"""
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -1,33 +1,33 @@
|
|||
"""
|
||||
This module defines handlers for storing sessions when handles
|
||||
sessions of users connecting to the server.
|
||||
This module defines handlers for storing sessions when handles
|
||||
sessions of users connecting to the server.
|
||||
|
||||
There are two similar but separate stores of sessions:
|
||||
ServerSessionHandler - this stores generic game sessions
|
||||
ServerSessionHandler - this stores generic game sessions
|
||||
for the game. These sessions has no knowledge about
|
||||
how they are connected to the world.
|
||||
how they are connected to the world.
|
||||
PortalSessionHandler - this stores sessions created by
|
||||
twisted protocols. These are dumb connectors that
|
||||
handle network communication but holds no game info.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
import time
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from src.server.models import ServerConfig
|
||||
from src.utils import utils
|
||||
from src.utils import utils
|
||||
|
||||
from src.commands.cmdhandler import CMD_LOGINSTART
|
||||
|
||||
# AMP signals
|
||||
# AMP signals
|
||||
PCONN = chr(1) # portal session connect
|
||||
PDISCONN = chr(2) # portal session disconnect
|
||||
PSYNC = chr(3) # portal session sync
|
||||
SLOGIN = chr(4) # server session login
|
||||
SDISCONN = chr(5) # server session disconnect
|
||||
SDISCONN = chr(5) # server session disconnect
|
||||
SDISCONNALL = chr(6) # server session disconnect all
|
||||
SSHUTD = chr(7) # server shutdown
|
||||
SSHUTD = chr(7) # server shutdown
|
||||
SSYNC = chr(8) # server session sync
|
||||
|
||||
# i18n
|
||||
|
|
@ -43,7 +43,7 @@ class SessionHandler(object):
|
|||
"""
|
||||
def __init__(self):
|
||||
"""
|
||||
Init the handler.
|
||||
Init the handler.
|
||||
"""
|
||||
self.sessions = {}
|
||||
|
||||
|
|
@ -64,13 +64,13 @@ class SessionHandler(object):
|
|||
|
||||
def get_all_sync_data(self):
|
||||
"""
|
||||
Create a dictionary of sessdata dicts representing all
|
||||
sessions in store.
|
||||
Create a dictionary of sessdata dicts representing all
|
||||
sessions in store.
|
||||
"""
|
||||
sessdict = {}
|
||||
for sess in self.sessions.values():
|
||||
# copy all relevant data from all sessions
|
||||
sessdict[sess.sessid] = sess.get_sync_data()
|
||||
sessdict[sess.sessid] = sess.get_sync_data()
|
||||
return sessdict
|
||||
|
||||
#------------------------------------------------------------
|
||||
|
|
@ -80,13 +80,13 @@ class SessionHandler(object):
|
|||
class ServerSessionHandler(SessionHandler):
|
||||
"""
|
||||
This object holds the stack of sessions active in the game at
|
||||
any time.
|
||||
any time.
|
||||
|
||||
A session register with the handler in two steps, first by
|
||||
registering itself with the connect() method. This indicates an
|
||||
non-authenticated session. Whenever the session is authenticated
|
||||
the session together with the related player is sent to the login()
|
||||
method.
|
||||
method.
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -94,15 +94,15 @@ class ServerSessionHandler(SessionHandler):
|
|||
|
||||
def __init__(self):
|
||||
"""
|
||||
Init the handler.
|
||||
Init the handler.
|
||||
"""
|
||||
self.sessions = {}
|
||||
self.server = None
|
||||
self.server = None
|
||||
self.server_data = {"servername":settings.SERVERNAME}
|
||||
|
||||
def portal_connect(self, sessid, session):
|
||||
"""
|
||||
Called by Portal when a new session has connected.
|
||||
Called by Portal when a new session has connected.
|
||||
Creates a new, unlogged-in game session.
|
||||
"""
|
||||
self.sessions[sessid] = session
|
||||
|
|
@ -122,10 +122,10 @@ class ServerSessionHandler(SessionHandler):
|
|||
"""
|
||||
Syncing all session ids of the portal with the ones of the server. This is instantiated
|
||||
by the portal when reconnecting.
|
||||
|
||||
|
||||
sesslist is a complete list of (sessid, session) pairs, matching the list on the portal.
|
||||
if session was logged in, the amp handler will have logged them in before this point.
|
||||
"""
|
||||
"""
|
||||
for sess in self.sessions.values():
|
||||
# we delete the old session to make sure to catch eventual lingering references.
|
||||
del sess
|
||||
|
|
@ -136,15 +136,15 @@ class ServerSessionHandler(SessionHandler):
|
|||
def portal_shutdown(self):
|
||||
"""
|
||||
Called by server when shutting down the portal.
|
||||
"""
|
||||
"""
|
||||
self.server.amp_protocol.call_remote_PortalAdmin(0,
|
||||
operation=SSHUTD,
|
||||
data="")
|
||||
# server-side access methods
|
||||
data="")
|
||||
# server-side access methods
|
||||
|
||||
def disconnect(self, session, reason=""):
|
||||
"""
|
||||
Called from server side to remove session and inform portal
|
||||
Called from server side to remove session and inform portal
|
||||
of this fact.
|
||||
"""
|
||||
session = self.sessions.get(session.sessid, None)
|
||||
|
|
@ -157,7 +157,7 @@ class ServerSessionHandler(SessionHandler):
|
|||
data=reason)
|
||||
self.session_count(-1)
|
||||
|
||||
|
||||
|
||||
def login(self, session):
|
||||
"""
|
||||
Log in the previously unloggedin session and the player we by
|
||||
|
|
@ -165,22 +165,22 @@ class ServerSessionHandler(SessionHandler):
|
|||
assume the session to be logged in one way or another.
|
||||
"""
|
||||
# prep the session with player/user info
|
||||
|
||||
|
||||
if not ALLOW_MULTISESSION:
|
||||
# disconnect previous sessions.
|
||||
self.disconnect_duplicate_sessions(session)
|
||||
session.logged_in = True
|
||||
session.logged_in = True
|
||||
self.session_count(1)
|
||||
# sync the portal to this session
|
||||
sessdata = session.get_sync_data()
|
||||
self.server.amp_protocol.call_remote_PortalAdmin(session.sessid,
|
||||
operation=SLOGIN,
|
||||
data=sessdata)
|
||||
|
||||
|
||||
def session_sync(self):
|
||||
"""
|
||||
This is called by the server when it reboots. It syncs all session data
|
||||
to the portal.
|
||||
to the portal.
|
||||
"""
|
||||
sessdata = self.get_all_sync_data()
|
||||
self.server.amp_protocol.call_remote_PortalAdmin(0,
|
||||
|
|
@ -192,7 +192,7 @@ class ServerSessionHandler(SessionHandler):
|
|||
"""
|
||||
Cleanly disconnect all of the connected sessions.
|
||||
"""
|
||||
|
||||
|
||||
for session in self.sessions:
|
||||
del session
|
||||
self.session_count(0)
|
||||
|
|
@ -203,42 +203,42 @@ class ServerSessionHandler(SessionHandler):
|
|||
|
||||
def disconnect_duplicate_sessions(self, curr_session, reason = _("Logged in from elsewhere. Disconnecting.") ):
|
||||
"""
|
||||
Disconnects any existing sessions with the same game object.
|
||||
Disconnects any existing sessions with the same game object.
|
||||
"""
|
||||
curr_char = curr_session.get_character()
|
||||
doublet_sessions = [sess for sess in self.sessions
|
||||
if sess.logged_in
|
||||
if sess.logged_in
|
||||
and sess.get_character() == curr_char
|
||||
and sess != curr_session]
|
||||
for sessid in doublet_sessions:
|
||||
self.disconnect(session, reason)
|
||||
self.disconnect(session, reason)
|
||||
self.session_count(-1)
|
||||
|
||||
|
||||
def validate_sessions(self):
|
||||
"""
|
||||
Check all currently connected sessions (logged in and not)
|
||||
Check all currently connected sessions (logged in and not)
|
||||
and see if any are dead.
|
||||
"""
|
||||
tcurr = time.time()
|
||||
reason= _("Idle timeout exceeded, disconnecting.")
|
||||
for session in (session for session in self.sessions.values()
|
||||
if session.logged_in and IDLE_TIMEOUT > 0
|
||||
for session in (session for session in self.sessions.values()
|
||||
if session.logged_in and IDLE_TIMEOUT > 0
|
||||
and (tcurr - session.cmd_last) > IDLE_TIMEOUT):
|
||||
self.disconnect(session, reason=reason)
|
||||
self.session_count(-1)
|
||||
|
||||
|
||||
def session_count(self, num=None):
|
||||
"""
|
||||
Count up/down the number of connected, authenticated users.
|
||||
Count up/down the number of connected, authenticated users.
|
||||
If num is None, the current number of sessions is returned.
|
||||
|
||||
num can be a positive or negative value to be added to the current count.
|
||||
If 0, the counter will be reset to 0.
|
||||
num can be a positive or negative value to be added to the current count.
|
||||
If 0, the counter will be reset to 0.
|
||||
"""
|
||||
if num == None:
|
||||
# show the current value. This also syncs it.
|
||||
return int(ServerConfig.objects.conf('nr_sessions', default=0))
|
||||
# show the current value. This also syncs it.
|
||||
return int(ServerConfig.objects.conf('nr_sessions', default=0))
|
||||
elif num == 0:
|
||||
# reset value to 0
|
||||
ServerConfig.objects.conf('nr_sessions', 0)
|
||||
|
|
@ -255,7 +255,7 @@ class ServerSessionHandler(SessionHandler):
|
|||
Only logged-in players are counted here.
|
||||
"""
|
||||
return len(set(session.uid for session in self.sessions.values() if session.logged_in))
|
||||
|
||||
|
||||
def sessions_from_player(self, player):
|
||||
"""
|
||||
Given a player, return any matching sessions.
|
||||
|
|
@ -275,7 +275,7 @@ class ServerSessionHandler(SessionHandler):
|
|||
player = character.player
|
||||
if player:
|
||||
return self.sessions_from_player(player)
|
||||
return None
|
||||
return None
|
||||
|
||||
|
||||
def announce_all(self, message):
|
||||
|
|
@ -295,15 +295,15 @@ class ServerSessionHandler(SessionHandler):
|
|||
def data_in(self, sessid, string="", data=""):
|
||||
"""
|
||||
Data Portal -> Server
|
||||
"""
|
||||
session = self.sessions.get(sessid, None)
|
||||
if session:
|
||||
"""
|
||||
session = self.sessions.get(sessid, None)
|
||||
if session:
|
||||
session.execute_cmd(string)
|
||||
|
||||
# ignore 'data' argument for now; this is otherwise the place
|
||||
# to put custom effects on the server due to data input, e.g.
|
||||
# from a custom client.
|
||||
|
||||
# from a custom client.
|
||||
|
||||
def oob_data_in(self, sessid, data):
|
||||
"""
|
||||
OOB (Out-of-band) Data Portal -> Server
|
||||
|
|
@ -327,11 +327,11 @@ 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.
|
||||
connection.
|
||||
|
||||
Sessions register with the handler using the connect() method. This
|
||||
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.
|
||||
to the server using the AMP connection.
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -339,7 +339,7 @@ class PortalSessionHandler(SessionHandler):
|
|||
"""
|
||||
Init the handler
|
||||
"""
|
||||
self.portal = None
|
||||
self.portal = None
|
||||
self.sessions = {}
|
||||
self.latest_sessid = 0
|
||||
self.uptime = time.time()
|
||||
|
|
@ -351,19 +351,19 @@ class PortalSessionHandler(SessionHandler):
|
|||
Server. At this point, the AMP connection is already
|
||||
established.
|
||||
"""
|
||||
self.connection_time = time.time()
|
||||
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.
|
||||
"""
|
||||
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
|
||||
# sync with server-side
|
||||
self.portal.amp_protocol.call_remote_ServerAdmin(sessid,
|
||||
operation=PCONN,
|
||||
data=sessdata)
|
||||
|
|
@ -374,23 +374,23 @@ class PortalSessionHandler(SessionHandler):
|
|||
sessid = session.sessid
|
||||
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)
|
||||
del session
|
||||
session.disconnect(reason)
|
||||
del session
|
||||
|
||||
def server_disconnect_all(self, reason=""):
|
||||
"""
|
||||
Called by server when forcing a clean disconnect for everyone.
|
||||
"""
|
||||
for session in self.sessions.values():
|
||||
for session in self.sessions.values():
|
||||
session.disconnect(reason)
|
||||
del session
|
||||
del session
|
||||
|
||||
|
||||
def count_loggedin(self, include_unloggedin=False):
|
||||
|
|
@ -398,22 +398,22 @@ class PortalSessionHandler(SessionHandler):
|
|||
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)
|
||||
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.
|
||||
"""
|
||||
Called by portal sessions for relaying data coming
|
||||
in from the protocol to the server. data is
|
||||
serialized before passed on.
|
||||
"""
|
||||
self.portal.amp_protocol.call_remote_MsgPortal2Server(session.sessid,
|
||||
msg=string,
|
||||
data=data)
|
||||
|
|
@ -426,12 +426,12 @@ class PortalSessionHandler(SessionHandler):
|
|||
|
||||
def data_out(self, sessid, string="", data=""):
|
||||
"""
|
||||
Called by server for having the portal relay messages and data
|
||||
to the correct session protocol.
|
||||
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)
|
||||
session.data_out(string, data=data)
|
||||
|
||||
def oob_data_in(self, session, data):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ This depends on a generic session module that implements
|
|||
the actual login procedure of the game, tracks
|
||||
sessions etc.
|
||||
|
||||
Using standard ssh client,
|
||||
Using standard ssh client,
|
||||
|
||||
"""
|
||||
import os
|
||||
|
|
@ -65,11 +65,11 @@ class SshProtocol(Manhole, session.Session):
|
|||
|
||||
# initialize the session
|
||||
client_address = self.getClientAddress()
|
||||
self.init_session("ssh", client_address, self.cfactory.sessionhandler)
|
||||
self.init_session("ssh", client_address, self.cfactory.sessionhandler)
|
||||
|
||||
# since we might have authenticated already, we might set this here.
|
||||
# since we might have authenticated already, we might set this here.
|
||||
if self.authenticated_player:
|
||||
self.logged_in = True
|
||||
self.logged_in = True
|
||||
self.uid = self.authenticated_player.user.id
|
||||
self.sessionhandler.connect(self)
|
||||
|
||||
|
|
@ -82,8 +82,8 @@ class SshProtocol(Manhole, session.Session):
|
|||
self.keyHandlers[CTRL_C] = self.handle_INT
|
||||
self.keyHandlers[CTRL_D] = self.handle_EOF
|
||||
self.keyHandlers[CTRL_L] = self.handle_FF
|
||||
self.keyHandlers[CTRL_BACKSLASH] = self.handle_QUIT
|
||||
|
||||
self.keyHandlers[CTRL_BACKSLASH] = self.handle_QUIT
|
||||
|
||||
# initalize
|
||||
|
||||
def handle_INT(self):
|
||||
|
|
@ -128,8 +128,8 @@ class SshProtocol(Manhole, session.Session):
|
|||
def connectionLost(self, reason=None):
|
||||
"""
|
||||
This is executed when the connection is lost for
|
||||
whatever reason. It can also be called directly,
|
||||
from the disconnect method.
|
||||
whatever reason. It can also be called directly,
|
||||
from the disconnect method.
|
||||
|
||||
"""
|
||||
insults.TerminalProtocol.connectionLost(self, reason)
|
||||
|
|
@ -186,7 +186,7 @@ class SshProtocol(Manhole, session.Session):
|
|||
self.lineSend(str(e))
|
||||
return
|
||||
nomarkup = False
|
||||
raw = False
|
||||
raw = False
|
||||
if type(data) == dict:
|
||||
# check if we want escape codes to go through unparsed.
|
||||
raw = data.get("raw", False)
|
||||
|
|
@ -230,7 +230,7 @@ class PlayerDBPasswordChecker(object):
|
|||
username = up.username
|
||||
password = up.password
|
||||
player = PlayerDB.objects.get_player_from_name(username)
|
||||
res = (None, self.factory)
|
||||
res = (None, self.factory)
|
||||
if player and player.user.check_password(password):
|
||||
res = (player, self.factory)
|
||||
return defer.succeed(res)
|
||||
|
|
@ -290,13 +290,13 @@ class TerminalSessionTransport_getPeer:
|
|||
def getKeyPair(pubkeyfile, privkeyfile):
|
||||
"""
|
||||
This function looks for RSA keypair files in the current directory. If they
|
||||
do not exist, the keypair is created.
|
||||
do not exist, the keypair is created.
|
||||
"""
|
||||
|
||||
if not (os.path.exists(pubkeyfile) and os.path.exists(privkeyfile)):
|
||||
# No keypair exists. Generate a new RSA keypair
|
||||
print _(" Generating SSH RSA keypair ..."),
|
||||
from Crypto.PublicKey import RSA
|
||||
from Crypto.PublicKey import RSA
|
||||
|
||||
KEY_LENGTH = 1024
|
||||
rsaKey = Key(RSA.generate(KEY_LENGTH))
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
"""
|
||||
This is a simple context factory for auto-creating
|
||||
SSL keys and certificates.
|
||||
This is a simple context factory for auto-creating
|
||||
SSL keys and certificates.
|
||||
"""
|
||||
|
||||
import os, sys
|
||||
|
|
@ -16,21 +16,21 @@ from src.server.telnet import TelnetProtocol
|
|||
class SSLProtocol(TelnetProtocol):
|
||||
"""
|
||||
Communication is the same as telnet, except data transfer
|
||||
is done with encryption.
|
||||
is done with encryption.
|
||||
"""
|
||||
pass
|
||||
|
||||
def verify_SSL_key_and_cert(keyfile, certfile):
|
||||
"""
|
||||
This function looks for RSA key and certificate in the current
|
||||
directory. If files ssl.key and ssl.cert does not exist, they
|
||||
directory. If files ssl.key and ssl.cert does not exist, they
|
||||
are created.
|
||||
"""
|
||||
|
||||
if not (os.path.exists(keyfile) and os.path.exists(certfile)):
|
||||
# key/cert does not exist. Create.
|
||||
# key/cert does not exist. Create.
|
||||
import subprocess
|
||||
from Crypto.PublicKey import RSA
|
||||
from Crypto.PublicKey import RSA
|
||||
from twisted.conch.ssh.keys import Key
|
||||
|
||||
print _(" Creating SSL key and certificate ... "),
|
||||
|
|
@ -39,16 +39,16 @@ def verify_SSL_key_and_cert(keyfile, certfile):
|
|||
# create the RSA key and store it.
|
||||
KEY_LENGTH = 1024
|
||||
rsaKey = Key(RSA.generate(KEY_LENGTH))
|
||||
keyString = rsaKey.toString(type="OPENSSH")
|
||||
keyString = rsaKey.toString(type="OPENSSH")
|
||||
file(keyfile, 'w+b').write(keyString)
|
||||
except Exception,e:
|
||||
except Exception,e:
|
||||
print _("rsaKey error: %(e)s\n WARNING: Evennia could not auto-generate SSL private key.") % {'e': e}
|
||||
print _("If this error persists, create game/%(keyfile)s yourself using third-party tools.") % {'keyfile': keyfile}
|
||||
sys.exit(5)
|
||||
|
||||
|
||||
# try to create the certificate
|
||||
CERT_EXPIRE = 365 * 20 # twenty years validity
|
||||
# default:
|
||||
CERT_EXPIRE = 365 * 20 # twenty years validity
|
||||
# default:
|
||||
#openssl req -new -x509 -key ssl.key -out ssl.cert -days 7300
|
||||
exestring = "openssl req -new -x509 -key %s -out %s -days %s" % (keyfile, certfile, CERT_EXPIRE)
|
||||
#print "exestring:", exestring
|
||||
|
|
@ -58,9 +58,9 @@ def verify_SSL_key_and_cert(keyfile, certfile):
|
|||
print " %s\n" % e
|
||||
print _(" Evennia's SSL context factory could not automatically create an SSL certificate game/%(cert)s.") % {'cert': certfile}
|
||||
print _(" A private key 'ssl.key' was already created. Please create %(cert)s manually using the commands valid") % {'cert': certfile}
|
||||
print _(" for your operating system.")
|
||||
print _(" for your operating system.")
|
||||
print _(" Example (linux, using the openssl program): ")
|
||||
print " %s" % exestring
|
||||
print " %s" % exestring
|
||||
sys.exit(5)
|
||||
print "done."
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
This module implements the telnet protocol.
|
||||
|
||||
This depends on a generic session module that implements
|
||||
the actual login procedure of the game, tracks
|
||||
sessions etc.
|
||||
the actual login procedure of the game, tracks
|
||||
sessions etc.
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -11,44 +11,44 @@ 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.utils import utils, ansi
|
||||
from src.utils import utils, ansi
|
||||
|
||||
class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||
"""
|
||||
Each player connecting over telnet (ie using most traditional mud
|
||||
clients) gets a telnet protocol instance assigned to them. All
|
||||
communication between game and player goes through here.
|
||||
"""
|
||||
"""
|
||||
def connectionMade(self):
|
||||
"""
|
||||
This is called when the connection is first
|
||||
established.
|
||||
"""
|
||||
This is called when the connection is first
|
||||
established.
|
||||
"""
|
||||
# initialize the session
|
||||
client_address = self.transport.client
|
||||
client_address = self.transport.client
|
||||
self.init_session("telnet", client_address, self.factory.sessionhandler)
|
||||
|
||||
# negotiate mccp (data compression)
|
||||
self.mccp = Mccp(self)
|
||||
|
||||
self.mccp = Mccp(self)
|
||||
|
||||
# negotiate ttype (client info)
|
||||
self.ttype = ttype.Ttype(self)
|
||||
|
||||
# negotiate mssp (crawler communication)
|
||||
self.mssp = mssp.Mssp(self)
|
||||
|
||||
# add this new connection to sessionhandler so
|
||||
# the Server becomes aware of it.
|
||||
self.sessionhandler.connect(self)
|
||||
|
||||
|
||||
def enableRemote(self, option):
|
||||
# add this new connection to sessionhandler so
|
||||
# the Server becomes aware of it.
|
||||
self.sessionhandler.connect(self)
|
||||
|
||||
|
||||
def enableRemote(self, option):
|
||||
"""
|
||||
This sets up the options we allow for this protocol.
|
||||
"""
|
||||
return (option == LINEMODE or
|
||||
option == ttype.TTYPE or
|
||||
option == MCCP or
|
||||
option == MCCP or
|
||||
option == mssp.MSSP)
|
||||
|
||||
def enableLocal(self, option):
|
||||
|
|
@ -60,18 +60,18 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
|||
def disableLocal(self, option):
|
||||
if option == MCCP:
|
||||
self.mccp.no_mccp(option)
|
||||
return True
|
||||
return True
|
||||
else:
|
||||
return super(TelnetProtocol, self).disableLocal(option)
|
||||
|
||||
|
||||
|
||||
|
||||
def connectionLost(self, reason):
|
||||
"""
|
||||
This is executed when the connection is lost for
|
||||
This is executed when the connection is lost for
|
||||
whatever reason. It can also be called directly, from
|
||||
the disconnect method
|
||||
"""
|
||||
self.sessionhandler.disconnect(self)
|
||||
"""
|
||||
self.sessionhandler.disconnect(self)
|
||||
self.transport.loseConnection()
|
||||
|
||||
def dataReceived(self, data):
|
||||
|
|
@ -83,7 +83,7 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
|||
# print "dataRcv:", data,
|
||||
# try:
|
||||
# for b in data:
|
||||
# print ord(b),
|
||||
# print ord(b),
|
||||
# print ""
|
||||
# except Exception, e:
|
||||
# print str(e) + ":", str(data)
|
||||
|
|
@ -91,19 +91,19 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
|||
if data and data[0] == IAC:
|
||||
try:
|
||||
super(TelnetProtocol, self).dataReceived(data)
|
||||
return
|
||||
return
|
||||
except Exception:
|
||||
pass
|
||||
StatefulTelnetProtocol.dataReceived(self, data)
|
||||
|
||||
|
||||
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 data))
|
||||
#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 by linereceiver"
|
||||
"hook overloading the one used by linereceiver"
|
||||
#print "sendLine (%s):\n%s" % (self.state, line)
|
||||
#escape IAC in line mode, and correctly add \r\n
|
||||
line += self.delimiter
|
||||
|
|
@ -112,17 +112,17 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
|||
|
||||
def lineReceived(self, string):
|
||||
"""
|
||||
Telnet method called when data is coming in over the telnet
|
||||
Telnet method called when data is coming in over the telnet
|
||||
connection. We pass it on to the game engine directly.
|
||||
"""
|
||||
"""
|
||||
self.sessionhandler.data_in(self, string)
|
||||
|
||||
|
||||
# Session hooks
|
||||
|
||||
# Session hooks
|
||||
|
||||
def disconnect(self, reason=None):
|
||||
"""
|
||||
generic hook for the engine to call in order to
|
||||
generic hook for the engine to call in order to
|
||||
disconnect this protocol.
|
||||
"""
|
||||
if reason:
|
||||
|
|
@ -131,25 +131,25 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
|||
|
||||
def data_out(self, string, data=None):
|
||||
"""
|
||||
generic hook method for engine to call in order to send data
|
||||
through the telnet connection.
|
||||
Data Evennia -> Player.
|
||||
generic hook method for engine to call in order to send data
|
||||
through the telnet connection.
|
||||
Data Evennia -> Player.
|
||||
data argument may contain a dict with output flags.
|
||||
"""
|
||||
try:
|
||||
string = utils.to_str(string, encoding=self.encoding)
|
||||
except Exception, e:
|
||||
self.sendLine(str(e))
|
||||
return
|
||||
try:
|
||||
string = utils.to_str(string, encoding=self.encoding)
|
||||
except Exception, e:
|
||||
self.sendLine(str(e))
|
||||
return
|
||||
ttype = self.protocol_flags.get('TTYPE', {})
|
||||
nomarkup = not (ttype.get('256 COLORS') or ttype.get('ANSI') or not ttype.get("init_done"))
|
||||
raw = False
|
||||
if type(data) == dict:
|
||||
if type(data) == dict:
|
||||
# check if we want escape codes to go through unparsed.
|
||||
raw = data.get("raw", False)
|
||||
# check if we want to remove all markup (TTYPE override)
|
||||
nomarkup = data.get("nomarkup", False)
|
||||
if raw:
|
||||
self.sendLine(string)
|
||||
if raw:
|
||||
self.sendLine(string)
|
||||
else:
|
||||
self.sendLine(ansi.parse_ansi(string, strip_ansi=nomarkup, xterm256=ttype.get('256 COLORS')))
|
||||
|
|
|
|||
|
|
@ -17,18 +17,18 @@ IS = chr(0)
|
|||
SEND = chr(1)
|
||||
|
||||
# terminal capabilities and their codes
|
||||
MTTS = [(128,'PROXY'),
|
||||
(64, 'SCREEN READER'),
|
||||
(32, 'OSC COLOR PALETTE'),
|
||||
(16, 'MOUSE TRACKING'),
|
||||
(8, '256 COLORS'),
|
||||
MTTS = [(128,'PROXY'),
|
||||
(64, 'SCREEN READER'),
|
||||
(32, 'OSC COLOR PALETTE'),
|
||||
(16, 'MOUSE TRACKING'),
|
||||
(8, '256 COLORS'),
|
||||
(4, 'UTF-8'),
|
||||
(2, 'VT100'),
|
||||
(1, 'ANSI')]
|
||||
|
||||
class Ttype(object):
|
||||
"""
|
||||
Handles ttype negotiations. Called and initiated by the
|
||||
Handles ttype negotiations. Called and initiated by the
|
||||
telnet protocol.
|
||||
"""
|
||||
def __init__(self, protocol):
|
||||
|
|
@ -39,26 +39,26 @@ class Ttype(object):
|
|||
the ttype_step indicates how far in the data retrieval we've
|
||||
gotten.
|
||||
"""
|
||||
self.ttype_step = 0
|
||||
self.ttype_step = 0
|
||||
self.protocol = protocol
|
||||
self.protocol.protocol_flags['TTYPE'] = {"init_done":False}
|
||||
|
||||
# setup protocol to handle ttype initialization and negotiation
|
||||
self.protocol.negotiationMap[TTYPE] = self.do_ttype
|
||||
self.protocol.negotiationMap[TTYPE] = self.do_ttype
|
||||
# ask if client will ttype, connect callback if it does.
|
||||
self.protocol.will(TTYPE).addCallbacks(self.do_ttype, self.no_ttype)
|
||||
|
||||
|
||||
def no_ttype(self, option):
|
||||
"""
|
||||
Callback if ttype is not supported by client.
|
||||
Callback if ttype is not supported by client.
|
||||
"""
|
||||
self.protocol.protocol_flags['TTYPE'] = False
|
||||
self.protocol.protocol_flags['TTYPE'] = False
|
||||
|
||||
def do_ttype(self, option):
|
||||
"""
|
||||
Handles negotiation of the ttype protocol once the
|
||||
client has confirmed that it supports the ttype
|
||||
protocol.
|
||||
Handles negotiation of the ttype protocol once the
|
||||
client has confirmed that it supports the ttype
|
||||
protocol.
|
||||
|
||||
The negotiation proceeds in several steps, each returning a
|
||||
certain piece of information about the client. All data is
|
||||
|
|
@ -66,15 +66,15 @@ class Ttype(object):
|
|||
"""
|
||||
|
||||
if self.protocol.protocol_flags['TTYPE']['init_done']:
|
||||
return
|
||||
return
|
||||
|
||||
self.ttype_step += 1
|
||||
|
||||
if self.ttype_step == 1:
|
||||
# set up info storage and initialize subnegotiation
|
||||
if self.ttype_step == 1:
|
||||
# set up info storage and initialize subnegotiation
|
||||
self.protocol.requestNegotiation(TTYPE, SEND)
|
||||
else:
|
||||
# receive data
|
||||
# receive data
|
||||
option = "".join(option).lstrip(IS)
|
||||
if self.ttype_step == 2:
|
||||
self.protocol.protocol_flags['TTYPE']['CLIENTNAME'] = option
|
||||
|
|
@ -82,17 +82,16 @@ class Ttype(object):
|
|||
elif self.ttype_step == 3:
|
||||
self.protocol.protocol_flags['TTYPE']['TERM'] = option
|
||||
self.protocol.requestNegotiation(TTYPE, SEND)
|
||||
elif self.ttype_step == 4:
|
||||
elif self.ttype_step == 4:
|
||||
option = int(option.strip('MTTS '))
|
||||
self.protocol.protocol_flags['TTYPE']['MTTS'] = option
|
||||
for codenum, standard in MTTS:
|
||||
self.protocol.protocol_flags['TTYPE']['MTTS'] = option
|
||||
for codenum, standard in MTTS:
|
||||
if option == 0:
|
||||
break
|
||||
break
|
||||
status = option % codenum < option
|
||||
self.protocol.protocol_flags['TTYPE'][standard] = status
|
||||
if status:
|
||||
if status:
|
||||
option = option % codenum
|
||||
self.protocol.protocol_flags['TTYPE']['init_done'] = True
|
||||
|
||||
#print "ttype results:", self.protocol.protocol_flags['TTYPE']
|
||||
|
||||
|
|
|
|||
|
|
@ -4,17 +4,17 @@ Web client server resource.
|
|||
The Evennia web client consists of two components running
|
||||
on twisted and django. They are both a part of the Evennia
|
||||
website url tree (so the testing website might be located
|
||||
on http://localhost:8000/, whereas the webclient can be
|
||||
found on http://localhost:8000/webclient.)
|
||||
on http://localhost:8000/, whereas the webclient can be
|
||||
found on http://localhost:8000/webclient.)
|
||||
|
||||
/webclient - this url is handled through django's template
|
||||
system and serves the html page for the client
|
||||
itself along with its javascript chat program.
|
||||
/webclientdata - this url is called by the ajax chat using
|
||||
POST requests (long-polling when necessary)
|
||||
The WebClient resource in this module will
|
||||
handle these requests and act as a gateway
|
||||
to sessions connected over the webclient.
|
||||
The WebClient resource in this module will
|
||||
handle these requests and act as a gateway
|
||||
to sessions connected over the webclient.
|
||||
"""
|
||||
import time
|
||||
from hashlib import md5
|
||||
|
|
@ -25,7 +25,7 @@ from twisted.internet import defer, reactor
|
|||
from django.utils import simplejson
|
||||
from django.utils.functional import Promise
|
||||
from django.utils.encoding import force_unicode
|
||||
from django.conf import settings
|
||||
from django.conf import settings
|
||||
from src.utils import utils, logger, ansi
|
||||
from src.utils.text2html import parse_html
|
||||
from src.server import session
|
||||
|
|
@ -34,7 +34,7 @@ SERVERNAME = settings.SERVERNAME
|
|||
ENCODINGS = settings.ENCODINGS
|
||||
|
||||
# defining a simple json encoder for returning
|
||||
# django data to the client. Might need to
|
||||
# django data to the client. Might need to
|
||||
# extend this if one wants to send more
|
||||
# complex database objects too.
|
||||
|
||||
|
|
@ -51,36 +51,36 @@ def jsonify(obj):
|
|||
# WebClient resource - this is called by the ajax client
|
||||
# using POST requests to /webclientdata.
|
||||
#
|
||||
|
||||
|
||||
class WebClient(resource.Resource):
|
||||
"""
|
||||
An ajax/comet long-polling transport
|
||||
An ajax/comet long-polling transport
|
||||
"""
|
||||
isLeaf = True
|
||||
isLeaf = True
|
||||
allowedMethods = ('POST',)
|
||||
|
||||
def __init__(self):
|
||||
self.requests = {}
|
||||
self.databuffer = {}
|
||||
|
||||
|
||||
def getChild(self, path, request):
|
||||
"""
|
||||
This is the place to put dynamic content.
|
||||
"""
|
||||
return self
|
||||
|
||||
return self
|
||||
|
||||
def _responseFailed(self, failure, suid, request):
|
||||
"callback if a request is lost/timed out"
|
||||
"callback if a request is lost/timed out"
|
||||
try:
|
||||
self.requests.get(suid, []).remove(request)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
pass
|
||||
|
||||
def lineSend(self, suid, string, data=None):
|
||||
"""
|
||||
This adds the data to the buffer and/or sends it to
|
||||
the client as soon as possible.
|
||||
"""
|
||||
"""
|
||||
requests = self.requests.get(suid, None)
|
||||
if requests:
|
||||
request = requests.pop(0)
|
||||
|
|
@ -88,22 +88,22 @@ class WebClient(resource.Resource):
|
|||
request.write(jsonify({'msg':string, 'data':data}))
|
||||
request.finish()
|
||||
self.requests[suid] = requests
|
||||
else:
|
||||
else:
|
||||
# no waiting request. Store data in buffer
|
||||
dataentries = self.databuffer.get(suid, [])
|
||||
dataentries.append(jsonify({'msg':string, 'data':data}))
|
||||
self.databuffer[suid] = dataentries
|
||||
|
||||
|
||||
def client_disconnect(self, suid):
|
||||
"""
|
||||
Disconnect session with given suid.
|
||||
"""
|
||||
"""
|
||||
if self.requests.has_key(suid):
|
||||
for request in self.requests.get(suid, []):
|
||||
request.finish()
|
||||
del self.requests[suid]
|
||||
if self.databuffer.has_key(suid):
|
||||
del self.databuffer[suid]
|
||||
del self.databuffer[suid]
|
||||
|
||||
def mode_init(self, request):
|
||||
"""
|
||||
|
|
@ -120,10 +120,10 @@ class WebClient(resource.Resource):
|
|||
# creating a unique id hash string
|
||||
suid = md5(str(time.time())).hexdigest()
|
||||
self.requests[suid] = []
|
||||
self.databuffer[suid] = []
|
||||
self.databuffer[suid] = []
|
||||
|
||||
sess = WebClientSession()
|
||||
sess.client = self
|
||||
sess.client = self
|
||||
sess.init_session("comet", remote_addr, self.sessionhandler)
|
||||
sess.suid = suid
|
||||
sess.sessionhandler.connect(sess)
|
||||
|
|
@ -154,22 +154,22 @@ class WebClient(resource.Resource):
|
|||
available.
|
||||
"""
|
||||
suid = request.args.get('suid', ['0'])[0]
|
||||
if suid == '0':
|
||||
if suid == '0':
|
||||
return ''
|
||||
|
||||
|
||||
dataentries = self.databuffer.get(suid, [])
|
||||
if dataentries:
|
||||
return dataentries.pop(0)
|
||||
reqlist = self.requests.get(suid, [])
|
||||
request.notifyFinish().addErrback(self._responseFailed, suid, request)
|
||||
reqlist.append(request)
|
||||
reqlist.append(request)
|
||||
self.requests[suid] = reqlist
|
||||
return server.NOT_DONE_YET
|
||||
|
||||
def mode_close(self, request):
|
||||
"""
|
||||
This is called by render_POST when the client is signalling
|
||||
that it is about to be closed.
|
||||
that it is about to be closed.
|
||||
"""
|
||||
suid = request.args.get('suid', ['0'])[0]
|
||||
if suid == '0':
|
||||
|
|
@ -184,8 +184,8 @@ class WebClient(resource.Resource):
|
|||
initializing or sending/receving data through the request. It
|
||||
uses a long-polling mechanism to avoid sending data unless
|
||||
there is actual data available.
|
||||
"""
|
||||
dmode = request.args.get('mode', [None])[0]
|
||||
"""
|
||||
dmode = request.args.get('mode', [None])[0]
|
||||
if dmode == 'init':
|
||||
# startup. Setup the server.
|
||||
return self.mode_init(request)
|
||||
|
|
@ -201,11 +201,11 @@ class WebClient(resource.Resource):
|
|||
else:
|
||||
# this should not happen if client sends valid data.
|
||||
return ''
|
||||
|
||||
|
||||
#
|
||||
# A session type handling communication over the
|
||||
# web client interface.
|
||||
#
|
||||
# A session type handling communication over the
|
||||
# web client interface.
|
||||
#
|
||||
|
||||
class WebClientSession(session.Session):
|
||||
"""
|
||||
|
|
@ -215,38 +215,38 @@ class WebClientSession(session.Session):
|
|||
def disconnect(self, reason=None):
|
||||
"""
|
||||
Disconnect from server
|
||||
"""
|
||||
"""
|
||||
if reason:
|
||||
self.client.lineSend(self.suid, reason)
|
||||
self.client.client_disconnect(self.suid)
|
||||
|
||||
def data_out(self, string='', data=None):
|
||||
"""
|
||||
Data Evennia -> Player access hook.
|
||||
Data Evennia -> Player access hook.
|
||||
|
||||
data argument may be used depending on
|
||||
the client-server implementation.
|
||||
the client-server implementation.
|
||||
"""
|
||||
|
||||
|
||||
if data:
|
||||
# treat data?
|
||||
pass
|
||||
|
||||
# string handling is similar to telnet
|
||||
try:
|
||||
string = utils.to_str(string, encoding=self.encoding)
|
||||
|
||||
string = utils.to_str(string, encoding=self.encoding)
|
||||
|
||||
nomarkup = False
|
||||
raw = False
|
||||
raw = False
|
||||
if type(data) == dict:
|
||||
# check if we want escape codes to go through unparsed.
|
||||
raw = data.get("raw", False)
|
||||
# check if we want to remove all markup
|
||||
nomarkup = data.get("nomarkup", False)
|
||||
# check if we want to remove all markup
|
||||
nomarkup = data.get("nomarkup", False)
|
||||
if raw:
|
||||
self.client.lineSend(self.suid, string)
|
||||
else:
|
||||
self.client.lineSend(self.suid, parse_html(ansi.parse_ansi(string, strip_ansi=nomarkup)))
|
||||
return
|
||||
except Exception, e:
|
||||
return
|
||||
except Exception, e:
|
||||
logger.log_trace()
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ a great example/aid on how to do this.)
|
|||
from twisted.web import resource
|
||||
from twisted.python import threadpool
|
||||
from twisted.internet import reactor
|
||||
from twisted.application import service, internet
|
||||
from twisted.application import service, internet
|
||||
|
||||
from twisted.web.wsgi import WSGIResource
|
||||
from django.core.handlers.wsgi import WSGIHandler
|
||||
|
|
@ -26,19 +26,19 @@ from django.core.handlers.wsgi import WSGIHandler
|
|||
class DjangoWebRoot(resource.Resource):
|
||||
"""
|
||||
This creates a web root (/) that Django
|
||||
understands by tweaking the way the
|
||||
child instancee are recognized.
|
||||
understands by tweaking the way the
|
||||
child instancee are recognized.
|
||||
"""
|
||||
def __init__(self, pool):
|
||||
"""
|
||||
Setup the django+twisted resource
|
||||
"""
|
||||
resource.Resource.__init__(self)
|
||||
resource.Resource.__init__(self)
|
||||
self.wsgi_resource = WSGIResource(reactor, pool , WSGIHandler())
|
||||
|
||||
def getChild(self, path, request):
|
||||
"""
|
||||
To make things work we nudge the
|
||||
To make things work we nudge the
|
||||
url tree to make this the root.
|
||||
"""
|
||||
path0 = request.prepath.pop(0)
|
||||
|
|
@ -62,9 +62,9 @@ class WSGIWebServer(internet.TCPServer):
|
|||
internet.TCPServer.__init__(self, *args, **kwargs)
|
||||
def startService(self):
|
||||
"Start the pool after the service"
|
||||
internet.TCPServer.startService(self)
|
||||
self.pool.start()
|
||||
def stopService(self):
|
||||
internet.TCPServer.startService(self)
|
||||
self.pool.start()
|
||||
def stopService(self):
|
||||
"Safely stop the pool after service stop."
|
||||
internet.TCPServer.stopService(self)
|
||||
internet.TCPServer.stopService(self)
|
||||
self.pool.stop()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue