Data ordering problem in AnsiNote in callbacks (gmcp)

Posted by Fiendish on Fri 18 Nov 2016 03:14 AM — 28 posts, 95,908 views.

USA Global Moderator #0
Aardwolf sends (upon commanding 'look'):

The Grand Plaza
room.info { "num": 32443, "name": "The Grand Plaza", "zone": "aylor", "terrain": "hellfountain", "details": "", "exits": { "n": 32454, "e": 32444, "s": 32457, "w": 32442 }, "coord": { "id": 0, "x": 30, "y": 20, "cont": 0 } }
  In contrast to the somber reputation of the arch marking the western
entrance to the plaza, the Sunrise Arch is a place to celebrate new life. 
When the New Year's procession winds through the streets, it is tradition
for the celebrants to write their worries of the old year on scrolls and
toss them into a fire built under the arch.  The stone has been stained
black with the soot of a million fears, and each sunrise is a pyre to
consume them again.  At night, the arch is a favored meeting place of
conspirators.  All varieties of hooded figures skulk here by night, before
wandering off to conduct their business.  

[ Exits: north east south west ]

where room.info is GMCP data.

With the test plugin below, I'm expecting to see (because it's a new line per change 7 in 5.02):

The Grand Plaza
** MY NOTE **hmm

  In contrast to the somber reputation of the arch marking the western
entrance to the plaza, the Sunrise Arch is a place to celebrate new life. 
When the New Year's procession winds through the streets, it is tradition
for the celebrants to write their worries of the old year on scrolls and
toss them into a fire built under the arch.  The stone has been stained
black with the soot of a million fears, and each sunrise is a pyre to
consume them again.  At night, the arch is a favored meeting place of
conspirators.  All varieties of hooded figures skulk here by night, before
wandering off to conduct their business.  

[ Exits: north east south west ]


Instead I see:

The Grand Plaza
** MY NOTE **  In contrast to the somber reputation of the arch marking the western
entrance to the plaza, the Sunrise Arch is a place to celebrate new life. 
hmm

When the New Year's procession winds through the streets, it is tradition
for the celebrants to write their worries of the old year on scrolls and
toss them into a fire built under the arch.  The stone has been stained
black with the soot of a million fears, and each sunrise is a pyre to
consume them again.  At night, the arch is a favored meeting place of
conspirators.  All varieties of hooded figures skulk here by night, before
wandering off to conduct their business.  

[ Exits: north east south west ]
(G) A stout dwarf, Wesnir, walks slowly, admiring the views of Aylor.


If I uncomment the empty Simulate, which I think should be doing absolutely nothing, then the output is correct. So I guess whatever the empty Simulate is doing there is needed.

Test Plugin:
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE muclient>
<muclient>
<plugin
   name="test_callback_ansinote_simulate"
   author="Fiendish"
   id="b0ba651e71e0c226a60ae8f1"
   language="Lua"
   date_written="2014-04-08 09:43:19"
   requires="4.90"
   version="1.0"
>
</plugin>
<script>
function OnPluginBroadcast (msg, id, name, text)
   if id == "3e7dedbe37e44942dd46d264" then -- gmcphandler
      if (text == "room.info") then
--        Simulate("")
         AnsiNote(ANSI(0,38,5,33).."** MY NOTE **"..ANSI(0,38,5,7).."hmm\n")
      end -- if room.info
   end
end
</script>
</muclient>
Amended on Fri 18 Nov 2016 03:18 AM by Fiendish
Australia Forum Administrator #1
Can you capture a test packet, so I can reproduce, please?

Quote:

If I uncomment the empty Simulate, which I think should be doing absolutely nothing ...


That's an interesting philosophical point. When you call Simulate() it calls DisplayMsg() which is designed to display MUD output. Normally DisplayMsg() expects to be called when there is data from the MUD, so it makes some state changes. For example, it terminates user input, or world notes, starts a new line (if necessary), and then gets ready to process the data in that line.

At this point it finds that there is zero data to process and exits. But the state changes have still been made.

As an analogy, if I ring your phone, and then hang up when you answer, you are getting "no data" from me. But you have still answered the phone, perhaps having to have got out of the bath to do it (a state change). The fact that I say nothing doesn't change the fact you had to make that state change to take the call.
USA Global Moderator #2

