Add better error-handling for AMP, start design launcer API
This commit is contained in:
parent
b0d545a086
commit
e0d8d8293d
3 changed files with 115 additions and 8 deletions
|
|
@ -126,6 +126,7 @@ class AMPServerClientProtocol(amp.AMPMultiConnectionProtocol):
|
||||||
# receiving AMP data
|
# receiving AMP data
|
||||||
|
|
||||||
@amp.MsgPortal2Server.responder
|
@amp.MsgPortal2Server.responder
|
||||||
|
@amp.catch_traceback
|
||||||
def server_receive_msgportal2server(self, packed_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
|
||||||
|
|
@ -142,6 +143,7 @@ class AMPServerClientProtocol(amp.AMPMultiConnectionProtocol):
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
@amp.AdminPortal2Server.responder
|
@amp.AdminPortal2Server.responder
|
||||||
|
@amp.catch_traceback
|
||||||
def server_receive_adminportal2server(self, packed_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
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ This module acts as a central place for AMP-servers and -clients to get commands
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
from functools import wraps
|
||||||
import time
|
import time
|
||||||
from twisted.protocols import amp
|
from twisted.protocols import amp
|
||||||
from collections import defaultdict, namedtuple
|
from collections import defaultdict, namedtuple
|
||||||
|
|
@ -17,9 +18,10 @@ except ImportError:
|
||||||
import pickle
|
import pickle
|
||||||
|
|
||||||
from twisted.internet.defer import DeferredList, Deferred
|
from twisted.internet.defer import DeferredList, Deferred
|
||||||
from evennia.utils import logger
|
|
||||||
from evennia.utils.utils import to_str, variable_from_module
|
from evennia.utils.utils import to_str, variable_from_module
|
||||||
|
|
||||||
|
# delayed import
|
||||||
|
_LOGGER = None
|
||||||
|
|
||||||
# communication bits
|
# communication bits
|
||||||
# (chr(9) and chr(10) are \t and \n, so skipping them)
|
# (chr(9) and chr(10) are \t and \n, so skipping them)
|
||||||
|
|
@ -30,12 +32,15 @@ PSYNC = chr(3) # portal session sync
|
||||||
SLOGIN = chr(4) # server session login
|
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
|
SDISCONNALL = chr(6) # server session disconnect all
|
||||||
SSHUTD = chr(7) # server shutdown (shutdown portal too)
|
SSHUTD = chr(7) # server shutdown
|
||||||
SSYNC = chr(8) # server session sync
|
SSYNC = chr(8) # server session sync
|
||||||
SCONN = chr(11) # server creating new connection (for irc bots and etc)
|
SCONN = chr(11) # server creating new connection (for irc bots and etc)
|
||||||
PCONNSYNC = chr(12) # portal post-syncing a session
|
PCONNSYNC = chr(12) # portal post-syncing a session
|
||||||
PDISCONNALL = chr(13) # portal session disconnect all
|
PDISCONNALL = chr(13) # portal session disconnect all
|
||||||
SRELOAD = chr(14) # server reloading (have portal start a new server)
|
SRELOAD = chr(14) # server reloading (have portal start a new server)
|
||||||
|
PSTART = chr(15) # server+portal start
|
||||||
|
PSHUTD = chr(16) # portal (+server) shutdown
|
||||||
|
PPING = chr(17) # server or portal status
|
||||||
|
|
||||||
AMP_MAXLEN = amp.MAX_VALUE_LENGTH # max allowed data length in AMP protocol (cannot be changed)
|
AMP_MAXLEN = amp.MAX_VALUE_LENGTH # max allowed data length in AMP protocol (cannot be changed)
|
||||||
BATCH_RATE = 250 # max commands/sec before switching to batch-sending
|
BATCH_RATE = 250 # max commands/sec before switching to batch-sending
|
||||||
|
|
@ -71,6 +76,21 @@ def loads(data):
|
||||||
return pickle.loads(to_str(data))
|
return pickle.loads(to_str(data))
|
||||||
|
|
||||||
|
|
||||||
|
@wraps
|
||||||
|
def catch_traceback(func):
|
||||||
|
"Helper decorator"
|
||||||
|
def decorator(*args, **kwargs):
|
||||||
|
try:
|
||||||
|
func(*args, **kwargs)
|
||||||
|
except Exception:
|
||||||
|
global _LOGGER
|
||||||
|
if not _LOGGER:
|
||||||
|
from evennia.utils import logger as _LOGGER
|
||||||
|
_LOGGER.log_trace()
|
||||||
|
raise # make sure the error is visible on the other side of the connection too
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
# AMP Communication Command types
|
# AMP Communication Command types
|
||||||
|
|
||||||
class Compressed(amp.String):
|
class Compressed(amp.String):
|
||||||
|
|
@ -123,6 +143,18 @@ class Compressed(amp.String):
|
||||||
return zlib.decompress(inString)
|
return zlib.decompress(inString)
|
||||||
|
|
||||||
|
|
||||||
|
class MsgLauncher2Portal(amp.Command):
|
||||||
|
"""
|
||||||
|
Message Launcher -> Portal
|
||||||
|
|
||||||
|
"""
|
||||||
|
key = "MsgLauncher2Portal"
|
||||||
|
arguments = [('operation', amp.String()),
|
||||||
|
('argument', amp.String())]
|
||||||
|
errors = {Exception: 'EXCEPTION'}
|
||||||
|
response = [('result', amp.String())]
|
||||||
|
|
||||||
|
|
||||||
class MsgPortal2Server(amp.Command):
|
class MsgPortal2Server(amp.Command):
|
||||||
"""
|
"""
|
||||||
Message Portal -> Server
|
Message Portal -> Server
|
||||||
|
|
@ -173,6 +205,17 @@ class AdminServer2Portal(amp.Command):
|
||||||
response = []
|
response = []
|
||||||
|
|
||||||
|
|
||||||
|
class MsgPing(amp.Command):
|
||||||
|
"""
|
||||||
|
Ping between AMP services
|
||||||
|
|
||||||
|
"""
|
||||||
|
key = "AMPPing"
|
||||||
|
arguments = [('ping', amp.Boolean())]
|
||||||
|
errors = {Exception: 'EXCEPTION'}
|
||||||
|
response = [('pong', amp.Boolean())]
|
||||||
|
|
||||||
|
|
||||||
class FunctionCall(amp.Command):
|
class FunctionCall(amp.Command):
|
||||||
"""
|
"""
|
||||||
Bidirectional Server <-> Portal
|
Bidirectional Server <-> Portal
|
||||||
|
|
@ -259,9 +302,12 @@ class AMPMultiConnectionProtocol(amp.AMP):
|
||||||
info (str): Error string.
|
info (str): Error string.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
global _LOGGER
|
||||||
|
if not _LOGGER:
|
||||||
|
from evennia.utils import logger as _LOGGER
|
||||||
e.trap(Exception)
|
e.trap(Exception)
|
||||||
logger.log_err("AMP Error for %(info)s: %(e)s" % {'info': info,
|
_LOGGER.log_err("AMP Error for %(info)s: %(e)s" % {'info': info,
|
||||||
'e': e.getErrorMessage()})
|
'e': e.getErrorMessage()})
|
||||||
|
|
||||||
def data_in(self, packed_data):
|
def data_in(self, packed_data):
|
||||||
"""
|
"""
|
||||||
|
|
@ -292,10 +338,28 @@ class AMPMultiConnectionProtocol(amp.AMP):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
deferreds = []
|
deferreds = []
|
||||||
for prot in self.factory.broadcasts:
|
for protcl in self.factory.broadcasts:
|
||||||
deferreds.append(prot.callRemote(command,
|
deferreds.append(protcl.callRemote(command,
|
||||||
packed_data=dumps((sessid, kwargs))))
|
packed_data=dumps((sessid, kwargs))).addErrback(
|
||||||
return DeferredList(deferreds, fireOnOneErrback=1).addErrback(self.errback, command.key)
|
self.errback, command.key))
|
||||||
|
return DeferredList(deferreds)
|
||||||
|
|
||||||
|
def send_ping(self, port, callback, errback):
|
||||||
|
"""
|
||||||
|
Ping to the given AMP port.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
port (int): The port to ping
|
||||||
|
callback (callable): This will be called with the port that replied to the ping.
|
||||||
|
errback (callable0: This will be called with the port that failed to reply.
|
||||||
|
|
||||||
|
"""
|
||||||
|
targets = [(protcl, protcl.getHost()[1]) for protcl in self.factory.broadcasts]
|
||||||
|
deferreds = []
|
||||||
|
for protcl, port in ((protcl, prt) for protcl, prt in targets if prt == port):
|
||||||
|
deferreds.append(protcl.callRemote(MsgPing, ping=True).addCallback(
|
||||||
|
callback, port).addErrback(errback, port))
|
||||||
|
return DeferredList(deferreds)
|
||||||
|
|
||||||
# generic function send/recvs
|
# generic function send/recvs
|
||||||
|
|
||||||
|
|
@ -323,6 +387,7 @@ class AMPMultiConnectionProtocol(amp.AMP):
|
||||||
lambda r: loads(r["result"])).addErrback(self.errback, "FunctionCall")
|
lambda r: loads(r["result"])).addErrback(self.errback, "FunctionCall")
|
||||||
|
|
||||||
@FunctionCall.responder
|
@FunctionCall.responder
|
||||||
|
@catch_traceback
|
||||||
def receive_functioncall(self, module, function, func_args, func_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
|
||||||
|
|
|
||||||
|
|
@ -96,9 +96,48 @@ class AMPServerProtocol(amp.AMPMultiConnectionProtocol):
|
||||||
"""
|
"""
|
||||||
return self.data_out(amp.AdminPortal2Server, session.sessid, operation=operation, **kwargs)
|
return self.data_out(amp.AdminPortal2Server, session.sessid, operation=operation, **kwargs)
|
||||||
|
|
||||||
|
def sendPingPortal2Server(self, callback):
|
||||||
|
"""
|
||||||
|
Send ping to check if Server is alive.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
# receive amp data
|
# receive amp data
|
||||||
|
|
||||||
|
@amp.MsgLauncher2Portal.responder
|
||||||
|
@amp.catch_traceback
|
||||||
|
def portal_receive_launcher2portal(self, operation, argument):
|
||||||
|
"""
|
||||||
|
Receives message arriving from evennia_launcher.
|
||||||
|
This method is executed on the Portal.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
operation (str): The action to perform.
|
||||||
|
argument (str): A possible argument to the instruction, or the empty string.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
result (dict): The result back to the launcher.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
This is the entrypoint for controlling the entire Evennia system from the
|
||||||
|
evennia launcher.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if operation == amp.PPING: # check portal and server status
|
||||||
|
pass
|
||||||
|
elif operation == amp.PSTART: # portal start (server start or reload)
|
||||||
|
pass
|
||||||
|
elif operation == amp.SRELOAD: # reload server
|
||||||
|
pass
|
||||||
|
elif operation == amp.PSHUTD: # portal + server shutdown
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise Exception("operation %(op)s not recognized." % {'op': operation})
|
||||||
|
# fallback
|
||||||
|
return {"result": ""}
|
||||||
|
|
||||||
@amp.MsgServer2Portal.responder
|
@amp.MsgServer2Portal.responder
|
||||||
|
@amp.catch_traceback
|
||||||
def portal_receive_server2portal(self, packed_data):
|
def portal_receive_server2portal(self, packed_data):
|
||||||
"""
|
"""
|
||||||
Receives message arriving to Portal from Server.
|
Receives message arriving to Portal from Server.
|
||||||
|
|
@ -115,6 +154,7 @@ class AMPServerProtocol(amp.AMPMultiConnectionProtocol):
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
@amp.AdminServer2Portal.responder
|
@amp.AdminServer2Portal.responder
|
||||||
|
@amp.catch_traceback
|
||||||
def portal_receive_adminserver2portal(self, packed_data):
|
def portal_receive_adminserver2portal(self, packed_data):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue