Posted by
| Nick Gammon
Australia (23,120 posts) Bio
Forum Administrator |
Message
| This post has been written to help scripters understand the exact sequence of events, and design considerations, in processing incoming text from each connected world (MUD).
First we need to consider how data arrives from the MUD, namely in packets. The TCP/IP protocol is a streaming protocol, which means that the TCP/IP layer breaks up data sent by the MUD server into packets based on their size and time constraints. Thus a particular packet:
- May be a partial line (that is, a line on the screen may be composed of multiple packets)
- May contain multiple lines (a few small lines may appear in a single packet)
- Does not necessarily contain information to indicate a line has ended (like the newline character)
We are further constrained by the fact that we need to display a packet as soon as it arrives, without waiting for a newline character (linefeed) because it may be a prompt line, like:
Enter your character's name, or type new:
If we waited for the newline character in this case, before displaying the line, you would never see it, and thus never log into the MUD.
So, the way MUSHclient treats incoming packets is as follows:
- It passes it to the MCCP decompressor, if MCCP (Mud Client Compression Protocol) is active, to convert from compressed to uncompressed data.
- It passes the packet to any plugin that implements the OnPluginPacketReceived script callback. This gives the plugin a chance to "get at" the raw packet data, including the ANSI codes. The plugin can modify the packet if desired, thus changing the packet contents before the next step.
http://www.gammon.com.au/scripts/doc.php?general=plugin_callbacks
- It passes the packet to the "state machine" that handles MXP codes (MUD Extension Protocol), ANSI codes, and Telnet codes. I use the words "state machine" because this maintains a state from packet to packet, so that if an ANSI code spans multiple packets, it will be handled correctly.
This phase will handle things like Telnet queries (eg. "is MXP supported?"), conversion of MXP codes to the correct colour and style run types (like hyperlinks), and conversion of the ANSI colour (and other sequences) to appropriate style runs.
- The output from the state machine (eg. a style run in red on black) is added to the current line in the output window, which is drawn. Thus if a line spans multiple packets they will be gradually drawn on the screen. At this stage the line no longer contains ANSI codes but is put into batches of style runs, where each style consists of the same colour, boldness and so on. In fact in the case of MXP there was never an ANSI code but an MXP sequence that might have specified a colour.
- Once a newline character (hex 0x0A) arrives "end of line processing" commences for the current line.
Thus you can change data before it reaches the screen, bearing in mind the limitation of the fact that a line may be broken over multiple packets, and thus a regexp for instance may not match the entire line.
Once we reach "end of line processing" we do more things to the most recently arrived paragraph. A paragraph is one or more lines that you see on the screen that eventually end in a newline. Also a paragraph is constructed when the output transitions from one type to another (eg. user input to MUD output, or script note to MUD output). I am using the words "line" and "paragraph" interchangeably below. Strictly speaking a "line" is really a sequence of one to many lines which eventually end in a newline character, or a different type of line.
- Assemble the pure text of the line into a string for trigger matching (that is, iterating over the lines that comprise this paragraph, selecting the text portion and discarding colour information).
- Make a copy of the line, with style information included, for use with Lua scripts (this is the fourth argument supplied to Lua trigger functions).
- For input (command input) and notes (script notes) scan installed plugins for any OnPluginScreendraw script callbacks. This is for people who use screen drawer software (speech) to speak the contents of both input and note lines.
- For input (command input) and notes (script notes) log the line if required.
- If "chat system" snooping is active, send the paragraph to any chat user that is snooping us.
- For input (command input) and notes (script notes) finish processing the paragraph.
- For MUD output lines keep going with the following steps:
- Scan installed plugins for any OnPluginLineReceived script callbacks. If found, pass the line to the function, so it knows a complete line has arrived. If any plugin returns FALSE, then the "omit from output flag" is set for it. It is too late for the plugin to change the line, as it has been drawn on the screen, MXP codes processed, and so on.
- For the auto-mapper, check if this line matches the "mapping failure" string. If so, discard the last mapping command.
- If Pueblo-detection is active, check if this line matches the Pueblo initialization string.
- Add this line to the "most recent 200 lines" vector for use with multi-line trigger matching. These do not include notes and command input.
- Run trigger matching (in trigger sequence) over the text of the line (or the multi-line trigger text, as applicable). For multi-line triggers, a text string consisting of the number of lines specified, counting backwards for the required number of lines, is assembled prior to calling the trigger. If a trigger matches the trigger action is taken (such as flagging the line for omission, omitting from log file, calling an inline script, and so on. Further trigger matching is done if "keep evaluating" is set for the matching trigger. If a trigger does "send to output" this is batched up in a single string. If the trigger colours some or all of the line the line is re-coloured now (incrementally, for each trigger). That is, this step may change the colour style runs for the line in the output buffer.
StopEvaluatingTriggers - Version 4.89 added the function call "StopEvaluatingTriggers" which, if called, stops trigger evaluation regardless of the state of the "keep evaluating" flag, and also can optionally stop evaluation in all subsequent plugins.
Match sequence - the order in which triggers are matched is:
- All plugins with a negative sequence number are scanned in sequence number order (the lower the earlier). Within each plugin (which are handled one-by-one), the triggers are tested in trigger sequence.
- Then all triggers in the main world file are tested, in trigger sequence. If two triggers have the same sequence number the match order is not defined.
- Then all plugins with a positive sequence number (the default) are scanned in sequence number order (in the order in which the plugins were loaded, if the sequence number is the same as another plugin). Within each plugin (which are handled one-by-one), the triggers are tested in trigger sequence. From version 4.89 onwards you can set plugin evaluation sequencing by using the "sequence" option in the plugin header.
Sequence information added 3rd March 2007. Information about negative plugin sequence numbers added 24th March 2020.
- If a trigger has a designated script routine (in the "script name" box, not "send to script") then that script name is added to a list of pending scripts to be run. This is done so that multiple trigger matches for a single line do not call the same script multiple times.
- If "repeat on same line" is set for the trigger, the trigger regular expression is re-evaluated on the same line, with its starting column incremented past the last match. If further matches are made then the additional matching text is also coloured where applicable. This is for triggers that repeat on the same line, in case they need to colour words that might appear more than once.
Note: When matching with "repeat on same line" set, the "send" box is not used again, nor is the script routine called more than once. Also, the trigger count is only increment once for the line, not once per match on that line. The repeating is simply intended for colouring the same word (eg. the name of a friend) more than once if it appears multiple times on one line.
If you want to handle words individually on a line (eg. a "who" list, or a list of exits), then you need to make a trigger that matches the entire line, and break the line down further in a script.
- If the line was not flagged by any previous trigger as "omit from log" then the line is now logged to the log file, if any.
- If the line was flagged by any previous trigger (or OnPluginLineReceived plugin callback) as "omit from output" then lines are discarded from the output buffer up to and including the first line of the current paragraph, and the screen redrawn. A side-effect of this is that any inline scripts (that is "send to script") which did "notes" to the output window will have the note(s) discarded.
- Scan installed plugins for any OnPluginScreendraw script callbacks. This is done now for lines arriving from the MUD, so that speech synthesizers do not speak lines which have been omitted.
- Output from triggers that is "send to output" (and batched in a previous step) is now displayed in the output window in the world "note" colour.
- Any triggers which were "sent to script (after omit)" now run those scripts. This allows such scripts to do world.Note function calls, without the notes being omitted as part of the "omit from output" phase.
- Any script functions designated by triggers that needed to be run (script file scripts, not inline scripts) are now run. Script file functions can safely do "notes" to the output window, and they will not be discarded. The functions are run in the order in which the triggers matched.
- Any triggers which were designated "one-shot" are now deleted from the trigger list, in the main world, and in plugins, as appropriate.
|
- Nick Gammon
www.gammon.com.au, www.mushclient.com | Top |
|