The Message path¶
> look
A Meadow
This is a beautiful meadow. It is full of flowers.
You see: a flower
Exits: north, east
When you send a command like look
into Evennia - what actually happens? How does that look
string end up being handled by the CmdLook
class? What happens when we use e.g. caller.msg()
to send the message back?
Understanding this flow of data - the message path is important in order to understand how Evennia works.
Ingoing message path¶
Internet│
┌─────┐ │ ┌────────┐
┌──────┐ │Text │ │ ┌────────────┐ ┌─────────┐ │Command │
│Client├────┤JSON ├─┼──►commandtuple├────►Inputfunc├────►DB query│
└──────┘ │etc │ │ └────────────┘ └─────────┘ │etc │
└─────┘ │ └────────┘
│Evennia
Incoming command tuples¶
Ingoing data from the client (coming in as raw strings or serialized JSON) is converted by Evennia to a commandtuple
. Thesa are the same regardless of what client or connection was used. A commandtuple
is a simple tuple with three elements:
(commandname, (args), {kwargs})
For the look
-command (and anything else written by the player), the text
commandtuple
is generated:
("text", ("look",), {})
Inputfuncs¶
On the Evennia server side, a list of inputfucs are registered. You can add your own by extending settings.INPUT_FUNC_MODULES
.
inputfunc_commandname(session, *args, **kwargs)
Here the session
represents the unique client connection this is coming from (that is, it’s identifying just who is sending this input).
One such inputfunc is named text
. For sending a look
, it will be called as
text(session, *("look",), **{})
What an inputfunc
does with this depends. For an Out-of-band instruction, it could fetch the health of a player or tick down some counter.
For the text
inputfunc
the Evennia CommandHandler is invoked and the argument is parsed further in order to figure which command was intended.
In the example of look
, the CmdLook
command-class will be invoked. This will retrieve the description of the current location.
Outgoing message path¶
Internet│
┌─────┐ │
┌──────┐ │Text │ │ ┌──────────┐ ┌────────────┐ ┌─────┐
│Client◄────┤JSON ├─┼──┤outputfunc◄────┤commandtuple◄───┤msg()│
└──────┘ │etc │ │ └──────────┘ └────────────┘ └─────┘
└─────┘ │
│Evennia
msg
to outgoing commandtuple¶
When the inputfunc
has finished whatever it is supposed to, the server may or may not decide to return a result (Some types of inputcommands
may not expect or require a response at all). The server also often sends outgoing messages without any prior matching ingoing data.
Whenever data needs to be sent “out” of Evennia, we must generalize it into a (now outgoing) commandtuple
(commandname, (args), {kwargs})
. This we do with the msg()
method. For convenience, this methods is available on every major entity, such as Object.msg()
and Account.msg()
. They all link back to Session.msg()
.
msg(text=None, from_obj=None, session=None, options=None, **kwargs)
text
is so common that it is given as the default:
msg("A meadow\n\nThis is a beautiful meadow...")
This is converted to a commandtuple
looking like this:
("text", ("A meadow\n\nThis is a beutiful meadow...",) {})
The msg()
method allows you to define the commandtuple
directly, for whatever outgoing instruction you want to find:
msg(current_status=(("healthy", "charged"), {"hp": 12, "mp": 20}))
This will be converted to a commandtuple
looking like this:
("current_status", ("healthy", "charged"), {"hp": 12, "mp": 20})
outputfuncs¶
Since msg()
is aware of which Session to send to, the outgoing commandtuple
is always end up pointed at the right client.
Each supported Evennia Protocol (Telnet, SSH, Webclient etc) has their own outputfunc
, which converts the generic commandtuple
into a form that particular protocol understands, such as telnet instructions or JSON.
For telnet (no SSL), the look
will return over the wire as plain text:
A meadow\n\nThis is a beautiful meadow...
When sending to the webclient, the commandtuple
is converted as serialized JSON, like this:
'["look", ["A meadow\\n\\nThis is a beautiful meadow..."], {}]'
This is then sent to the client over the wire. It’s then up to the client to interpret and handle the data properly.
Components along the path¶
Ingoing¶
┌──────┐ ┌─────────────────────────┐
│Client│ │ │
└──┬───┘ │ ┌────────────────────┐ │
│ ┌──────┼─►│ServerSessionHandler│ │
┌──────────────────┼──────┐ │ │ └───┬────────────────┘ │
│ Portal │ │ │ │ │ │
│ ┌─────────▼───┐ │ ┌─┴─┐ │ ┌───▼─────────┐ │
│ │PortalSession│ │ │AMP│ │ │ServerSession│ │
│ └─────────┬───┘ │ └─┬─┘ │ └───┬─────────┘ │
│ │ │ │ │ │ │
│ ┌────────────────▼───┐ │ │ │ ┌───▼─────┐ │
│ │PortalSessionHandler├──┼──────┘ │ │Inputfunc│ │
│ └────────────────────┘ │ │ └─────────┘ │
│ │ │ Server │
└─────────────────────────┘ └─────────────────────────┘
Client - sends handshake or commands over the wire. This is received by the Evennia Portal.
PortalSession
represents one client connection. It understands the communiation protocol used. It converts the protocol-specific input to a genericcommandtuple
structure(cmdname, (args), {kwargs})
.PortalSessionHandler
handles all connections. It pickles thecommandtuple
together with the session-id.Pickled data is sent across the
AMP
(Asynchronous Message Protocol) connection to the Server part of Evennia.ServerSessionHandler
unpickles thecommandtuple
and matches the session-id to a matchingSessionSession
.ServerSession
represents the session-connection on the Server side. It looks through its registry of Inputfuncs to find a match.The appropriate
Inputfunc
is called with the args/kwargs included in thecommandtuple
. Depending onInputfunc
, this could have different effects. For thetext
inputfunc, it fires the CommandHandler.
Outgoing¶
┌──────┐ ┌─────────────────────────┐
│Client│ │ │
└──▲───┘ │ ┌────────────────────┐ │
│ ┌──────┼──┤ServerSessionHandler│ │
┌──────────────────┼──────┐ │ │ └───▲────────────────┘ │
│ Portal │ │ │ │ │ │
│ ┌─────────┴───┐ │ ┌─┴─┐ │ ┌───┴─────────┐ │
│ │PortalSession│ │ │AMP│ │ │ServerSession│ │
│ └─────────▲───┘ │ └─┬─┘ │ └───▲─────────┘ │
│ │ │ │ │ │ │
│ ┌────────────────┴───┐ │ │ │ ┌───┴──────┐ │
│ │PortalSessionHandler◄──┼──────┘ │ │msg() call│ │
│ └────────────────────┘ │ │ └──────────┘ │
│ │ │ Server │
└─────────────────────────┘ └─────────────────────────┘
The
msg()
method is calledServerSession
and in particularServerSession.msg()
is the central point through which allmsg()
calls are routed in order to send data to that Session.ServerSessionHandler
converts themsg
input to a propercommandtuple
structure(cmdname, (args), {kwargs})
. It pickles thecommandtuple
together with the session-id.Pickled data is sent across across the
AMP
(Asynchronous Message Protocol) connection to the Portal part of Evennia.PortalSessionHandler
unpickles thecommandtuple
and matches its session id to a matchingPortalSession
.The
PortalSession
is now responsible for converting the genericcommandtuple
to the communication protocol used by that particular connection.The Client receives the data and can act on it.