Sent  packet: 47 (6 bytes) at Thursday, November 17, 2016, 11:33:24 PM

look..             6c 6f 6f 6b 0d 0a

Incoming packet: 32 (968 bytes) at Thursday, November 17, 2016, 11:33:25 PM

.[1;32mThe Grand   1b 5b 31 3b 33 32 6d 54 68 65 20 47 72 61 6e 64
 Plaza.[0;37m..{   20 50 6c 61 7a 61 1b 5b 30 3b 33 37 6d 0a 0d 7b
coords}0,30,20..   63 6f 6f 72 64 73 7d 30 2c 33 30 2c 32 30 0a 0d
ÿúÉroom.info { "   ff fa c9 72 6f 6f 6d 2e 69 6e 66 6f 20 7b 20 22
num": 32443, "na   6e 75 6d 22 3a 20 33 32 34 34 33 2c 20 22 6e 61
me": "The Grand    6d 65 22 3a 20 22 54 68 65 20 47 72 61 6e 64 20
Plaza", "zone":    50 6c 61 7a 61 22 2c 20 22 7a 6f 6e 65 22 3a 20
"aylor", "terrai   22 61 79 6c 6f 72 22 2c 20 22 74 65 72 72 61 69
n": "hellfountai   6e 22 3a 20 22 68 65 6c 6c 66 6f 75 6e 74 61 69
n", "details": "   6e 22 2c 20 22 64 65 74 61 69 6c 73 22 3a 20 22
", "exits": { "n   22 2c 20 22 65 78 69 74 73 22 3a 20 7b 20 22 6e
": 32454, "e": 3   22 3a 20 33 32 34 35 34 2c 20 22 65 22 3a 20 33
2444, "s": 32457   32 34 34 34 2c 20 22 73 22 3a 20 33 32 34 35 37
, "w": 32442 },    2c 20 22 77 22 3a 20 33 32 34 34 32 20 7d 2c 20
"coord": { "id":   22 63 6f 6f 72 64 22 3a 20 7b 20 22 69 64 22 3a
 0, "x": 30, "y"   20 30 2c 20 22 78 22 3a 20 33 30 2c 20 22 79 22
: 20, "cont": 0    3a 20 32 30 2c 20 22 63 6f 6e 74 22 3a 20 30 20
} }ÿð  .[0;37mIn   7d 20 7d ff f0 20 20 1b 5b 30 3b 33 37 6d 49 6e
 contrast to the   20 63 6f 6e 74 72 61 73 74 20 74 6f 20 74 68 65
 somber reputati   20 73 6f 6d 62 65 72 20 72 65 70 75 74 61 74 69
on of the arch m   6f 6e 20 6f 66 20 74 68 65 20 61 72 63 68 20 6d
arking the weste   61 72 6b 69 6e 67 20 74 68 65 20 77 65 73 74 65
rn..entrance to    72 6e 0a 0d 65 6e 74 72 61 6e 63 65 20 74 6f 20
the plaza, the S   74 68 65 20 70 6c 61 7a 61 2c 20 74 68 65 20 53
unrise Arch is a   75 6e 72 69 73 65 20 41 72 63 68 20 69 73 20 61
 place to celebr   20 70 6c 61 63 65 20 74 6f 20 63 65 6c 65 62 72
ate new life. ..   61 74 65 20 6e 65 77 20 6c 69 66 65 2e 20 0a 0d
When the New Yea   57 68 65 6e 20 74 68 65 20 4e 65 77 20 59 65 61
r's procession w   72 27 73 20 70 72 6f 63 65 73 73 69 6f 6e 20 77
inds through the   69 6e 64 73 20 74 68 72 6f 75 67 68 20 74 68 65
 streets, it is    20 73 74 72 65 65 74 73 2c 20 69 74 20 69 73 20
tradition..for t   74 72 61 64 69 74 69 6f 6e 0a 0d 66 6f 72 20 74
he celebrants to   68 65 20 63 65 6c 65 62 72 61 6e 74 73 20 74 6f
 write their wor   20 77 72 69 74 65 20 74 68 65 69 72 20 77 6f 72
ries of the old    72 69 65 73 20 6f 66 20 74 68 65 20 6f 6c 64 20
year on scrolls    79 65 61 72 20 6f 6e 20 73 63 72 6f 6c 6c 73 20
and..toss them i   61 6e 64 0a 0d 74 6f 73 73 20 74 68 65 6d 20 69
nto a fire built   6e 74 6f 20 61 20 66 69 72 65 20 62 75 69 6c 74
 under the arch.   20 75 6e 64 65 72 20 74 68 65 20 61 72 63 68 2e
  The stone has    20 20 54 68 65 20 73 74 6f 6e 65 20 68 61 73 20
been stained..bl   62 65 65 6e 20 73 74 61 69 6e 65 64 0a 0d 62 6c
ack with the soo   61 63 6b 20 77 69 74 68 20 74 68 65 20 73 6f 6f
t of a million f   74 20 6f 66 20 61 20 6d 69 6c 6c 69 6f 6e 20 66
ears, and each s   65 61 72 73 2c 20 61 6e 64 20 65 61 63 68 20 73
unrise is a pyre   75 6e 72 69 73 65 20 69 73 20 61 20 70 79 72 65
 to..consume the   20 74 6f 0a 0d 63 6f 6e 73 75 6d 65 20 74 68 65
m again.  At nig   6d 20 61 67 61 69 6e 2e 20 20 41 74 20 6e 69 67
ht, the arch is    68 74 2c 20 74 68 65 20 61 72 63 68 20 69 73 20
a favored meetin   61 20 66 61 76 6f 72 65 64 20 6d 65 65 74 69 6e
g place of..cons   67 20 70 6c 61 63 65 20 6f 66 0a 0d 63 6f 6e 73
pirators.  All v   70 69 72 61 74 6f 72 73 2e 20 20 41 6c 6c 20 76
arieties of hood   61 72 69 65 74 69 65 73 20 6f 66 20 68 6f 6f 64
ed figures skulk   65 64 20 66 69 67 75 72 65 73 20 73 6b 75 6c 6b
 here by night,    20 68 65 72 65 20 62 79 20 6e 69 67 68 74 2c 20
before..wanderin   62 65 66 6f 72 65 0a 0d 77 61 6e 64 65 72 69 6e
g off to conduct   67 20 6f 66 66 20 74 6f 20 63 6f 6e 64 75 63 74
 their business.   20 74 68 65 69 72 20 62 75 73 69 6e 65 73 73 2e
  .....[1;32m[ E   20 20 0a 0d 0a 0d 1b 5b 31 3b 33 32 6d 5b 20 45
xits: north east   78 69 74 73 3a 20 6e 6f 72 74 68 20 65 61 73 74
 south west ].[0   20 73 6f 75 74 68 20 77 65 73 74 20 5d 1b 5b 30
;37m....           3b 33 37 6d 0a 0d 0a 0d
Australia Forum Administrator #3
There's more to this than meets the eye. For one thing I see an extra line:


The Grand Plaza
{coords}0,30,20
  In contrast to the somber reputation of the arch marking the western
** MY NOTE **hmm

entrance to the plaza, the Sunrise Arch is a place to celebrate new life. 
When the New Year's procession winds through the streets, it is tradition
for the celebrants to write their worries of the old year on scrolls and
toss them into a fire built under the arch.  The stone has been stained
black with the soot of a million fears, and each sunrise is a pyre to
consume them again.  At night, the arch is a favored meeting place of
conspirators.  All varieties of hooded figures skulk here by night, before
wandering off to conduct their business.  
[ Exits: north east south west ]


Are you detecting "{coords}0,30,20" in a trigger and omitting it from output?

Second, I get (almost) the same output whether or not the Simulate "" line is there.

[EDIT] With the simulate there, there is one more blank line:


The Grand Plaza
  In contrast to the somber reputation of the arch marking the western
** MY NOTE **hmm


entrance to the plaza, the Sunrise Arch is a place to celebrate new life. 
When the New Year's procession winds through the streets, it is tradition
for the celebrants to write their worries of the old year on scrolls and
toss them into a fire built under the arch.  The stone has been stained
black with the soot of a million fears, and each sunrise is a pyre to
consume them again.  At night, the arch is a favored meeting place of
conspirators.  All varieties of hooded figures skulk here by night, before
wandering off to conduct their business.  
[ Exits: north east south west ]
Amended on Sat 19 Nov 2016 03:06 AM by Nick Gammon
Australia Forum Administrator #4
This is what you are sending:

Quote:

\1b[1;32mThe Grand Plaza\1b[0;37m\0a\0d{coords}0,30,20\0a\0d\ff\fa\c9room.info { "num": 32443, "name": "The Grand Plaza", "zone": "aylor", "terrain": "hellfountain", "details": "", "exits": { "n": 32454, "e": 32444, "s": 32457, "w": 32442 }, "coord": { "id": 0, "x": 30, "y": 20, "cont": 0 } }\ff\f0 \1b[0;37mIn contrast to the somber reputation of the arch marking the western\0a\0dentrance to the plaza, ...


Truncated past the relevant bit. Now the odd thing is, using the debugger, that when you get the GMCP stuff and you do the AnsiNote that it fails to be sent immediately because the line length is 70, not zero as expected. Also the line contents are " In contrast to the somber reputation of the arch marking the western".

I can't quite explain this. It's as if the room.info stuff was deferred until the end of line, but that shouldn't happen.
USA Global Moderator #5
Quote:
Are you detecting "{coords}0,30,20" in a trigger and omitting it from output?

Yes, sorry. I can do a fully clean test without that in a day or so. Possibly sooner.
Amended on Sat 19 Nov 2016 02:42 PM by Fiendish
Australia Forum Administrator #6
If I omit that line from the test packet, the problem persists.
Australia Forum Administrator #7
I can reproduce the problem now. I had your TextSubstitution plugin installed (from an earlier test) and that was re-arranging the order of stuff in the packet. You may want to look at that as a possible issue, as it moved the GMCP stuff to after the first line. That meant that now the line length wasn't zero.

The order of the plugins appears to influence this too.


029152CC  1B 5B 31 3B 33 32 6D 54 68 65 20 47 72 61 6E 64 20 50 6C 61 7A 61 1B 5B 30  .[1;32mThe Grand Plaza.[0
029152E5  3B 33 37 6D 0A 0D 7B 63 6F 6F 72 64 73 7D 30 2C 33 30 2C 32 30 0A 0D 20 20  ;37m..{coords}0,30,20..  
029152FE  1B 5B 30 3B 33 37 6D 49 6E 20 63 6F 6E 74 72 61 73 74 20 74 6F 20 74 68 65  .[0;37mIn contrast to the
02915317  20 73 6F 6D 62 65 72 20 72 65 70 75 74 61 74 69 6F 6E 20 6F 66 20 74 68 65   somber reputation of the
02915330  20 61 72 63 68 20 6D 61 72 6B 69 6E 67 20 74 68 65 20 77 65 73 74 65 72 6E   arch marking the western
02915349  FF FA C9 72 6F 6F 6D 2E 69 6E 66 6F 20 7B 20 22 6E 75 6D 22 3A 20 33 32 34  ÿúÉroom.info { "num": 324
02915362  34 33 2C 20 22 6E 61 6D 65 22 3A 20 22 54 68 65 20 47 72 61 6E 64 20 50 6C  43, "name": "The Grand Pl
0291537B  61 7A 61 22 2C 20 22 7A 6F 6E 65 22 3A 20 22 61 79 6C 6F 72 22 2C 20 22 74  aza", "zone": "aylor", "t
02915394  65 72 72 61 69 6E 22 3A 20 22 68 65 6C 6C 66 6F 75 6E 74 61 69 6E 22 2C 20  errain": "hellfountain", 
029153AD  22 64 65 74 61 69 6C 73 22 3A 20 22 22 2C 20 22 65 78 69 74 73 22 3A 20 7B  "details": "", "exits": {
029153C6  20 22 6E 22 3A 20 33 32 34 35 34 2C 20 22 65 22 3A 20 33 32 34 34 34 2C 20   "n": 32454, "e": 32444, 
029153DF  22 73 22 3A 20 33 32 34 35 37 2C 20 22 77 22 3A 20 33 32 34 34 32 20 7D 2C  "s": 32457, "w": 32442 },
029153F8  20 22 63 6F 6F 72 64 22 3A 20 7B 20 22 69 64 22 3A 20 30 2C 20 22 78 22 3A   "coord": { "id": 0, "x":
02915411  20 33 30 2C 20 22 79 22 3A 20 32 30 2C 20 22 63 6F 6E 74 22 3A 20 30 20 7D   30, "y": 20, "cont": 0 }
0291542A  20 7D FF F0 0A 0D 65 6E 74 72 61 6E 63 65 20 74 6F 20 74 68 65 20 70 6C 61   }ÿð..entrance to the pla
02915443  7A 61 2C 20 74 68 65 20 53 75 6E 72 69 73 65 20 41 72 63 68 20 69 73 20 61  za, the Sunrise Arch is a
0291545C  20 70 6C 61 63 65 20 74 6F 20 63 65 6C 65 62 72 61 74 65 20 6E 65 77 20 6C   place to celebrate new l
02915475  69 66 65 2E 20 0A 0D 57 68 65 6E 20 74 68 65 20 4E 65 77 20 59 65 61 72 27  ife. ..When the New Year'
0291548E  73 20 70 72 6F 63 65 73 73 69 6F 6E 20 77 69 6E 64 73 20 74 68 72 6F 75 67  s procession winds throug
029154A7  68 20 74 68 65 20 73 74 72 65 65 74 73 2C 20 69 74 20 69 73 20 74 72 61 64  h the streets, it is trad


Notice that the first line is moved before the room.info stuff, however the CR/LF is after it. Thus the line isn't empty.
Australia Forum Administrator #8
I think I've found why the line was split. The code for deciding whether to defer the note was checking if the line was empty. Thus the first Tell (effectively inside the AnsiNote) was for an empty line so it appeared first. Then the second one found the line wasn't empty so it was deferred.

I've changed it to keep allowing non-deferred notes if the current line is not an output line, regardless of its length. I think that should work.
Australia Forum Administrator #9
Fixed, I think, in version 5.04.

https://github.com/nickgammon/mushclient/commit/7290cfe

Notice that you will still have to fix the re-ordering of the GMCP sequences in your TextSubstitution plugin.

Also completely re-wrote AnsiNote because it was so complicated I couldn't even follow what it was doing. :)
Australia Forum Administrator #10

-- Subnegotiations go back in just before the EOL so they
-- get processed by MUSHclient in the right sequence.


I think you'll have to change that to "just after" so that they occur at the start of a line, not the end of the previous one.
USA Global Moderator #11
Testing 5.04 now. I'm getting a bit of style bleed.

This picture is using the same small plugin as before but with slight change from AnsiNote to ColourNote white on red, but it also happens with AnsiNote using eg AnsiNote(ANSI(0,38,5,33).."** MY NOTE **"..ANSI(41).."hmm\n"):

https://imgur.com/emmMhfl
Amended on Sat 10 Dec 2016 01:36 PM by Fiendish
USA Global Moderator #12
Fiendish said:

Testing 5.04 now. I'm getting a bit of style bleed.

This picture is using the same small plugin as before but with slight change from AnsiNote to ColourNote white on red, but it also happens with AnsiNote using eg AnsiNote(ANSI(0,38,5,33).."** MY NOTE **"..ANSI(41).."hmm\n"):

imgur.com/emmMhfl

It actually looks like the next line after isn't being considered its own new line? Because the bleed appears to be interfering with an omit trigger as well. Something that should be getting omitted is being shown but using the bled color from the note.

http://imgur.com/a/3Ib6E

On Aardwolf I have `tags roomdescs on` set and then triggers that omit {rdesc} and {/rdesc}

<triggers>
<trigger
   enabled="y"
   match="{rdesc}"
   omit_from_output="y"
   sequence="100"   
   send_to="12"
></trigger>
<trigger
   enabled="y"
   match="{/rdesc}"
   omit_from_output="y"
   sequence="100"   
   send_to="12"
></trigger>
</triggers>


But you can see in the image that when gmcpdebug 1 is active the {rdesc} shows up in the note color.
Amended on Fri 09 Dec 2016 08:02 PM by Fiendish
USA Global Moderator #13
Maybe https://github.com/nickgammon/mushclient/pull/27/files
Australia Forum Administrator #14
Looks reasonable, I assume it worked in testing? I merged the commit.
USA Global Moderator #15
It seemed to work in testing on Windows 10.
I can't figure out how to make Visual Studio Community 2015 build an executable that works in XP or Wine as a drop-in .exe replacement no matter what project settings I poke at.
Australia Forum Administrator #16
You mean, it won't compile, or it compiles and works under Windows 10 but not Windows XP?
USA Global Moderator #17
I can compile it no problem and it works in Windows 10 without changes.
But I haven't yet produced an executable that runs in XP without also transporting a bunch of extra DLLs along with it.
Australia Forum Administrator #18
I tried to compile, and it said (under XP) that it wasn't a valid Win32 application.

Ah well, that's Windows for you!
Australia Forum Administrator #19
I'm a little surprised it worked for you without this change:

https://github.com/nickgammon/mushclient/commit/e7b539f
USA Global Moderator #20
Nick Gammon said:

I'm a little surprised it worked for you without this change:

https://github.com/nickgammon/mushclient/commit/e7b539f

O_o
Hah. Should I guess that this happened to you? It didn't happen to me. ^_^
Amended on Wed 14 Dec 2016 10:27 AM by Fiendish
Australia Forum Administrator #21
I probably have a Note or similar in a plugin, outside any function (in my default testing world). That would be called early in the program startup sequence, before the main output window is created.
USA Global Moderator #22
Nick Gammon said:


-- Subnegotiations go back in just before the EOL so they
-- get processed by MUSHclient in the right sequence.


I think you'll have to change that to "just after" so that they occur at the start of a line, not the end of the previous one.


Hehe. You might be interested, my current iterations on that project are:
https://raw.githubusercontent.com/fiendish/aardwolfclientpackage/development/MUSHclient/worlds/plugins/text_substitution.xml
https://raw.githubusercontent.com/fiendish/aardwolfclientpackage/development/MUSHclient/worlds/plugins/simplified_text_substitution.xml

The first filters both main output and GMCP comm.channel.msg in OnPluginPacketReceived.
The second does only main output via a maximum priority catchall trigger and lets other plugins opt-in with CallPlugin("48f867c18f6ff1d6d3b52918", "filter_string", msg)
Both are still in testing. I expect some fragility. [1]

I'm thinking about doing a third one, which is the middle ground between the two, that does GMCP comm.channel.msg in OnPluginPacketReceived and main output with a trigger.

[1] - e.g. I just got a report from a tester using the first one of the following error. Could be coincidence, could be not. :
GMCP DATA ERROR: C:\awmcd\MUSHclient\lua\json\decode\util.lua:35: 
unexpected character @ character: 11 0:11 ["] line:
{ "name": "
\\;ndo", "class": "Thief", "subclass": "Assassin", "race": "Dark elf", 
"clan": "boot", "pretitle": "", "perlevel": 2000, "tier": 0, "remorts": 0, 
"redos": "0" }
Amended on Sat 17 Dec 2016 01:26 PM by Fiendish
Australia Forum Administrator #23
Perhaps we are seeing why MUSHclient doesn't automagically allow substitutions inside the incoming text. It's hard to get right.
USA Global Moderator #24
Nick Gammon said:

Perhaps we are seeing why MUSHclient doesn't automagically allow substitutions inside the incoming text. It's hard to get right.

Ehhhhh...we're pretty smart. I bet other things are harder.
Australia Forum Administrator #25
What, like world peace?
USA Global Moderator #26
Nick Gammon said:

What, like world peace?

Nah, basic gagging/replacement code isn't actually that hard. I've chosen to complicate things somewhat by allowing for gsub-like patterns, color codes and resets, and external functions for conditional replacements through a proxied CallPlugin interface. Because why do something simple when you can do something awesome?

Actually the only reason why I still don't have the simplified version fully working yet is because of having to work within MUSHclient's triggering framework (I keep failing to reliably conditionally omit, halt trigger evaluation, and also simulate, while working together with other plugins that also want to simulate).
Amended on Mon 19 Dec 2016 05:51 PM by Fiendish
USA Global Moderator #27
Fiendish said:

Nick Gammon said:

What, like world peace?

Nah, basic gagging/replacement code isn't actually that hard. I've chosen to complicate things somewhat by allowing for gsub-like patterns, color codes and resets, and external functions for conditional replacements through a proxied CallPlugin interface. Because why do something simple when you can do something awesome?

Actually the only reason why I still don't have the simplified version fully working yet is because of having to work within MUSHclient's triggering framework (I keep failing to reliably conditionally omit, halt trigger evaluation, and also simulate, while working together with other plugins that also want to simulate).


Though I think I have all the kinks worked out now pending the fix for dangling styles.