Reworked the flow of the data property through AMP to fix inconsistencies and make some overall cleanup of the arguments used to send AMP messages. Resolves #815.

This commit is contained in:
Griatch 2015-10-11 18:49:38 +02:00
parent 6a11da1714
commit 1348fae5a0
3 changed files with 62 additions and 78 deletions

View file

@ -249,7 +249,7 @@ class MsgPortal2Server(amp.Command):
""" """
key = "MsgPortal2Server" key = "MsgPortal2Server"
arguments = [('data', Compressed())] arguments = [('packed_data', Compressed())]
errors = [(Exception, 'EXCEPTION')] errors = [(Exception, 'EXCEPTION')]
response = [] response = []
@ -260,7 +260,7 @@ class MsgServer2Portal(amp.Command):
""" """
key = "MsgServer2Portal" key = "MsgServer2Portal"
arguments = [('data', Compressed())] arguments = [('packed_data', Compressed())]
errors = [(Exception, 'EXCEPTION')] errors = [(Exception, 'EXCEPTION')]
response = [] response = []
@ -274,7 +274,7 @@ class AdminPortal2Server(amp.Command):
""" """
key = "AdminPortal2Server" key = "AdminPortal2Server"
arguments = [('data', Compressed())] arguments = [('packed_data', Compressed())]
errors = [(Exception, 'EXCEPTION')] errors = [(Exception, 'EXCEPTION')]
response = [] response = []
@ -288,7 +288,7 @@ class AdminServer2Portal(amp.Command):
""" """
key = "AdminServer2Portal" key = "AdminServer2Portal"
arguments = [('data', Compressed())] arguments = [('packed_data', Compressed())]
errors = [(Exception, 'EXCEPTION')] errors = [(Exception, 'EXCEPTION')]
response = [] response = []
@ -361,7 +361,7 @@ class AMPProtocol(amp.AMP):
sessdata = self.factory.portal.sessions.get_all_sync_data() sessdata = self.factory.portal.sessions.get_all_sync_data()
self.send_AdminPortal2Server(0, self.send_AdminPortal2Server(0,
PSYNC, PSYNC,
data=sessdata) sessiondata=sessdata)
self.factory.portal.sessions.at_server_connection() self.factory.portal.sessions.at_server_connection()
if hasattr(self.factory, "server_restart_mode"): if hasattr(self.factory, "server_restart_mode"):
del self.factory.server_restart_mode del self.factory.server_restart_mode
@ -398,53 +398,49 @@ class AMPProtocol(amp.AMP):
(sessid, kwargs). (sessid, kwargs).
""" """
batch = dumps((sessid, kwargs))
return self.callRemote(command, return self.callRemote(command,
data=batch).addErrback(self.errback, command.key) packed_data=dumps((sessid, kwargs))
).addErrback(self.errback, command.key)
# Message definition + helper methods to call/create each message type # Message definition + helper methods to call/create each message type
# Portal -> Server Msg # Portal -> Server Msg
@MsgPortal2Server.responder @MsgPortal2Server.responder
def server_receive_msgportal2server(self, data): def server_receive_msgportal2server(self, packed_data):
""" """
Receives message arriving to server. This method is executed Receives message arriving to server. This method is executed
on the Server. on the Server.
Args: Args:
data (str): Data to receive (a pickled tuple (sessid,kwargs)) packed_data (str): Data to receive (a pickled tuple (sessid,kwargs))
""" """
sessid, kwargs = loads(data) sessid, kwargs = loads(packed_data)
#print "msg portal -> server (server side):", sessid, msg, loads(ret["data"]) #print "msg portal -> server (server side):", sessid, msg, loads(ret["data"])
self.factory.server.sessions.data_in(sessid, self.factory.server.sessions.data_in(sessid, **kwargs)
text=kwargs["msg"],
data=kwargs["data"])
return {} return {}
def send_MsgPortal2Server(self, sessid, msg="", data=""): def send_MsgPortal2Server(self, sessid, text="", **kwargs):
""" """
Access method called by the Portal and executed on the Portal. Access method called by the Portal and executed on the Portal.
Args: Args:
sessid (int): Unique Session id. sessid (int): Unique Session id.
msg (str): Message to send over the wire. msg (str): Message to send over the wire.
data (str, optional): Optional data. kwargs (any, optional): Optional data.
Returns: Returns:
deferred (Deferred): Asynchronous return. deferred (Deferred): Asynchronous return.
""" """
#print "msg portal->server (portal side):", sessid, msg, data #print "msg portal->server (portal side):", sessid, msg, data
return self.send_data(MsgPortal2Server, sessid, return self.send_data(MsgPortal2Server, sessid, text=text, **kwargs)
msg=msg,
data=data)
# Server -> Portal message # Server -> Portal message
@MsgServer2Portal.responder @MsgServer2Portal.responder
def portal_receive_server2portal(self, data): def portal_receive_server2portal(self, packed_data):
""" """
Receives message arriving to Portal from Server. Receives message arriving to Portal from Server.
This method is executed on the Portal. This method is executed on the Portal.
@ -455,17 +451,15 @@ class AMPProtocol(amp.AMP):
before continuing. before continuing.
Args: Args:
data (str): Pickled data (sessid, kwargs) coming over the wire. packed_data (str): Pickled data (sessid, kwargs) coming over the wire.
""" """
sessid, kwargs = loads(data) sessid, kwargs = loads(packed_data)
#print "msg server->portal (portal side):", sessid, ret["text"], loads(ret["data"]) #print "msg server->portal (portal side):", sessid, ret["text"], loads(ret["data"])
self.factory.portal.sessions.data_out(sessid, self.factory.portal.sessions.data_out(sessid, **kwargs)
text=kwargs["msg"],
data=kwargs["data"])
return {} return {}
def send_MsgServer2Portal(self, sessid, msg="", data=""): def send_MsgServer2Portal(self, sessid, text="", **kwargs):
""" """
Access method - executed on the Server for sending data Access method - executed on the Server for sending data
to Portal. to Portal.
@ -473,39 +467,37 @@ class AMPProtocol(amp.AMP):
Args: Args:
sessid (int): Unique Session id. sessid (int): Unique Session id.
msg (str, optional): Message to send over the wire. msg (str, optional): Message to send over the wire.
data (str, optional): Extra data. kwargs (any, optiona): Extra data.
""" """
#print "msg server->portal (server side):", sessid, msg, data #print "msg server->portal (server side):", sessid, msg, data
return self.send_data(MsgServer2Portal, sessid, msg=msg, data=data) return self.send_data(MsgServer2Portal, sessid, text=text, **kwargs)
# Server administration from the Portal side # Server administration from the Portal side
@AdminPortal2Server.responder @AdminPortal2Server.responder
def server_receive_adminportal2server(self, data): def server_receive_adminportal2server(self, packed_data):
""" """
Receives admin data from the Portal (allows the portal to Receives admin data from the Portal (allows the portal to
perform admin operations on the server). This is executed on perform admin operations on the server). This is executed on
the Server. the Server.
Args: Args:
data (str): Data to send (often a part of a batch) packed_data (str): Incoming, pickled data.
""" """
#print "serveradmin (server side):", hashid, ipart, nparts #print "serveradmin (server side):", hashid, ipart, nparts
sessid, kwargs = loads(data) sessid, kwargs = loads(packed_data)
operation = kwargs.pop("operation", "")
operation = kwargs["operation"]
data = kwargs["data"]
server_sessionhandler = self.factory.server.sessions server_sessionhandler = self.factory.server.sessions
#print "serveradmin (server side):", sessid, ord(operation), data #print "serveradmin (server side):", sessid, ord(operation), data
if operation == PCONN: # portal_session_connect if operation == PCONN: # portal_session_connect
# create a new session and sync it # create a new session and sync it
server_sessionhandler.portal_connect(data) server_sessionhandler.portal_connect(kwargs.get("sessiondata"))
elif operation == PCONNSYNC: #portal_session_sync elif operation == PCONNSYNC: #portal_session_sync
server_sessionhandler.portal_session_sync(data) server_sessionhandler.portal_session_sync(kwargs.get("sessiondata"))
elif operation == PDISCONN: # portal_session_disconnect elif operation == PDISCONN: # portal_session_disconnect
# session closed from portal side # session closed from portal side
@ -517,12 +509,12 @@ class AMPProtocol(amp.AMP):
# contains a dict {sessid: {arg1:val1,...}} # contains a dict {sessid: {arg1:val1,...}}
# representing the attributes to sync for each # representing the attributes to sync for each
# session. # session.
server_sessionhandler.portal_sessions_sync(data) server_sessionhandler.portal_sessions_sync(kwargs.get("sessiondata"))
else: else:
raise Exception("operation %(op)s not recognized." % {'op': operation}) raise Exception("operation %(op)s not recognized." % {'op': operation})
return {} return {}
def send_AdminPortal2Server(self, sessid, operation="", data=""): def send_AdminPortal2Server(self, sessid, operation="", **kwargs):
""" """
Send Admin instructions from the Portal to the Server. Send Admin instructions from the Portal to the Server.
Executed Executed
@ -532,42 +524,41 @@ class AMPProtocol(amp.AMP):
sessid (int): Session id. sessid (int): Session id.
operation (char, optional): Identifier for the server operation, as defined by the operation (char, optional): Identifier for the server operation, as defined by the
global variables in `evennia/server/amp.py`. global variables in `evennia/server/amp.py`.
data (str, optional): Data going into the adminstrative operation. data (str or dict, optional): Data used in the administrative operation.
""" """
#print "serveradmin (portal side):", sessid, ord(operation), data #print "serveradmin (portal side):", sessid, ord(operation), data
return self.send_data(AdminPortal2Server, sessid, operation=operation, data=data) return self.send_data(AdminPortal2Server, sessid, operation=operation, **kwargs)
# Portal administraton from the Server side # Portal administraton from the Server side
@AdminServer2Portal.responder @AdminServer2Portal.responder
def portal_receive_adminserver2portal(self, data): def portal_receive_adminserver2portal(self, packed_data):
""" """
Receives and handles admin operations sent to the Portal Receives and handles admin operations sent to the Portal
This is executed on the Portal. This is executed on the Portal.
Args: Args:
data (str): Data received, a pickled tuple (sessid, kwargs). packed_data (str): Data received, a pickled tuple (sessid, kwargs).
""" """
#print "portaladmin (portal side):", sessid, ord(operation), data #print "portaladmin (portal side):", sessid, ord(operation), data
sessid, kwargs = loads(data) sessid, kwargs = loads(packed_data)
operation = kwargs["operation"] operation = kwargs.pop("operation")
data = kwargs["data"]
portal_sessionhandler = self.factory.portal.sessions portal_sessionhandler = self.factory.portal.sessions
if operation == SLOGIN: # server_session_login if operation == SLOGIN: # server_session_login
# a session has authenticated; sync it. # a session has authenticated; sync it.
portal_sessionhandler.server_logged_in(sessid, data) portal_sessionhandler.server_logged_in(sessid, kwargs.get("sessiondata"))
elif operation == SDISCONN: # server_session_disconnect elif operation == SDISCONN: # server_session_disconnect
# the server is ordering to disconnect the session # the server is ordering to disconnect the session
portal_sessionhandler.server_disconnect(sessid, reason=data) portal_sessionhandler.server_disconnect(sessid, reason=kwargs.get("reason"))
elif operation == SDISCONNALL: # server_session_disconnect_all elif operation == SDISCONNALL: # server_session_disconnect_all
# server orders all sessions to disconnect # server orders all sessions to disconnect
portal_sessionhandler.server_disconnect_all(reason=data) portal_sessionhandler.server_disconnect_all(reason=kwargs.get("reason"))
elif operation == SSHUTD: # server_shutdown elif operation == SSHUTD: # server_shutdown
# the server orders the portal to shut down # the server orders the portal to shut down
@ -576,18 +567,18 @@ class AMPProtocol(amp.AMP):
elif operation == SSYNC: # server_session_sync elif operation == SSYNC: # server_session_sync
# server wants to save session data to the portal, # server wants to save session data to the portal,
# maybe because it's about to shut down. # maybe because it's about to shut down.
portal_sessionhandler.server_session_sync(data) portal_sessionhandler.server_session_sync(kwargs.get("sessiondata"))
# set a flag in case we are about to shut down soon # set a flag in case we are about to shut down soon
self.factory.server_restart_mode = True self.factory.server_restart_mode = True
elif operation == SCONN: # server_force_connection (for irc/imc2 etc) elif operation == SCONN: # server_force_connection (for irc/imc2 etc)
portal_sessionhandler.server_connect(**data) portal_sessionhandler.server_connect(**kwargs)
else: else:
raise Exception("operation %(op)s not recognized." % {'op': operation}) raise Exception("operation %(op)s not recognized." % {'op': operation})
return {} return {}
def send_AdminServer2Portal(self, sessid, operation="", data=""): def send_AdminServer2Portal(self, sessid, operation="", **kwargs):
""" """
Administrative access method called by the Server to send an Administrative access method called by the Server to send an
instruction to the Portal. instruction to the Portal.
@ -597,16 +588,15 @@ class AMPProtocol(amp.AMP):
operation (char, optional): Identifier for the server operation (char, optional): Identifier for the server
operation, as defined by the global variables in operation, as defined by the global variables in
`evennia/server/amp.py`. `evennia/server/amp.py`.
data (str, optional): Data going into the adminstrative data (str or dict, optional): Data going into the adminstrative.
operation.
""" """
return self.send_data(AdminServer2Portal, sessid, operation=operation, data=data) return self.send_data(AdminServer2Portal, sessid, operation=operation, **kwargs)
# Extra functions # Extra functions
@FunctionCall.responder @FunctionCall.responder
def receive_functioncall(self, module, function, args, **kwargs): def receive_functioncall(self, module, function, func_args, func_kwargs):
""" """
This allows Portal- and Server-process to call an arbitrary This allows Portal- and Server-process to call an arbitrary
function in the other process. It is intended for use by function in the other process. It is intended for use by
@ -617,12 +607,12 @@ class AMPProtocol(amp.AMP):
`function` to call. `function` to call.
function (str): The name of the function to call in function (str): The name of the function to call in
`module`. `module`.
args, kwargs (any): These will be used as args/kwargs to func_args (str): Pickled args tuple for use in `function` call.
`function`. func_kwargs (str): Pickled kwargs dict for use in `function` call.
""" """
args = loads(args) args = loads(func_args)
kwargs = loads(kwargs) kwargs = loads(func_kwargs)
# call the function (don't catch tracebacks here) # call the function (don't catch tracebacks here)
result = variable_from_module(module, function)(*args, **kwargs) result = variable_from_module(module, function)(*args, **kwargs)

View file

@ -113,7 +113,7 @@ class PortalSessionHandler(SessionHandler):
#print "connecting", session.sessid, " number:", len(self.sessions) #print "connecting", session.sessid, " number:", len(self.sessions)
self.portal.amp_protocol.send_AdminPortal2Server(session.sessid, self.portal.amp_protocol.send_AdminPortal2Server(session.sessid,
operation=PCONN, operation=PCONN,
data=sessdata) sessiondata=sessdata)
def sync(self, session): def sync(self, session):
""" """
@ -142,7 +142,7 @@ class PortalSessionHandler(SessionHandler):
"server_data",)) "server_data",))
self.portal.amp_protocol.send_AdminPortal2Server(session.sessid, self.portal.amp_protocol.send_AdminPortal2Server(session.sessid,
operation=PCONNSYNC, operation=PCONNSYNC,
data=sessdata) sessiondata=sessdata)
def disconnect(self, session): def disconnect(self, session):
""" """
@ -389,7 +389,7 @@ class PortalSessionHandler(SessionHandler):
# data throttle (anti DoS measure) # data throttle (anti DoS measure)
now = time() now = time()
dT = now - self.command_counter_reset dT = now - self.command_counter_reset
print " command rate:", _MAX_COMMAND_RATE / dT, dT, self.command_counter #print " command rate:", _MAX_COMMAND_RATE / dT, dT, self.command_counter
self.command_counter = 0 self.command_counter = 0
self.command_counter_reset = now self.command_counter_reset = now
self.command_overflow = dT < 1.0 self.command_overflow = dT < 1.0
@ -401,8 +401,8 @@ class PortalSessionHandler(SessionHandler):
# relay data to Server # relay data to Server
self.command_counter += 1 self.command_counter += 1
self.portal.amp_protocol.send_MsgPortal2Server(session.sessid, self.portal.amp_protocol.send_MsgPortal2Server(session.sessid,
msg=text, text=text,
data=kwargs) **kwargs)
else: else:
# called by the callLater callback # called by the callLater callback
if self.command_overflow: if self.command_overflow:

View file

@ -282,11 +282,8 @@ class ServerSessionHandler(SessionHandler):
the Server. the Server.
""" """
data = {"protocol_path":protocol_path, self.server.amp_protocol.send_AdminServer2Portal(0, operation=SCONN,
"config":configdict} protocol_path=protocol_path, config=configdict)
self.server.amp_protocol.send_AdminServer2Portal(0,
operation=SCONN,
data=data)
def portal_shutdown(self): def portal_shutdown(self):
""" """
@ -294,8 +291,7 @@ class ServerSessionHandler(SessionHandler):
""" """
self.server.amp_protocol.send_AdminServer2Portal(0, self.server.amp_protocol.send_AdminServer2Portal(0,
operation=SSHUTD, operation=SSHUTD)
data="")
def login(self, session, player, testmode=False): def login(self, session, player, testmode=False):
""" """
@ -338,14 +334,12 @@ class ServerSessionHandler(SessionHandler):
string = "Logged in: {player} {address} ({nsessions} session(s) total)" string = "Logged in: {player} {address} ({nsessions} session(s) total)"
string = string.format(player=player,address=session.address, nsessions=nsess) string = string.format(player=player,address=session.address, nsessions=nsess)
session.log(string) session.log(string)
session.logged_in = True session.logged_in = True
# sync the portal to the session # sync the portal to the session
sessdata = {"logged_in": True}
if not testmode: if not testmode:
self.server.amp_protocol.send_AdminServer2Portal(session.sessid, self.server.amp_protocol.send_AdminServer2Portal(session.sessid,
operation=SLOGIN, operation=SLOGIN,
data=sessdata) sessiondata={"logged_in": True})
player.at_post_login(sessid=session.sessid) player.at_post_login(sessid=session.sessid)
def disconnect(self, session, reason=""): def disconnect(self, session, reason=""):
@ -375,7 +369,7 @@ class ServerSessionHandler(SessionHandler):
# inform portal that session should be closed. # inform portal that session should be closed.
self.server.amp_protocol.send_AdminServer2Portal(sessid, self.server.amp_protocol.send_AdminServer2Portal(sessid,
operation=SDISCONN, operation=SDISCONN,
data=reason) reason=reason)
def all_sessions_portal_sync(self): def all_sessions_portal_sync(self):
""" """
@ -386,7 +380,7 @@ class ServerSessionHandler(SessionHandler):
sessdata = self.get_all_sync_data() sessdata = self.get_all_sync_data()
return self.server.amp_protocol.send_AdminServer2Portal(0, return self.server.amp_protocol.send_AdminServer2Portal(0,
operation=SSYNC, operation=SSYNC,
data=sessdata) sessiondata=sessdata)
def disconnect_all_sessions(self, reason="You have been disconnected."): def disconnect_all_sessions(self, reason="You have been disconnected."):
""" """
@ -402,7 +396,7 @@ class ServerSessionHandler(SessionHandler):
# tell portal to disconnect all sessions # tell portal to disconnect all sessions
self.server.amp_protocol.send_AdminServer2Portal(0, self.server.amp_protocol.send_AdminServer2Portal(0,
operation=SDISCONNALL, operation=SDISCONNALL,
data=reason) reason=reason)
def disconnect_duplicate_sessions(self, curr_session, def disconnect_duplicate_sessions(self, curr_session,
reason=_("Logged in from elsewhere. Disconnecting.")): reason=_("Logged in from elsewhere. Disconnecting.")):
@ -586,8 +580,8 @@ class ServerSessionHandler(SessionHandler):
# send to all found sessions # send to all found sessions
for session in sessions: for session in sessions:
self.server.amp_protocol.send_MsgServer2Portal(sessid=session.sessid, self.server.amp_protocol.send_MsgServer2Portal(sessid=session.sessid,
msg=text, text=text,
data=kwargs) **kwargs)
def data_in(self, sessid, text="", **kwargs): def data_in(self, sessid, text="", **kwargs):
""" """