mirror of
https://github.com/evennia/evennia.git
synced 2026-03-22 07:46:30 +01:00
Added remote function call abilities to AMP protocol, courtesy of patch by user Shell.
This allows for Server to call functions on Portal and vice-versa. Some rewrites and cleanup done before applying /Griatch.
This commit is contained in:
parent
049cc84be7
commit
592bc26b99
2 changed files with 71 additions and 5 deletions
|
|
@ -22,7 +22,8 @@ except ImportError:
|
|||
import pickle
|
||||
from twisted.protocols import amp
|
||||
from twisted.internet import protocol
|
||||
from src.utils.utils import to_str
|
||||
from twisted.internet.defer import Deferred
|
||||
from src.utils.utils import to_str, variable_from_module
|
||||
|
||||
# these are only needed on the server side, so we delay loading of them
|
||||
# so as to not have to load them on the portal too. Note: It's doubtful
|
||||
|
|
@ -133,6 +134,8 @@ class AmpClientFactory(protocol.ReconnectingClientFactory):
|
|||
protocol.ReconnectingClientFactory.clientConnectionFailed(self, connector, reason)
|
||||
|
||||
|
||||
# AMP Communication Command types
|
||||
|
||||
class MsgPortal2Server(amp.Command):
|
||||
"""
|
||||
Message portal -> server
|
||||
|
|
@ -198,6 +201,23 @@ class PortalAdmin(amp.Command):
|
|||
errors = [(Exception, 'EXCEPTION')]
|
||||
response = []
|
||||
|
||||
class FunctionCall(amp.Command):
|
||||
"""
|
||||
Bidirectional
|
||||
|
||||
Sent when either process needs to call an
|
||||
arbitrary function in the other.
|
||||
"""
|
||||
arguments = [('module', amp.String()),
|
||||
('function', amp.String()),
|
||||
('args', amp.String()),
|
||||
('kwargs', amp.String())]
|
||||
errors = [(Exception, 'EXCEPTION')]
|
||||
response = [('result', amp.String())]
|
||||
|
||||
|
||||
# Helper functions
|
||||
|
||||
dumps = lambda data: to_str(pickle.dumps(data, pickle.HIGHEST_PROTOCOL))
|
||||
loads = lambda data: pickle.loads(to_str(data))
|
||||
|
||||
|
|
@ -469,3 +489,43 @@ class AMPProtocol(amp.AMP):
|
|||
sessid=sessid,
|
||||
operation=operation,
|
||||
data=data).addErrback(self.errback, "PortalAdmin")
|
||||
|
||||
# Extra functions
|
||||
|
||||
def amp_function_call(self, module, function, args, kwargs):
|
||||
"""
|
||||
This allows Portal- and Server-process to call an arbitrary function
|
||||
in the other process. It is intended for use by plugin modules.
|
||||
"""
|
||||
args = loads(args)
|
||||
kwargs = loads(kwargs)
|
||||
|
||||
# call the function (don't catch tracebacks here)
|
||||
result = variable_from_module(module, function)(*args, **kwargs)
|
||||
|
||||
if isinstance(result, Deferred):
|
||||
# if result is a deferred, attach handler to properly wrap the return value
|
||||
result.addCallback(lambda r: {"result":dumps(r)})
|
||||
return result
|
||||
else:
|
||||
return {'result':dumps(result)}
|
||||
FunctionCall.responder(amp_function_call)
|
||||
|
||||
|
||||
def call_remote_FunctionCall(self, modulepath, functionname, *args, **kwargs):
|
||||
"""
|
||||
Access method called by either process. This will call an arbitrary function
|
||||
on the other process (On Portal if calling from Server and vice versa).
|
||||
|
||||
Inputs:
|
||||
modulepath (str) - python path to module holding function to call
|
||||
functionname (str) - name of function in given module
|
||||
*args, **kwargs will be used as arguments/keyword args for the remote function call
|
||||
Returns:
|
||||
A deferred that fires with the return value of the remote function call
|
||||
"""
|
||||
return self.callRemote(FunctionCall,
|
||||
module=modulepath,
|
||||
function=functionname,
|
||||
args=dumps(args),
|
||||
kwargs=dumps(kwargs)).addCallback(lambda r: loads(r["result"])).addErrback(self.errback, "FunctionCall")
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import os, sys, imp, types, math
|
|||
import textwrap, datetime, random
|
||||
from inspect import ismodule
|
||||
from collections import defaultdict
|
||||
from twisted.internet import threads
|
||||
from twisted.internet import threads, defer, reactor
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.conf import settings
|
||||
|
||||
|
|
@ -39,6 +39,7 @@ def is_iter(iterable):
|
|||
except AttributeError:
|
||||
return False
|
||||
|
||||
|
||||
def make_iter(obj):
|
||||
"Makes sure that the object is always iterable."
|
||||
return not hasattr(obj, '__iter__') and [obj] or obj
|
||||
|
|
@ -87,9 +88,15 @@ def list_to_string(inlist, endsep="and", addquote=False):
|
|||
"""
|
||||
This pretty-formats a list as string output, adding
|
||||
an optional alternative separator to the second to last entry.
|
||||
If addquote is True, the outgoing strints will be surrounded by quotes.
|
||||
If addquote is True, the outgoing strings will be surrounded by quotes.
|
||||
|
||||
[1,2,3] -> '1, 2 and 3'
|
||||
Examples:
|
||||
no endsep:
|
||||
[1,2,3] -> '1, 2, 3'
|
||||
with endsep=='and':
|
||||
[1,2,3] -> '1, 2 and 3'
|
||||
with addquote and endsep
|
||||
[1,2,3] -> '"1", "2" and "3"'
|
||||
"""
|
||||
if not inlist:
|
||||
return ""
|
||||
|
|
@ -490,7 +497,6 @@ def uses_database(name="sqlite3"):
|
|||
return engine == "django.db.backends.%s" % name
|
||||
|
||||
|
||||
|
||||
_FROM_MODEL_MAP = None
|
||||
_TO_DBOBJ = lambda o: (hasattr(o, "dbobj") and o.dbobj) or o
|
||||
_TO_PACKED_DBOBJ = lambda natural_key, dbref: ('__packed_dbobj__', natural_key, dbref)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue