Add ability to ping Portal from launcher over AMP
This commit is contained in:
parent
e0d8d8293d
commit
84e0f463a5
3 changed files with 137 additions and 16 deletions
|
|
@ -20,6 +20,8 @@ import importlib
|
||||||
from distutils.version import LooseVersion
|
from distutils.version import LooseVersion
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
from subprocess import Popen, check_output, call, CalledProcessError, STDOUT
|
from subprocess import Popen, check_output, call, CalledProcessError, STDOUT
|
||||||
|
from twisted.protocols import amp
|
||||||
|
from twisted.internet import reactor, endpoints
|
||||||
import django
|
import django
|
||||||
|
|
||||||
# Signal processing
|
# Signal processing
|
||||||
|
|
@ -49,18 +51,32 @@ CURRENT_DIR = os.getcwd()
|
||||||
GAMEDIR = CURRENT_DIR
|
GAMEDIR = CURRENT_DIR
|
||||||
|
|
||||||
# Operational setup
|
# Operational setup
|
||||||
|
AMP_PORT = None
|
||||||
|
AMP_HOST = None
|
||||||
|
AMP_INTERFACE = None
|
||||||
|
|
||||||
SERVER_LOGFILE = None
|
SERVER_LOGFILE = None
|
||||||
PORTAL_LOGFILE = None
|
PORTAL_LOGFILE = None
|
||||||
HTTP_LOGFILE = None
|
HTTP_LOGFILE = None
|
||||||
|
|
||||||
SERVER_PIDFILE = None
|
SERVER_PIDFILE = None
|
||||||
PORTAL_PIDFILE = None
|
PORTAL_PIDFILE = None
|
||||||
SERVER_RESTART = None
|
SERVER_RESTART = None
|
||||||
PORTAL_RESTART = None
|
PORTAL_RESTART = None
|
||||||
|
|
||||||
SERVER_PY_FILE = None
|
SERVER_PY_FILE = None
|
||||||
PORTAL_PY_FILE = None
|
PORTAL_PY_FILE = None
|
||||||
|
|
||||||
TEST_MODE = False
|
TEST_MODE = False
|
||||||
ENFORCED_SETTING = False
|
ENFORCED_SETTING = False
|
||||||
|
|
||||||
|
# communication constants
|
||||||
|
|
||||||
|
SRELOAD = chr(14) # server reloading (have portal start a new server)
|
||||||
|
PSTART = chr(15) # server+portal start
|
||||||
|
PSHUTD = chr(16) # portal (+server) shutdown
|
||||||
|
PSTATUS = chr(17) # ping server or portal status
|
||||||
|
|
||||||
# requirements
|
# requirements
|
||||||
PYTHON_MIN = '2.7'
|
PYTHON_MIN = '2.7'
|
||||||
TWISTED_MIN = '16.0.0'
|
TWISTED_MIN = '16.0.0'
|
||||||
|
|
@ -303,6 +319,15 @@ MENU = \
|
||||||
+---------------------------------------------------------------+
|
+---------------------------------------------------------------+
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
ERROR_AMP_UNCONFIGURED = \
|
||||||
|
"""
|
||||||
|
Can't find server info for connecting. Either run this command from
|
||||||
|
the game dir (it will then use the game's settings file) or specify
|
||||||
|
the path to your game's settings file manually with the --settings
|
||||||
|
option.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
ERROR_LOGDIR_MISSING = \
|
ERROR_LOGDIR_MISSING = \
|
||||||
"""
|
"""
|
||||||
ERROR: One or more log-file directory locations could not be
|
ERROR: One or more log-file directory locations could not be
|
||||||
|
|
@ -391,11 +416,94 @@ NOTE_TEST_CUSTOM = \
|
||||||
on the game dir.)
|
on the game dir.)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
#------------------------------------------------------------
|
|
||||||
|
# ------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Functions
|
# Protocol Evennia launcher - Portal/Server communication
|
||||||
#
|
#
|
||||||
#------------------------------------------------------------
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
class MsgStatus(amp.Command):
|
||||||
|
"""
|
||||||
|
Ping between AMP services
|
||||||
|
|
||||||
|
"""
|
||||||
|
key = "AMPPing"
|
||||||
|
arguments = [('question', amp.String())]
|
||||||
|
errors = {Exception: 'EXCEPTION'}
|
||||||
|
response = [('status', amp.String())]
|
||||||
|
|
||||||
|
|
||||||
|
class MsgLauncher2Portal(amp.Command):
|
||||||
|
"""
|
||||||
|
Message Launcher -> Portal
|
||||||
|
|
||||||
|
"""
|
||||||
|
key = "MsgLauncher2Portal"
|
||||||
|
arguments = [('operation', amp.String()),
|
||||||
|
('argument', amp.String())]
|
||||||
|
errors = {Exception: 'EXCEPTION'}
|
||||||
|
response = [('result', amp.String())]
|
||||||
|
|
||||||
|
|
||||||
|
def send_instruction(instruction, argument, callback, errback):
|
||||||
|
"""
|
||||||
|
Send instruction and handle the response.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if None in (AMP_HOST, AMP_PORT, AMP_INTERFACE):
|
||||||
|
print(ERROR_AMP_UNCONFIGURED)
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
def _on_connect(prot):
|
||||||
|
"""
|
||||||
|
This fires with the protocol when connection is established. We
|
||||||
|
immediately send off the instruction then shut down.
|
||||||
|
|
||||||
|
"""
|
||||||
|
def _callback(result):
|
||||||
|
callback(result)
|
||||||
|
prot.transport.loseConnection()
|
||||||
|
reactor.stop()
|
||||||
|
|
||||||
|
def _errback(fail):
|
||||||
|
errback(fail)
|
||||||
|
prot.transport.loseConnection()
|
||||||
|
reactor.stop()
|
||||||
|
|
||||||
|
if instruction == PSTATUS:
|
||||||
|
prot.callRemote(MsgStatus, question="").addCallbacks(_callback, _errback)
|
||||||
|
else:
|
||||||
|
prot.callRemote(MsgLauncher2Portal, instruction, argument).addCallbacks(
|
||||||
|
_callback, _errback)
|
||||||
|
|
||||||
|
point = endpoints.TCP4ClientEndpoint(reactor, AMP_HOST, AMP_PORT)
|
||||||
|
deferred = endpoints.connectProtocol(point, amp.AMP())
|
||||||
|
deferred.addCallbacks(_on_connect, errback)
|
||||||
|
reactor.run()
|
||||||
|
|
||||||
|
|
||||||
|
def send_status():
|
||||||
|
"""
|
||||||
|
Send ping to portal
|
||||||
|
|
||||||
|
"""
|
||||||
|
import time
|
||||||
|
t0 = time.time()
|
||||||
|
def _callback(status):
|
||||||
|
print("STATUS returned: %s (%gms)" % (status, (time.time()-t0) * 1000))
|
||||||
|
|
||||||
|
def _errback(err):
|
||||||
|
print("STATUS returned: %s" % err)
|
||||||
|
|
||||||
|
send_instruction(PSTATUS, None, _callback, _errback)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Helper functions
|
||||||
|
#
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
def evennia_version():
|
def evennia_version():
|
||||||
|
|
@ -869,12 +977,17 @@ def init_game_directory(path, check_db=True):
|
||||||
check_database()
|
check_database()
|
||||||
|
|
||||||
# set up the Evennia executables and log file locations
|
# set up the Evennia executables and log file locations
|
||||||
|
global AMP_PORT, AMP_HOST, AMP_INTERFACE
|
||||||
global SERVER_PY_FILE, PORTAL_PY_FILE
|
global SERVER_PY_FILE, PORTAL_PY_FILE
|
||||||
global SERVER_LOGFILE, PORTAL_LOGFILE, HTTP_LOGFILE
|
global SERVER_LOGFILE, PORTAL_LOGFILE, HTTP_LOGFILE
|
||||||
global SERVER_PIDFILE, PORTAL_PIDFILE
|
global SERVER_PIDFILE, PORTAL_PIDFILE
|
||||||
global SERVER_RESTART, PORTAL_RESTART
|
global SERVER_RESTART, PORTAL_RESTART
|
||||||
global EVENNIA_VERSION
|
global EVENNIA_VERSION
|
||||||
|
|
||||||
|
AMP_PORT = settings.AMP_PORT
|
||||||
|
AMP_HOST = settings.AMP_HOST
|
||||||
|
AMP_INTERFACE = settings.AMP_INTERFACE
|
||||||
|
|
||||||
SERVER_PY_FILE = os.path.join(EVENNIA_LIB, "server", "server.py")
|
SERVER_PY_FILE = os.path.join(EVENNIA_LIB, "server", "server.py")
|
||||||
PORTAL_PY_FILE = os.path.join(EVENNIA_LIB, "portal", "portal", "portal.py")
|
PORTAL_PY_FILE = os.path.join(EVENNIA_LIB, "portal", "portal", "portal.py")
|
||||||
|
|
||||||
|
|
@ -1239,6 +1352,9 @@ def main():
|
||||||
"service", metavar="component", nargs='?', default="all",
|
"service", metavar="component", nargs='?', default="all",
|
||||||
help=("Which component to operate on: "
|
help=("Which component to operate on: "
|
||||||
"'server', 'portal' or 'all' (default if not set)."))
|
"'server', 'portal' or 'all' (default if not set)."))
|
||||||
|
parser.add_argument(
|
||||||
|
"--status", action='store_true', dest='get_status',
|
||||||
|
default=None, help='Get current server status.')
|
||||||
parser.epilog = (
|
parser.epilog = (
|
||||||
"Common usage: evennia start|stop|reload. Django-admin database commands:"
|
"Common usage: evennia start|stop|reload. Django-admin database commands:"
|
||||||
"evennia migration|flush|shell|dbshell (see the django documentation for more django-admin commands.)")
|
"evennia migration|flush|shell|dbshell (see the django documentation for more django-admin commands.)")
|
||||||
|
|
@ -1289,6 +1405,11 @@ def main():
|
||||||
print(ERROR_INITSETTINGS)
|
print(ERROR_INITSETTINGS)
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
if args.get_status:
|
||||||
|
init_game_directory(CURRENT_DIR, check_db=True)
|
||||||
|
send_status()
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
if args.dummyrunner:
|
if args.dummyrunner:
|
||||||
# launch the dummy runner
|
# launch the dummy runner
|
||||||
init_game_directory(CURRENT_DIR, check_db=True)
|
init_game_directory(CURRENT_DIR, check_db=True)
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ 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
|
PSTART = chr(15) # server+portal start
|
||||||
PSHUTD = chr(16) # portal (+server) shutdown
|
PSHUTD = chr(16) # portal (+server) shutdown
|
||||||
PPING = chr(17) # server or portal status
|
PSTATUS = chr(17) # ping 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
|
||||||
|
|
@ -205,15 +205,15 @@ class AdminServer2Portal(amp.Command):
|
||||||
response = []
|
response = []
|
||||||
|
|
||||||
|
|
||||||
class MsgPing(amp.Command):
|
class MsgStatus(amp.Command):
|
||||||
"""
|
"""
|
||||||
Ping between AMP services
|
Check Status between AMP services
|
||||||
|
|
||||||
"""
|
"""
|
||||||
key = "AMPPing"
|
key = "AMPPing"
|
||||||
arguments = [('ping', amp.Boolean())]
|
arguments = [('question', amp.String())]
|
||||||
errors = {Exception: 'EXCEPTION'}
|
errors = {Exception: 'EXCEPTION'}
|
||||||
response = [('pong', amp.Boolean())]
|
response = [('status', amp.String())]
|
||||||
|
|
||||||
|
|
||||||
class FunctionCall(amp.Command):
|
class FunctionCall(amp.Command):
|
||||||
|
|
@ -271,9 +271,7 @@ class AMPMultiConnectionProtocol(amp.AMP):
|
||||||
|
|
||||||
def connectionMade(self):
|
def connectionMade(self):
|
||||||
"""
|
"""
|
||||||
This is called when an AMP connection is (re-)established
|
This is called when an AMP connection is (re-)established AMP calls it on both sides.
|
||||||
between server and portal. AMP calls it on both sides, so we
|
|
||||||
need to make sure to only trigger resync from the portal side.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self.factory.broadcasts.append(self)
|
self.factory.broadcasts.append(self)
|
||||||
|
|
@ -344,7 +342,7 @@ class AMPMultiConnectionProtocol(amp.AMP):
|
||||||
self.errback, command.key))
|
self.errback, command.key))
|
||||||
return DeferredList(deferreds)
|
return DeferredList(deferreds)
|
||||||
|
|
||||||
def send_ping(self, port, callback, errback):
|
def send_status(self, port, callback, errback):
|
||||||
"""
|
"""
|
||||||
Ping to the given AMP port.
|
Ping to the given AMP port.
|
||||||
|
|
||||||
|
|
@ -357,7 +355,7 @@ class AMPMultiConnectionProtocol(amp.AMP):
|
||||||
targets = [(protcl, protcl.getHost()[1]) for protcl in self.factory.broadcasts]
|
targets = [(protcl, protcl.getHost()[1]) for protcl in self.factory.broadcasts]
|
||||||
deferreds = []
|
deferreds = []
|
||||||
for protcl, port in ((protcl, prt) for protcl, prt in targets if prt == port):
|
for protcl, port in ((protcl, prt) for protcl, prt in targets if prt == port):
|
||||||
deferreds.append(protcl.callRemote(MsgPing, ping=True).addCallback(
|
deferreds.append(protcl.callRemote(MsgStatus, status=True).addCallback(
|
||||||
callback, port).addErrback(errback, port))
|
callback, port).addErrback(errback, port))
|
||||||
return DeferredList(deferreds)
|
return DeferredList(deferreds)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,10 @@ class AMPServerProtocol(amp.AMPMultiConnectionProtocol):
|
||||||
|
|
||||||
# receive amp data
|
# receive amp data
|
||||||
|
|
||||||
|
@amp.MsgStatus.responder
|
||||||
|
def portal_receive_status(self, question):
|
||||||
|
return {"status": "All well"}
|
||||||
|
|
||||||
@amp.MsgLauncher2Portal.responder
|
@amp.MsgLauncher2Portal.responder
|
||||||
@amp.catch_traceback
|
@amp.catch_traceback
|
||||||
def portal_receive_launcher2portal(self, operation, argument):
|
def portal_receive_launcher2portal(self, operation, argument):
|
||||||
|
|
@ -123,9 +127,7 @@ class AMPServerProtocol(amp.AMPMultiConnectionProtocol):
|
||||||
evennia launcher.
|
evennia launcher.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if operation == amp.PPING: # check portal and server status
|
if operation == amp.PSTART: # portal start (server start or reload)
|
||||||
pass
|
|
||||||
elif operation == amp.PSTART: # portal start (server start or reload)
|
|
||||||
pass
|
pass
|
||||||
elif operation == amp.SRELOAD: # reload server
|
elif operation == amp.SRELOAD: # reload server
|
||||||
pass
|
pass
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue