Friday, December 11, 2020

The Communications Hub and a very annoying SL bug

Today, one of my Hubs stopped working after a sim restart.   Mesmerizers and Communicators just wouldn't connect to it when the sim came back.  Luckily, I recognized this as the re-appearance of an SL bug that dates back for at least 15 years.

CHAOS devices (Mesmerizers and Communicators) talk to the Communications Hub using HTTP - the same protocol that a web browser uses to retrieve web pages.   To do that, they have to know the URL of the Hub they want to talk to.  In Second Life, URLs for objects are allocated on-demand, and when a sim is restarted the Hub needs to ask for a new URL, and it will be allocated a different URL each time it asks.  So how do Mesmerizers and Communicators find out the current URL of their Hub?  Well, they ask the Hub. But obviously they can't use HTTP to do that, since they don't know the URL.  Instead they use email.

Every object in SL has the potential to receive email.   The object's key becomes part of its email address, and since keys of rezzed objects don't change on a sim restart, a Hub always keeps the same email address, so long as you keep it rezzed.   If you take it back into inventory and then re-rez it, it will have a different key, and therefore a different email address, so the Mesmerizers it is supposed to serve will no longer be able to find it.   That's why you have to keep Communications Hubs rezzed - so they keep the email address that their connected devices know.  When a device needs to talk to a Hub for the first time, it sends it an email asking it for its current URL, and the Hub replies with its URL (over HTTP).

So what's the bug?  Well, every so often, it appears that some in-world objects suddenly stop receiving email.  And that's what happened to my Hub.  The Hub goes to check email every few minutes, and it was doing that, but finding no new emails.  As a result, the devices trying to connect couldn't get the Hub's new URL, so they couldn't communicate with it.

As I said, this bug has been around for over fifteen years, but it seemed to stop happening around 7 years ago, although there was no indication that a fix had been made.  And it appears that indeed, no fix has been made, since it happened again today.  Anyway, there is a workaround, if it happens to one of your Hubs.  In fact there are three.  But before you try any of these, click on the offending Hub and choose "Restart" - if the issue is something other than the bug under discussion here, a restart of the Hub may fix it, and will allow your users to connect once more.   If that doesn't work, you can try one of the following:

  • If you own your sim, or are on friendly terms with the actual owner, you can request that the sim be restarted.   This method may permit the queued emails to start flowing once again, but it doesn't always seem to clear the condition.  But it has the lowest impact on your users, and as people are used to weekly sim restarts, it's worth trying first if the simple Hub restart above doesn't help.
  • If you don't have many users on your Hub, you could simply take the Hub back into inventory (save the user database to the Users notecard first) and then rez a fresh instance.  Since re-rezzing will give the new instance a fresh key, the users will have to reconnect by physically coming to the Hub, clicking it and choosing "Connect".
  • If you'd rather not bother your users, there is an alternative.  First, pick up the Hub.  This doesn't mean take it into inventory - instead you're going to actually attach it to your avatar.   To do that, right-click the Hub and choose "Attach" from the pie-menu.  You will have to select an attachment point, and the Hub will attach to the point you select.  Picking up an object this way doesn't change its key.  Once you have the Hub attached, teleport to a different sim.  Teleporting doesn't change the key of worn attachments, so the Hub will retain its email address through all of this.  Then teleport back to the original sim - again the key and email address of the Hub will be preserved.  Finally, right click the (worn) Hub and choose "drop" to drop it back onto the ground.   Then click on the Hub and choose "Restart".

It appears that bringing an object into a sim has a side-effect of clearing whatever condition is preventing it receiving the emails.  In fact, after performing this operation, when the Hub first checks for email it will retrieve all the emails that had been sent, and it may take a while to work through those and get to current requests.  The Restart at the end is necessary to force the Hub to obtain fresh HTTP URLs once it's back in its home sim.

None of these workarounds is particularly straightforward, but until the Lindens deal with this bug, they are the only options I know of.   Given that I've been running six Hubs in my own sim for years, and haven't encountered this issue for at least 7 years, it seems to be a pretty rare bug.  However, it is possible that its re-emergence today may be related to some change the Lab has made, in which case it might recur more frequently going forward.  The possible good news is that, unless it happens soon after a sim restart, there's a good chance that CHAOS devices will have already obtained the current URL of their Hub by the time that the email path stops working, so if your users regularly log in to SL, they may not be affected by this bug.

Tuesday, December 8, 2020

The Enforcer 1.1 and CHAOS Scheduler Preview

 In a previous post, I mentioned my intent to support the scheduling of events within The Enforcer.  This is something that's been requested, and there are several interesting use-cases for such a feature.  So I had started work on building a scheduler into The Enforcer that could generate events on a predefined schedule.  However, when quite a way along with this effort, I had an idea if how to do it rather more elegantly.

There are two very distinct types of scheduled events we might want to support: ones that occur on a schedule (e.g. "every day at 10am"), and ones that are one-shot and occur after a delay (e.g. "in ten minutes from now").  The latter could be used to initiate a trance every so often, or to check whether the sub is making progress with some task;  the former is suitable for "housekeeping" tasks, particularly in situations where you don't really care which sub(s) the event runs for.

The Enforcer already has a crude way of doing the second type of event - the !delay command.  While this doesn't generate a new event but merely suspends the current event for the specified interval, that may often be sufficient, and if a separate event really is needed, the event could be generated immediately via the !event command, and then the notecard that processes that event could begin with a !delay command.  The only real problem with this is that the context that represents the event within The Enforcer hangs around for the duration of the delay, taking up resources.  So a variant of the !event command that fires after a delay (rather than immediately) would be useful.  This can be used both for one-shot events, or for repeating events (the event would schedule another future event each time it runs), and would typically run on behalf of the sub who caused the original event.  

The first type of event - scheduled for a specific time and then possibly repeating - is much more complicated.   There's a lot of complexity around handling date-times, that really has no place in The Enforcer.  And this type of event, being independent of an particular sub logging in, would presumably want to continue to run after a reset of The Enforcer.

I have also been thinking about teaching The Enforcer to run Mesmerizer trance scripts.  The Enforcer can be used for trances today, but it's clumsy in that you need to do a manual delay after every piece of text, and it's easy to forget that trance text needs to be sent within a ^text or ^text2 command.  The Mesmerizer script format is designed to make creating trances as simple as possible, and it would be very useful if The Enforcer could play them back unchanged.  But doing this within The Enforcer itself would mean that it would now have to support two scripting languages, which would very likely introduce bugs and increase memory use.

So, instead of adding these new features directly into The Enforcer, I have decided to add a remote control interface to The Enforcer, so that functionality like this - generation of events, and playback of trances - can be done by new individual products that connect to The Enforcer.  This means that only those people who want to make use of these functions will be impacted by them.  In addition, it opens the door to various custom add-ons to The Enforcer.  I plan to make available a simple toolkit that will permit LSL scripts to easily be connected to The Enforcer to allow for the easy construction of custom controllers.

So in addition to the "future event" capability, The Enforcer 1.1 will add the concept of "External Controllers".  The Enforcer will be able to scan the sim for new controllers and, if found, can connect to them.  Once connected, a controller can send commands to The Enforcer to generate events, or to be forwarded to a logged-in user's Mesmerizer.  At the same time, I will launch the CHAOS Scheduler as a new product.  The trance reader will come somewhat later. 

The CHAOS Scheduler offers the ability to generate Enforcer events according to a timetable set out in a notecard in the Scheduler's inventory.  Events can be repeating or one-shot, and since they're defined in a notecard, they will survive script resets, and will continue to operate if the Scheduler is taken back into inventory and then re-rezzed (although the new instance will have to be manually connected to The Enforcer before the events will have anywhere to go).

Saturday, October 10, 2020

Mesmerizer 1.5 release

Mesmerizer 1.5 is up in vendors and on Marketplace.  You can get an update through any CasperVend vendor, but if you visit one of mine (see "vendor links" in the right side-panel of this blog) your CSD purchases will be right at the top of the page, which makes it easy to find the Mesmerizer to ask for a redelivery.

The new features in Mesmerizer 1.5 have been discussed in the previous two or three posts - the randoutfit, menu, menu2, maygroup and getgroup commands, and some bugfixes (in particular one in the RLV relay that caused problems in ask and owner-ask modes - you should definitely update if you use the Mesmerizer's RLV relay in one of these modes). 

I have a relatively short list of new functionality that didn't make it into 1.5 that I will be working on very soon, but I wanted to get 1.5 out since the menu commands were what drove some of the new functionality in The Enforcer 1.0.

Friday, September 18, 2020

The Enforcer - release post-mortem and upcoming Mesmerizer 1.5 release

Hot on the heels of The Enforcer 1.0, I will soon be releasing Mesmerizer 1.5.  This is only a minor version update, with the significant new features being the randoutfitmenu, menu2maygroup and getgroup commands described earlier.  In addition to these new features, the on-text event now sets a variable called text when it's invoked, and there are a couple of bugfixes to the RLV relay.

I hope that the Mesmerizer 1.5 release goes more smoothly than The Enforcer 1.0.  For the latter release, I found two late-breaking bugs that I'd introduced in my performance enhancements, that meant I had to take down The Enforcer from MP and the vendors while I fixed them.  The good news is that, in this week's rolling restarts, LL seems to have addressed the script performance issues that were introduced a few weeks ago, and which forced me to delay the 1.0 release of The Enforcer while I made compensating performance improvements.  Now that raw script performance seems to be back to more-or-less how it was originally, with those performance enhancements The Enforcer simply flies - in fact it runs so fast that after the restart I had thought at first that it was completely broken, because contexts were completing and disappearing before I noticed them.

If you enable statistics on The Enforcer ("Stats On" in The Enforcer's menu), it will display and continuously update some information in hovertext above The Enforcer.  The first line of this hovertext gives the name of The Enforcer, and you should probably change this from simply "The Enforcer" to something more specific.  That will simplify things in the future if you ever find a reason to rez a second Enforcer (for example, to share traffic).  The second line of the statistics display shows how many Hubs are being monitored, and how many users are currently logged in across all those Hubs.  The third line shows the number of contexts and the number of namespaces being maintained.

I've discussed namespaces in previous posts.  The statistics display shows the total number of namespaces (persistent plus transient).  Contexts are the objects that The Enforcer uses to represent a notecard that's currently being executed.  So when The Enforcer is idle, the number of contexts shown should be 0, and the number of namespaces shown should be just the persistent namespaces that have been created, although there is a small delay between a context terminating and the associated transient namespace being deleted.

I have posted a reference guide to programming The Enforcer in the stickies to the right.  I intend to do something similar for the Mesmerizer, although because the Mesmerizer has a huge number of commands it will take a while to put them into a sensible structure for documentation.  In the mean time, also in the stickies is the "Useful Blog Pages" link, which includes a live query that will return all the blog pages flagged as containing Mesmerizer command documentation.

Once Mesmerizer 1.5 is out, I expect the next version of the Mesmerizer to be mostly a performance update.  I also plan to add at least the randoutfit and maygroup commands to the Owner HUD.  And in the next release of The Enforcer, I intend to add a scheduler, so that you can cause scripts to run at specific times, rather than just in response to external events.  Those are my current plans;  in almost all previous releases, I've ended up inserting additional functionality as a result of continued use of the current versions of the CHAOS products, and in response to user requests.  If you have an idea for a feature, please tell me about it in an IM in-world.

Friday, September 4, 2020

Enforcer 1.0 release

Enforcer 1.0 is going up in vendors as I type this, and will be on MP shortly.  Incidentally, regardless of whether you purchased a copiable CHAOS product on Marketplace or through a vendor, you can always get a redelivery of the latest version of that product through any CasperVend vendor.   If you use one of my vendors (locations given in the stickies to the right), your purchases from me will be collected at the top of the page, so you can easily find the item you're looking for.  I generally release new versions in vendors first, with MP listings lagging, so if you buy something on MP, it's worth getting a redelivery from CasperVend right away to make sure you have the very latest version.

Most of the new features in Enforcer 1.0 have already been discussed here and here, but there is one additional new feature that slipped in before the release cut-off, as well as a a new standard namespace, and a minor change to observable behavior relating to empty strings and undefined variables.  Also, just as I was preparing what I expected to be 1.0, the latest rolling restart seemed to have introduced some significant script performance issues, which were bad enough that I felt I needed to make a few compensating changes to The Enforcer.


Event command

The new feature is the ability to create an event from inside another event.  This is achieved by the new !event command.  The event command takes three parameters: the name of the event, the canonical legacy name of the user the event should run under, and a series of initial variable values, separated by semicolons.  As an example, the following command:

!event MyEvent::Nue Broome::answer=42;question=unknown

will generate an event called "MyEvent" for user "Nue Broome" (which will look in order for notecards called "MyEvent:Nue Broome", "MyEvent:*" or "MyEvent" and run the first one that it finds), and it will populate the new event's namespace with two variables: answer and question, initialized with the values "42" and "unknown" respectively.

The event command is a programmatic version of the Event choice on The Enforcer's menu, the only difference being that variable initializations are separated by semicolons in the event command, whereas they're specified on individual lines when creating an event via the menu.  Also, the individual variable values in the event command can be enclosed in double or single quotes, in case you want to include a semicolon in a value, or use leading or trailing spaces around the value (without quoting the value, such external whitespace would be removed).

So why would you want to create an event from inside another event?  The most important use for this is to cause activity in one user's Mesmerizer when a second user's Mesmerizer (or communicator) signals an event.  For example, let's assume that I have a sub called "Jane Doe", and I want to have Jane automatically teleport home if she's online when I log in.   Assuming that I always wear either a Communicator or a Mesmerizer, then a Logon event will be generated whenever I connect to a Hub, and this will happen when I log in to SL, and so this event would seem like a good place to teleport Jane home.  The problem is that the Logon event is running for me, and so it can't talk to Jane's Mesmerizer.   But if my Logon event were to generate a custom "SummonHome" event for Jane, that event would run on behalf of Jane and could send her Mesmerizer a tpto command to bring her home.  So the notecards involved would be:

----- Logon:Nue Broome -----

!event SummonHome::Jane Doe::


-----  SummonHome:Jane Doe -----

^tpto "My Home Sim (128, 128, 25)"


If Jane were not logged in, then the tpto command would simply be dropped (since her Mesmerizer would not be rezzed and listening for commands).

If you wanted to perform a number of tasks in the event running for Jane, you might want to make sure that she is actually online first, both so that The Enforcer doesn't waste time sending commands when nothing's listening, and also in case Jane does log in while the SummonHome event is still running, which might cause her to be sent only the last few commands in the notecard.  One obvious way to do this would be the use the new !checkonline command - something like:

!checkonline JaneOnline::Jane Doe
!exitif $JaneOnline isfalse
^tpto "My Home Sim (128, 128, 25)"
...


However, a better method might be to have the SummonHome event tell Jane's Mesmerizer to signal that it's ready to accept those commands:

^signal BringMeHome "$$sim"

This will cause Jane's Mesmerizer to initiate an event called BringMeHome, and that event can then issue multiple commands (including the tpto to bring her home).  Note the use of the $$sim in the second parameter to signal.  If you had used $sim, then before sending the command to Jane's Mesmerizer, The Enforcer would attempt to replace the $sim with the value of an Enforcer variable called sim, which probably isn't defined, and so would expand to an empty string.  However, a double dollar sign causes The Enforcer to simply replace the $$ with $ and not attempt to perform local expansion on sim, resulting in the string $sim being passed to Jane's Mesmerizer.  "sim" is a predefined symbol inside the Mesmerizer, so when Jane's Mesmerizer executes the signal command, "$sim" will expand to the name of the sim where Jane currently is, and this value would be sent back to The Enforcer as the data parameter to Jane's BringMeHome event, which might, for example, avoid issuing the tpto if Jane was already in her home sim.

In the near future, I will introduce commands to allow events to be scheduled in advance.

System Namespace

I talked about Persistent Namespaces in a previous post.  There is now a standard Persistent Namespace, called "System".  Whenever the main script in The Enforcer is reset, a variable called StartTime will be set in the System namespace, and will contain the timestamp of the restart.

There is nothing special about the System namespace (other than it automatically being created and populated when The Enforcer is restarted), but you should avoid creating or modifying variables in it, as I may use it for additional internal purposes in future releases.

Expansion of undefined variables

Previously, if you tried to expand a variable that hasn't been defined, the expansion would simply return the name of the variable (followed by a semicolon to separate it from any surrounding text).  So if variable X is not defined, a use of "$X" in an Enforcer commands would be translated to "$X;".  As of V1.0, this is no longer the case - undefined variables will now expand to an empty string.  There is no difference between a variable that's not defined and one that is set to an empty string (in fact, setting a variable to an empty string will delete the variable entirely).

This is a much more logical way of handling unset variables, but it requires that empty strings be properly supported, for example as operands within expressions.  This should now be the case.  For instance, the following tests whether variable X is defined:

!if "$X" "" streq
...
!fi 

Either single or double-quotes can be used to group words into single operands in an expression (but you have to open and close the quoted text with the same character).

When The Enforcer sees a dollar sign in a notecard line, it will assume this is a request to expand a variable (except if there are two consecutive dollar signs, as discussed above).  The variable name is everything after the dollar sign, until (and excluding) a character that isn't allowed in a variable name (basically any character that's not a letter or a number).  This means that if you had the following variable definitions:

!setvar foo::FOO
!setvar bar::BAR
!setvar baz::BAZ

Then the following expansions would be made:

source expansion
$foo $bar $baz FOO BAR BAZ
$foo$bar$baz FOOBARBAZ
$fooBAR$baz BAZ
$foo;BAR;$baz FOOBAR;BAZ
$foo;;BAR;$baz FOO;BAR;BAZ

Note the third row, there is no separating character between "foo" and "BAR", so The Enforcer treats these as a single variable name "fooBAR", which is undefined, and therefore expands to an empty string.  If you need to immediately follow a variable expansion with an alphanumeric character, you can use a semicolon to mark the end of the variable name, as in the fourth line.  This terminating semicolon is removed during variable expansion, as shown (so if you actually want a semicolon to appear immediately following a variable expansion, you need to provide two semicolons, as in the fifth line).

Futures


V1.0 of The Enforcer is a significant advance over the pre-release version, in terms of both capacity and the power of the new language features.  I have ideas for improving both of these areas further, so I anticipate a V1.1 release in the not too distant future.  However, I also realize there's a need for better documentation for both The Enforcer and the Mesmerizer, so I intend to spend some time (after Mesmerizer V1.5 is released) on putting together reference manuals for both, here in the blog, where they can be kept current.  The reference manual for The Enforcer can be found here, and is also available via the stickies on the right of the page.

Sunday, August 2, 2020

More on Mesmerizer 1.5 and Enforcer 1.0

I've admitted before that one reason I maintain this blog is as an attempt to combat my natural inclination to procrastinate over new releases.  There is certainly some work involved in releasing a new version of a product, albeit much less than introducing a completely new product, and I'm sometimes reluctant to incur that overhead without a pressing reason such as a significant bug.

The problem with the upcoming releases of Mesmerizer 1.5 and The Enforcer 1.0 isn't so much procrastination as it is that I've kept coming up with ideas for exciting new features.  This is especially true of The Enforcer.  I had expected the 1.0 release to be primarily a stability release, with just a few minor functional enhancements that I mentioned earlier.  However, partly as a result of the refactoring work I've done on The Enforcer, I've been using it to perform much more complex programming tasks, and that has given rise to many ideas for enhancements.

And that is the secondary purpose of this blog - by pre-announcing an upcoming release, I sort-of commit myself to a time-frame for that release, which limits the amount of feature-creep that can slip into a given release.  I expect what I'm documenting below, along with the previous post,  to describe the complete set of new features in the two product updates.  I have more features in-plan, but they can wait for the subsequent release.


Mesmerizer 1.5

Random Outfits

One other new feature slated for the upcoming Mesmerizer 1.5 release is random outfits.  While there's nothing stopping you using it with a flat folder structure, this feature is meant for use in conjunction with hierarchical outfit folders.  The command, randoutfit, takes a single parameter - the path of a folder within the Outfits hierarchy.  This path must be a folder, not an outfit, so if you have only a flat folder structure, the only valid parameter is the Outfit root folder, or "/".  The command will pick an outfit at random from among the outfits below the give path, and will change into it.

randoutfit is primarily intended for automatic outfit selection upon login (or some other event), and allows the wearer to collect a group of candidate outfits beneath a folder, from which the command will pick one to wear.  It is particularly well-suited for use remotely via The Enforcer, since it saves you having to program multiple wearoutfit commands, and having to update those commands whenever the wearer changes their stored outfits.

As an example, assume that the wearer has created a folder for P9-style outfits under #RLV called #RLV/Outfits.  Inside that folder, they might create a subfolder called LoginOutfits, and inside LoginOutfits, they could put multiple outfits.

Assuming the LoginOutfits folder contains at least one outfit, a login trigger in their Mesmerizer, or a login event handler in The Enforcer could issue a randoutfit "/LoginOutfits" command, which would cause them to wear one of those outfits at random on each login.

If there is a hierarchy of folders beneath the given path, randoutfit will traverse the hierarchy.  It does this by making an independent choice at each folder level, and only if that choice selects a subfolder (rather than an outfit) will that subfolder be traversed.  Also, empty folders are ignored in the traversal.

The randoutfit command will inform its issuer which outfit was selected, which is especially useful if the wearer is remote.  That notification has been added to the existing wearoutfit command as well.


Random Outfits and the new menu command (described in a previous post) are part of a new thrust towards supporting more flexible Mesmerizer programming.  Such programming can be done either in Mesmerizer triggers or Enforcer notecards, but the main focus is on The Enforcer, since it's not subject to the memory limitations of the Mesmerizer, and can therefore support much more complex programming.  Also, conditionals are key to creating interesting programming, and while the Mesmerizer does have the executeif command, true conditionals are better served in The Enforcer with its much more flexible set of conditional commands.


Enforcer 1.0

Namespaces

As part of the refactoring to gain more memory overhead for The Enforcer, namespaces have risen to become much more concrete objects than they were previously.   I already mentioned them in the previous post without fully explaining what they are, so I thought I should do that here.

A namespace is simply a collection of variables and their values.  When a program notecard is executing within The Enforcer, each line is executed more-or-less in isolation, with the only real "context" being the variables that successive lines share.  Whenever an event causes a program notecard to begin execution, The Enforcer creates a new namespace for that execution, initialized with a few standard variables (like $name), and then that namespace is used by the individual lines of program.  This means that the variable definitions that a program makes are only visible within that particular execution of the program.  This is almost always what's wanted, although there are a few specialized use-cases where you might want variable values to persist across executions.

A new namespace is also created when a program invokes a subprogram via the "@" action-character.  For instance, if Notecard1 is running, and it executes a line that contains @Notecard2, then execution of Notecard1 will be paused, and Notecard2 will begin executing, using a copy of Notecard1's namespace.  Since Notecard2 is using its own copy of the namespace, any changes it makes to variables will not be visible in Notecard1 when Notecard2 exits.  Again, this is usually what's wanted, since it prevents Notecard2 from accidentally overwriting variables that might be important to Notecard1.  However, it does make it impossible for Notecard2 to pass information back to Notecard1.

To support returning values from called subprograms, I have decided to go initially with the "shared namespace" approach discussed in the previous post.  Enforcer 1.0 will introduce an alternative action-character, "&", to mean "call with the current namespace".   So the commands @Notecard2 and &Notecard2 each invoke Notecard2 as a subprogram, pausing the current program until execution of Notecard2 completes, but whereas @Notecard2 will give Notecard2 a copy of the current namespace, &Notecard2 will allow Notecard2 to share the current namespace, so that any variable changes that Notecard2 makes will be visible in Notecard1 after Notecard2 has returned.

This also offers a way to share variable definitions between scripts.  For example, supposed that you have an event script that, among other things, displays a "message of the day" when a sub logs in, via the Mesmerizer's text command.  When you want to change that message, you could edit the parameter of the ^text command in the login script, but it's cleaner to put the message into a variable.  That way, if you have multiple scripts that want to use the same message, you could specify the message in a single script called, say DayMessage, as follows:

!setvar motd::This is today's message

Then, any event script that wishes to use the message of the day would simply invoke the DayMessage script using the & call:

&DayMessage

This would define the variable motd in the event script's namespace, where it could be used in a ^text or !sendim command.   

This is a good way to collect definitions that are needed by multiple event scripts into a single notecard, and works well for static configuration data - the sort of data for which it's ok to edit a notecard when you want to change it.  However, it doesn't help if you need your scripts to be able to change data, and have those changes preserved across multiple events (or multiple invocation of the same event).  For instance, let's say you wanted to count the number of times that a particular sub has logged in.  To do that, you'd need to be able to set a variable in one event, and then read it in another.

Persistent Namespaces

Namespaces are usually created and destroyed automatically by The Enforcer as part of the data belonging to an event.  However, there is sometimes a need to preserve data across events, and persistent namespaces address that need.   A persistent namespace is just like any other namespace - a collection of variable names and their values - except that it doesn't "belong" to any specific event invocation.  Instead, it is explicitly created either via The Enforcer's menu, or by an event script.

Before I go into the details of creating a persistent namespace, I'll discuss how it's used.  Persistent namespaces have names, and for the purposes of discussion, let's assume that there exists a persistent namespace called "PreservedValues" that contains a variable called "counter" with the value "3".  To access that value, an event script would use the new !attach command as follows:

!attach PreservedValues::counter

What this does is create a regular variable called "counter" in the event's namespace, and set it to the value of the counter variable from the PreservedValues namespace.  If counter didn't exist in PreservedValues, it will be created, and initialized to an empty string.  This typically means that, if the counter variable is to be treated as an integer, you would follow the above !attach command with something like:

!if 'X$counter' 'X' streq
!setvar counter::0
!fi

The local counter variable can then be modified, and then put back into the PreservedValues namespace with the !detach command:

!detach PreservedValues::counter

When an event script executes an !attach command, the variable in the persistent namespace is "locked", in such a way that any other event attempting to invoke !attach on that same variable will be made to wait until the first event either completes its !detach (on the same variable), or terminates.  If a script terminates with some variables still attached, the corresponding variables in the persistent namespace are simply unlocked - they are not automatically updated.

Under the covers, !attach creates a link between the variable in the persistent namespace and the event script's own namespace, and the link is removed either when a script running with that namespace executes a !detach (which also copies the current value of the variable in the local namespace into the corresponding variable in the persistent namespace), or upon deletion of the local namespace (which occurs automatically soon after the last script using that namespace terminates).  The fact that the link is to the namespace (rather than to the script) means that if a lock is established in one notecard, it will be held by subprograms invoked with the & action-character, but not in subprograms invoked with the @ action-character.

Finally, if a script just wants to read the current value of a variable, without locking it, it can use the !get command:

!get PreservedValues::counter

This simply copies the variable from the PreservedValues namespace into the event's namespace, without performing any locking (or waiting for locks to be released).  

A persistent namespace can be created via the "Namespaces" button on The Enforcer's main menu.  This will prompt for the name of the namespace, and once it's created you can also define variables within it from the menu.  Persistent namespaces can also be created from an event script via the new !namespace command:

!namespace PreservedValues

This will create an empty namespace called PreservedValues.  If the namespace already exists, the command is a no-op.   To simplify namespace creation, I recommend creating a Startup notecard which contains !namespace commands for all namespaces your other event scripts use.  Startup can also use !attach/!detach pairs to initialize variables within the new namespaces.  Startup is not currently a defined event, although it may become one in the future, but you can manually invoke it by generating a Startup event via The Enforcer's menu.

Currently, the only way to delete persistent namespaces is via the menu.

I have several future additional uses in mind for persistent namespaces, but as I said above, I want to get the V1.0 release out before I turn to additional feature work.


Events

Here is a list of the current "standard" events that The Enforcer understands.


Event Description
Logon A user establishes a session with a Hub
Login A user wears their Mesmerizer, or logs in to SL while wearing their Mesmerizer
Logoff A user's session with their hub times out
Address A connected hub allocates a new HTTP address
Location A user moves within SL
Update Periodic "keepalive" event
Menu User makes selection from a menu
Custom Event User's Mesmerizer invokes the signal command


These events use the following notecards, and define the following variables:
Event Notecard(s) Variables
Logon Logon:<name> name
Login Login:<name> name
Logoff Logoff:<name> name
Address Address:<hubname> hubName, hubAddress
Location Location:<name>:<sim> name, location, sim, x, y, x
Update Update:<name>:<sim> name, location, sim, x, y, x
Menu Menu:<name> name, data
Custom Event Event-name:<name> name, data

The notecard names in the above table have a fixed part that corresponds to the name of the event (e.g. Location), and one or two qualifiers, separated from one another and from the event name by colons.   Qualifiers are indicated in the table by being enclosed in angle-brackets, but that's just to make them stand out in the table - the angle brackets should not be used in the actual notecard names.  These qualifiers limit the applicability of the notecard so that it is only invoked for the event when it occurs for the specified person or other qualifier.  For instance, a notecard named "Login:Nue Broome" would be invoked each time I logged in, but not if someone else were to log in.  To create a notecard that would fire for anyone logging in, use "*" in place of the name, i.e. a notecard called "Login:*".  Only the most specific notecard will be invoked for an event, so if The Enforcer contained both of the above notecards, Login:Nue Broome would be used whenever I log in, and Login:* would be used for everyone else.  The name of the user is considered more "specific" than the sim they're in, so if a Location event were fired for me in the "Sleepy Hill" sim, and The Enforcer contained the following Location notecards:  Location:*.*, Location:Nue Broome:* and Location:*.Sleepy Hill, then the notecard that would be invoked would be Location:Nue Broome:*.

Currently, the matching of notecard names is done in a case-sensitive manner, so a notecard called Login:nue Broome would not work.   I may lift that restriction in the future, so you should avoid using notecards whose names differ only in the case of some of their letters.  There is a new Validate option in The Enforcer's menu, which goes through all the notecards and verifies that the user-names provided are canonical Legacy Names with correct case (e.g."Nue Broome" or "Fenella27 Resident"), and suggests corrections for any that are not.

Wednesday, July 15, 2020

Another Preview - Enforcer V1.0 and Mesmerizer 1.5

Enforcer V1.0


As promised at the beginning of the year, I intend to release the product version of The Enforcer soon.  The main improvement over the preview version is better memory use, which results in improved stability, as well as some bugfixes.  I've also renamed the scripts so that they all begin with a tilde "~".  This makes the scripts appear at the bottom of the content tab, rather than being mixed in with user-created event notecards.

There are also some enhancements to the scripting language, described below.  

List Indexing

List indexing has been in the Mesmerizer for quite a while, but hasn't previously been available in the Enforcer.   As a quick recap to how it works, consider the string ":who:what:i dont know:".  This is an example of a list - a string starting and ending with colons, and containing elements separated by colons[1].  So the above is a list containing three elements, with the values "who", "what" and "I dont know".  Lists are used by the Mesmerizer in several places, notably the precision attachment stripping and outfits features, each of which generates a list which can then be indexed to refer to a specific attachment or outfit.

Assume that a variable called on contains the above string.  Then $on would be the entire list value ":who:what:I dont know:", and the individual elements can be obtained by using a dot followed by the number of the desired element.  So $on.1 would be "who", $on.2 would be "what" and $on.3 would be "I dont know".

The main reason I've added list indexing to The Enforcer is to better support the use of custom events, generated by the Mesmerizer's signal command.  A custom event allows for a condition within the Mesmerizer to send a signal to The Enforcer, which can then perform some action in response.  The signal command takes two parameters:  The name of the event to be signaled, and "parameter data", which is available in The Enforcer's event as a variable called "data".  So if a connected Mesmerizer signaled an event as follows:

signal "myEvent" "myData"

then this could be caught in an event notecard in The Enforcer called "myEvent:*".  Enforcer script within that notecard can use the standard variables such as $name to give the full name of the Mesmerizer wearer, or $location to give their position within Second Life.  Custom events may also obtain the parameter data that was passed to the signal command as $data.  In this case, $data would contain the string "myData".

The above is fine if you only need to pass a single parameter to the event handler, but it's clumsy if you need to pass multiple parameters.  The obvious solution is to pass a list as the parameter data, for example:

signal "myEvent" ":FirstParameter:2ndParameter:"

Now when the Enforcer event fires, $data would contain the string ":FirstParameter:2ndParameter:", and since this string is a list value, the event script could read the first parameter as $data.1, giving "FirstParameter" or the second as $data.2, giving "2ndParameter".

In addition to the dot-notation above for obtaining a specific element from a list variable, The Enforcer has also acquired an element operator that can be used within expressions.  element takes two parameters, the first being the list, and the second being an integer specifying the position within a list.  As an example, using the data variable above, the expression:

$data 1 element

would select the first element from the list in data, giving "FirstParameter", just like $data.1.  While the dot notation is part of variable expansion and therefore can only be used with a variable and a fixed integer, element is a proper operator, and can therefore be used with arbitrary values, for example the expression:

':who:what:why:' $x element

would evaluate to "who" if x contains 1, "what" if x contains 2 and "why" if x contains 3 (and an empty string if x contains something else). 

$daytime pseudo-variable

The Enforcer supports a number of "pseudo-variables" - objects that are accessed like variables, but which are actually calculated on-demand.  An example is $datetime which always returns the current date and time.

To better support the programming of curfews, I have added $daytime, which returns the number of seconds since midnight.  $daytime joins the existing set of time-related pseudo-variables:

  • $datetime - returns a date/time, e.g. "Mon 2018-07-02 21:45 PST"
  • $time - the time-part of the above, e.g. "21:45"
  • $day - the day-part of the above, e.g. "Mon"
  • $date - the date-part of the above, e.g. "2018-07-02"
  • $daytime - the number of seconds since midnight, e.g. "78325"
Note that while the other time-related pseudo-variables have a resolution of one minute, $daytime is accurate to one second.  All time values are expressed in the SL Time-zone (PDT/PST).


Online checking

I've sometimes wished that I could program different Enforcer responses depending on whether or not I'm online.  The new checkonline command allows for this.  Its syntax is very similar to the setvarex command:

!checkonline myVar::Nue Broome

This will set variable myVar to true or false depending on whether or not I'm currently online.  Subsequently, $myVar can be used in a conditional or other expression.

The first parameter, in this case myVar, gives the name of the variable to set;  the second parameter, in this case "Nue Broome", specifies the avatar to be checked.  You can specify names using either "Firstname Lastname" or "firstname.lastname" formats, and if lastname is omitted, "Resident" is assumed.  You can also specify a user key instead of a name.

Note that there is a bug in LSL dating back almost ten years that means that checkonline may occasionally indicate that a given user is online up to ten minutes after they have actually logged out.


Local Events

In addition to the scripting enhancements above, Enforcer events can now be generated locally, via the menu.  A new Event menu choice allows you to specify the name of an event, the intended target, and any required variable definitions.   This lets you invoke a programmed response at any time, rather than having to depend on the target's actions triggering the event.   It can also be used to invoke script notecards that aren't directly associated with a "normal" event.

Note that local events will only cause event scripts to run - they are not treated as "real" events by The Enforcer.  For example, if you generate a Login event for a user, it will cause the appropriate Login notecard to be invoked, but it will not cause The Enforcer to believe that the user is actually logged in.

You can use local events for a number of things, including creating notecards to perform actions that can be run on-demand, and testing normal event notecards by generating their trigger events.


Scoping and returning values

Unlike the above features which are already implemented and working, the scoping/return feature is still somewhat on the drawing-board, so it may not make the V1.0 release.  If it doesn't, it will follow pretty soon.

The issue is to do with calling one Enforcer notecard from another.  The @ action-character allows for this today: Notecard_1 can "call" Notecard_2 simply by containing the line:

@Notecard_2

This will pause execution of Notecard_1, and cause Notecard_2 to run.  When execution reaches the end of Notecard_2, or an !exit or !exitif command is encountered, Notecard_2 will stop, and Notecard_1 will resume from the line after the call.

This allows you to easily divide your Enforcer scripts into subroutines, contained in separate notecards.  For instance, you might have a notecard that sets up some RLV restrictions that you want to be applied to some subs on login.  You could put a series of ^mayXXX commands in the Login notecard of every sub that you want the restrictions to be applied to, but it's much better to create a single notecard, perhaps called "Restrictions", to hold those commands, and then have each sub's Login notecard call that Restrictions notecard.  That way, if you want to change the set of restrictions, you only have to edit one notecard.

When Notecard_1 calls Notecard_2 with an @Notecard_2 command, Notecard_2 is given a copy of all the variables that were defined in Notecard_1 at the point of call.  So Notecard_2 can use $name to get the full name of the user who caused the event to run, as well as read any variables that Notecard_1 explicitly set with !setvar or !setvarex.  But because Notecard_2 gets a copy of those variables, any changes that Notecard_2 makes won't be visible in Notecard_1 after Notecard_2 exits.

Normally, this is good, because it means that Notecard_2 can make use of any variables it likes without having to worry about accidentally overwriting a variable that Notecard_1 might be using.  However, it means that there's no way for Notecard_2 to pass data back to Notecard_1.  Providing a way to do that is what this feature is about.

I'm considering three different approaches, and may end up implementing any one, two or all three of them.

The first is simply to create a way for a notecard to indicate that its variables should be shared with a second notecard that it calls, as opposed to being copied.  That way, any changes to variables that Notecard_2 makes will be visible in Notecard_1 when Notecard_2 exits.  This is the simplest method, and would likely be realized by defining a new "action character", possibly "&", to indicate a call in which variables are shared rather than copied.  So while @Notecard_2 would call Notecard_2 with a copy of the current variables, &Notecard_2 would call Notecard_2 with the actual current variables.

The second approach is to provide a way for a called notecard to explicitly return a value to the calling notecard.  This would require a new command to indicate a call that also specifies a variable to receive a returned value, as well as variants of the !exit and !exitif commands that would return a value.  If more than one value needs to be returned, the list mechanism described above could be used, returning multiple values as elements within a single list value.

The third, and possibly most interesting approach, would allow for namespaces to be explicitly specified when reading or setting variables.  This is interesting because it has the potential to allow communication between separate events, which would allow complex conditions to be evaluated.

At the moment I'm leaning towards implementing both the first and second methods - the first because it's quite trivial to implement, and the second because it's "clean".  I will likely implement the first method, and see what its impact on memory usage is, and then decide whether to implement the second as well.

Summary

The Enforcer is easily the most complex product in the CHAOS range, not so much in terms of lines of code - that honor goes to the Mesmerizer - but in terms of the amount of concentrated effort that has gone into its creation.  It's also, as far as I know, unique within SL.   When I move it to full product status, this reality will be reflected by a small increase in price.  Those of you who purchased The Enforcer during the extended Beta period will, of course, be entitled to the product version (and future updates) at no additional cost.


Mesmerizer 1.5


Menu command

One reason to want to better support multiple parameters to events is a new Mesmerizer feature, currently slated for V1.5.  This is a way to allow the Mesmerizer to offer some degree of interaction with the wearer.  While the Mesmerizer (and The Enforcer) allow you to program responses to various external events (the wearer moving to a sim, for instance, or logging in at a particular time), it is sometimes desirable to give the wearer an option to override those programmed responses.  For instance, let's say that you have defined a trance sequence to be played to the sub upon login, and that this trance sequence takes 15 minutes to play.  If the sub wants to quickly drop in to SL to purchase something in a sale, or to pay rent, then being forced to sit through a 15 minute trance is likely to be intensely annoying.   It would be nice to display a menu to the sub on login, asking them whether they want to skip the trance on this login.  Alternatively, the trance script itself could display a menu at a convenient point within the trance, giving the sub the choice of skipping to the awakening portion of the trance.

There are many other situations where a programmed response might want to collect some information from the wearer to choose between different actions.

Mesmerizer 1.5 will introduce a menu command, which allows a trigger-rule (or Enforcer event) to display a menu from which the wearer has to choose a response.  There will likely be two variants of the menu command, a simpler one:

menu <menuName> <menuText> <menuChoices>

and a slightly more complex one:

menu2 <menuName> <menuText> <menuChoices> <timeout> <default>

<menuName> is a string that will be used to identify this specific menu.  <menuText> is the text that will be displayed at the top of the menu-box.  <menuChoices> is a list of button labels.  There must be no more than 12 elements in the list, and no element should be longer than 12 characters.  The menu2 command has 2 additional parameters:  <timeout>, which is a float that specifies how many seconds to wait for a response, and <default> which is a string that will be returned if the timeout expires.  The simpler menu command simply defaults <timeout> to being 300 (5 minutes) and <default> to be the string "timeout".

When the wearer makes a choice (or the timeout expires), the new on-menu event will fire, and the variable menuid will be set to whatever was specified for <menuName> in the menu or menu2 command, and the variable select will be set to the choice that the wearer made (or the <default> string, if the timeout expired).

As an example, the following command:

menu "myMenu" "Pick one of the following:" ":Yes:No:Maybe So:"

would present the wearer with a menu asking them to pick one of "Yes", "No" and "Maybe So".  When they choose one of those options, or the 5 minute timeout expires, the on-menu event will fire, with $menuid being set to "myMenu", and $select being the label they chose, or the string "timeout".

If the Mesmerizer is connected to a Hub monitored by The Enforcer, then an Enforcer event called Menu will also be generated.  In addition to the standard event variables such as name and location, this event will be passed a variable called data containing a list value with two elements: the menuName (in this case "myMenu") and the chosen response (or default value if the timeout expired).  So if the wearer had picked "No", the value in variable data in the Menu event would be ":myMenu:No:".  With the new list indexing support above, the menuName can be obtained as $data.1, while the selection made can be read as $data.2.


Additional group support

I added the setgroup command to allow the Mesmerizer to change the active group back in V0.99g.  V1.5 augments this with the maygroup and getgroup commands.

maygroup lets you control whether the wearer may change their active group.  Like most of the mayXXX family of commands, it takes a Boolean parameter to indicate whether or not the ability is allowed.  So maygroup no will prevent the wearer from changing from their current group, while maygroup yes will allow them to change their group.

getgroup tells you the name of the group they currently have as active.   This is now also included in the information returned by the report command.



[1] While list values should both start and end with colons, some list-handling functions are currently tolerant of improperly-formed list values that omit one or both of these colons.  A future enhancement to lists will likely make the leading and trailing colon mandatory everywhere, so you should not rely on this behavior, and instead ensure that all list values are properly introduced and terminated with a colon character.

Monday, May 25, 2020

CHAOS Owner HUD 1.0

The Owner HUD for the Mesmerizer is going up in my vendors as I type.  I'll add it to my Marketplace store (along with the Mesmerizer 1.4 update) very soon.   The owner HUD was described in a previous post, but I do want to reiterate how much it simplifies the use of the Mesmerizer.  Not all Mesmerizer features are available through the HUD, but it supports most of the things that I use on a regular basis, and actually does a few things that the command-line interface isn't directly capable of (the "Teleport to Me" function, and the ability to precision-strip multiple attachments in a single operation spring to mind).

Apart from the icons in the button-bar (which were explained in the previous post), the HUD is mostly self-explanatory.  The most indispensable function of the Owner HUD is for use when trancing the sub.   As I said the in the previous post, I always use the HUD for trancing.  The textbox used for typing trance text supports both multiline text and embedded "\n" line-break sequences, which makes it easy to paste text from, for instance, a Mesmerizer script, that uses "\n" sequences.  This makes it easy to combine text fragments from scripts with text you compose in real-time.  The 'clear' function is also very useful, to provide "pauses" between sections of trance text, to allow the sub to focus on the spiral without any distracting text from the previous section.  In general for trancing, I find that I mostly use just the button bar, with the Trance tab only used to control whether to generate the on-text event, which I use, as discussed in a previous post, to play a gentle chime when there's new text for the sub to read.

The HUD remembers its on-screen positions independently for minimized mode and bar/full mode.  This allows you to place the minimized form somewhere unobtrusive on your screen, while ensuring that the bar and full forms appear in a place where they won't interfere with other HUDs.   The default position for the bar/full mode is intended to be near the top-right, but below the place where textbox popups appear (the top-right of the screen is the default placement of these).  This is because a major use of the bar-mode is for trancing, and textboxes are employed repeatedly in this mode, so the HUD should be positioned so that it's not hidden by these textboxes.

As the HUD operates by generating command-line Mesmerizer commands, you can use it to discover some commands that you might otherwise be unaware of.   The Owner HUD listens on channel 99 (the Mesmerizer command channel) and displays to you what commands are being issued (including the commands that it itself generates).   These commands will be displayed in local to you (although they're not visible to anyone else), so they will end up in your chat log, if you wish to look at them later.  This allows you to keep a log of trances, as well as any other Mesmerizer commands you issue.

Monday, May 11, 2020

Mesmerizer 1.4 release, and Owner HUD

CHAOS Mesmerizer 1.4

CHAOS Mesmerizer 1.4 is going up in vendors now, and will be on Marketplace soon.  See the previous post for a description of all the new features.  In addition to the features already described, I will be including with Mesmerizer 1.4 a gift card for the CHAOS Trance Relay/Titler that I described in an earlier post, but never released.

CHAOS Titler

The CHAOS Titler is an optional device that can be worn by the sub to let people nearby see what text is being displayed on their screen (under the control of their hypnotist).  This is intended for use in a public, or demonstration trance situation, but can also provide very useful feedback to the hypnotist of what their trance text actually looks like.  This is especially useful with multi-line messages.  The on-screen text that is displayed on the sub's screen is duplicated by the Titler as hover-text over the sub's head.  This can be enabled or disabled by the publictext and nopublictext commands.  The publictext and nopublictext commands persist across logins.  To avoid confusing the sub during trance, they will not be able to see the hovertext, although those around them will.

In addition to this trance relay use, the CHAOS Titler can be used as a standard titler.  The two functions (title and trance text) are visually distinguished by the title (if any) appearing in white, above the trance text which appears in green.

The CHAOS Titler is a PAN device, and is therefore accessed from the Mesmerizer via the pansend and pansendpars commands.  It supports the following commands:

pansend titler lock - Prevents the titler from being detached
pansend titler unlock - Permits the titler to be detached
pansendpars titler text "some text" - displays "some text" in green.  Empty string to clear.
pansendpars titler title "new title" - sets the sub's title to "new title".  Empty string to clear.

The publictext and nopublictext commands make the Mesmerizer repeat (or stop repeating) all received text and text2 commands as PAN text commands to the titler.  The publictext and nopublictext commands were implemented in Mesmerizer 1.2, so that version or later is required to use the titler as a trance relay (although the PAN commands will work with earlier Mesmerizer versions).

To claim your free CHAOS Titler, simply wear the gift card and click on it.

CHAOS Owner HUD

While finishing up 1.4, I've also been working on the CHAOS Owner HUD.   A very early preview release of this has been available as a freebie in my vendors for a while, but the product version is a huge improvement. It won't be a freebie, but it won't be hugely expensive, either.

While the Mesmerizer is very powerful, it's also a pretty complicated thing to use.  As someone once almost said: "With great power comes great complexity".  But the Owner HUD is an attempt to make most of the common features of the Mesmerizer much easier to use.  The HUD doesn't address triggers - those still need to be defined via the command interface.  What it does do, though, is make many of the Mesmerizer commands easily accessible at the click of a pushbutton.  It also groups the various commands into related functions, which greatly simplifies finding things.  The HUD works seamlessly with the CHAOS Communicator, so it can control Mesmerizers in a different sim, provided they're connected to the same CHAOS Communications HUB as your Communicator.

The HUD has three forms or "modes".  The first is a tiny mode, which is a single button that sits unobtrusively at the top right of the screen:
Clicking that left-arrow expands the HUD into its second mode, the "Button Bar":

The right-most button on this bar shrinks the HUD back down to its minimized form for when it's not in use.  The next button, with a down-arrow, expands the HUD to it's full form.  I'll go through the rest of the main bar buttons from left to right.

The first (left-most) button lets you set the target - the avatar whose Mesmerizer will be controlled.  The floating text above the HUD always shows the current target, in this case "All", meaning that the HUD will control all Mesmerizers within range.  Clicking the Target button allows you to set the target to a nearby avatar, any of the most recently selected 9 targets, or a name you type in.

The next button with a microphone label opens a textbox in which you can type messages to be displayed in the center of the target's screen.  This is useful during trance, when typically the Mesmerizer would display a spiral with the trance text overlaid.  It can be used at any time, though, without a trance or spiral, which can be useful to send an "out-of-band" message to the target.

The microphone/text button works in conjunction with the next button, the one bearing a speech bubble icon.  This "speech" button specifies whether any text displayed via the microphone button will erase itself after about 15 seconds (the default behavior when the button is not pressed), or whether text should remain until either explicitly erased or replaced with new text.  In this mode, the speech button is either red or green (green when there's text being displayed on the target's screen, red to indicate that future text will be retained).

The next button, the Spiral, toggles on and off the Mesmerizer's spiral.   It changes to green when it emits a spiral command, and back to its default color shown above when no spiral is displayed.

The next two buttons - "trance faces" - correspond to the hypnotize and wake commands.

The next button presents a textbox where you can type any channel 99 Mesmerizer command to be sent to the target's Mesmerizer.

Finally, the menu button displays a menu for quick access to a few functions (teleporting to saved destinations, macro commands, and the ability to save or load the HUD's state information, for use during upgrades).

The Button Bar is aimed mostly at trance use.  It greatly simplifies trancing over use of the raw text and text2 commands, and I've found that I always use the HUD rather than the raw commands when performing trances.  For other Mesmerizer functions, you need to expand the HUD, by clicking on the down-arrow button, one in from the right.  This button causes a tab control to drop down beneath the Main Bar, and this tab gives access to the remaining functions of the HUD.  There are six tabs, of which only five are populated in the initial release.  The tabs are: Move, TP/Sit, Dress, RLV, Misc, and Trance.  I'll go through each of them.


Above is the Move tab which lets you control how the Mesmerizer wearer should move.  The top (graphical) row of controls causes them to turn in various directions (relative to how they're currently facing), lets you make them face a particular direction, or causes them to take step forward, back, left, right , up or down.   Other than these controls, the rest of the HUD is non-graphical, and uses labeled buttons to perform actions.

The first column of buttons causes the target to walk (in the direction they're facing), stop walking, walk to an object or avatar, or turn to face an object or avatar.

The second column causes the target to follow you, stop following you, come to you, or to turn to face you.

The third column lets you prevent the target from moving, allow them to move again, or animate them with a built-in or custom pose.

The TP/Sit tab consists of two columns of buttons.  The first column is mostly concerning teleporting the target (to you, to an SLURL that you type, or to a saved destination), allows you to save a landmark as one of those destinations, or lets you play back or edit user-defined macros.  As an alternative to using saved destinations or typing a SLURL, you can teleport the current target(s) simply by dragging a landmark onto the HUD.

The second column of buttons is mostly to do with sitting on objects by choosing from a list of likely objects, or by providing the key or name of the object, resitting on the current object, or standing up. The final button in this column displays the Status menu, which lets you get status information either about the target's Mesmerizer, or about the HUD itself.


The top section of the Dress tab has controls for Outfits, Attachments and Clothing Layers.  Each of these sections has a button to let you see what's available (Outfits) or currently worn (Attachments and Layers), and a button to change (Outfits) or strip (Attachments and Layers).  The Attachment Strip function supports the "Precision Stripping" feature introduced in Mesmerizer 1.3, where attachments can be individually and predictably removed, regardless of their attachment point.

The lower section of the tab allows for navigation of the #RLV shared folder.

The RLV tab allows setting or clearing of a number of restrictions, as well as listing the restrictions currently in force.
Finally, the trance tab contains buttons that duplicate some of the functions from the Main Bar (Hypnotize, Wake, Spiral, No Spiral and the various text options), but it also has buttons to control scripts, and to set whether emotes should be generated on entering or leaving trance, whether trance text should be made available to the CHAOS Titler for public viewing, and whether the on-text event should fire for received text or text2 commands.

The Misc tab currently has no controls on it, and is reserved for future use.


The CHAOS Owner HUD should be available in vendors and on MP within the next few days.

Thursday, March 26, 2020

Mesmerizer 1.4 preview

This post describes many of the new features planned for Mesmerizer version 1.4.  All the features described here have been implemented, and are in various stages of testing.  While it's possible that I may find a problem that results in one of these features being pulled from the 1.4 release, that's pretty unlikely.

Overlays

As mentioned earlier, one of the features added in Mesmerizer 1.4 will be support for overlays.  Overlays are a way of adding an image to the screen.  Unlike an image displayed on a HUD, you can't interact with an overlay image - you should think of it as part of the viewer window, rather than something that's displayed within the window.  Overlays are currently only available in RLVa, which means that they will only work in viewers that support RLVa (as opposed to RLV).  Firestorm and Catznip are two viewers that support RLVa, so overlays should work for a majority of SL users.

Overlays are an alternative to the image display that's always been a part of the Mesmerizer.  Regular images are displayed on the Mesmerizer HUD itself, and it's possible for the user to move them aside.  They also interfere with mouse activity, so you don't want to leave them up for very long as they significantly impair the sub's interaction with their environment.  Overlays don't have this issue - you can click through them, and they always fill the entire viewer window - they can't be dragged to one side.   The fact that they are always stretched to fill the viewer window is actually a bit of an issue when making textures for them, since even if you assume that the window is maximized, screens come in various different aspect ratios.  However, the 16:9 format is undoubtedly the most common ratio in use, and a texture made in this ratio isn't distorted too much if displayed on either a 4:3 or an ultra-widescreen display.  Textures made for display using the Mesmerizer's show family of commands, on the other hand, will always be displayed in a square aspect ratio, filling the window vertically, but leaving gaps on either side.

The Mesmerizer uses the underlying overlay mechanism to implement two distinct type of overlay: a static image and a slide-show.  For both types, you can specify how long the image takes to fade up, how long it will be displayed for and how opaque that display will be, and how long it will take to fade to transparent.

The slide-show format might be useful as an automatic background for trances.  You can start a slide show during the trance and leave it running for as long as you like, and it will cycle through the chosen images in sequence.

The static overlay can be used as a "frame" around the SL window, if you use a texture with a large central transparent region, and narrow decorated edges.   You can use frames as visual triggers, to continually remind the sub of suggestions you've made in trance.  Another use for static overlays is as a blindfold, to block or restrict the sub's view.   A single texture can provide a number of different levels of restriction, simply by varying the the opacity of the overlay.

Overlays are specified with two commands: overlay which defines a static overlay, and overlayslides which defines one or more images to display in a slide-show.  Each of these commands takes a single parameter, which consists of one or more subcommand strings of the form keyword=value, separated by colons.  While the parameter syntax of both commands is the same, the valid keywords are different for each.

The overlay command specifies a single image that can be displayed either for a specified length of time, or indefinitely.  Valid keywords for overlay are as follows:


Keyword Description Example
t Key of texture t=2c658bf0-414d-89b0-de6d-999d9cc3c6bd
tint R/G/B color. Default is 1/1/1 tint=0.5/0.5/0.5
alpha Opacity of image.  Default is 1.0 alpha=1.0
hold Number of seconds to display image at specified opacity.  Default is 0, or indefinite. hold=5.0
ramp Number of seconds to take to bring image to specified opacity.  Default is 0.5 ramp=3.0
show Boolean - whether to show the image immediately (default is yes) show=yes
clear Boolean - whether to clear the current image clear=yes
report Boolean - whether to report overlay properties report=yes

Apart from clear and show, subcommands are executed left-to-right.

If present, clear=yes will be executed before any other subcommands.   It is only useful if the remainder of the parameter does not cause a new image to be displayed, since display of a new image would automatically cause any previous image to be removed.

If present, show=no will be executed after all other subcommands.  If it is not present, then the image will be displayed immediately.

If present, report=yes will be executed as it is processed, and will report the current settings of the static overlay.

The other keywords set the values that will be used when a static overlay is displayed.

Some examples:


Parameter Effect
overlay "clear=yes" Remove the overlay
overlay "show=yes" Display the current overlay
overlay "t=2c658bf0-414d-89b0-de6d-999d9cc3c6bd" Display the specified texture as the overlay
overlay "t=2c658bf0-414d-89b0-de6d-999d9cc3c6bd:show=no" Save the specified texture as the overlay, but don't display it yet
overlay "alpha=0.5:ramp=5.0:duration=10" Display the previously-specified static overlay at half-opacity, taking 5 seconds to ramp up to that opacity, and remaining for 10 seconds


If hold is specified as 0, meaning indefinite, this means that the image will be displayed continuously, and will not be removed.  This is particularly appropriate for "frames", that are intended to decorate the viewer window, perhaps to act as a reminder to the sub of some desired behavior.  If there is a static frame set with an indefinite hold, it will automatically be redisplayed after any slideshow overlays are completed.



The overlayslides command specifies one or more images that can be displayed in sequence.  Valid keywords for overlayslides are as follows:


Keyword Description Example
t Key of texture.  The t subcommand may appear multiple times to specify multiple textures. t=2c658bf0-414d-89b0-de6d-999d9cc3c6bd
max Maximum number of slides. If more slides than this are specified, older ones will be discarded. Default is 16 max=10
tint R/G/B color. Default is 1/1/1 tint=0.5/0.5/0.5
alpha Opacity of image.  Default is 1.0 alpha=1.0
hold Number of seconds to display image at specified opacity.  Default is 5 secondshold=3.0
rampup Number of seconds to take to bring image up to specified opacity.  Default is 0.5 rampup=3.0
rampdown Number of seconds to take to fad image down after display.  Default is 0.5 rampdown=3.0
interval Number of seconds to pause between slides.  Default is 0.5 interval=3.0
run See belowrun=no
clear Boolean - whether to clear the current texture list clear=yes
report Either keys or parameters report=keys

Apart from clear and run, subcommands are executed left-to-right.

If present, clear=yes will be executed before any other subcommands.   It is only useful if the remainder of the parameter does not cause a new image to be displayed, since display of a new image would automatically cause any previous image to be removed.

If present, run=no will be executed after all other subcommands.  If it is not present, then the image will be displayed immediately.  The value for run can be either a Boolean or an integer.  As a Boolean, it simply enables or disables the display of all slides.  As an integer, it specifies how many slides to display in sequence.  So run=yes will cause all slides to be shown in rotation, until the slideshow is explicitly stopped via a new overlay or overlayslides command; run=3 will display the next 3 slides in sequence and then end the slideshow.

If present, report=keys or report=parameters will be executed as it is processed, and will report the current settings of the overlay slides, listing either all the texture keys, or the other slide parameters.

The other keywords set the values that will be used when the slides are displayed.

Some examples:


Parameter Effect
overlayslides "clear=yes" Clear the slides.  If running, the slideshow will stop after the current slide is finished.
overlayslides "run=yes" Run the slideshow
overlayslides "t=2c658bf0-414d-89b0-de6d-999d9cc3c6bd" Add the specified texture to the list of slides and start the slideshow.
overlayslides "t=2c658bf0-414d-89b0-de6d-999d9cc3c6bd:run=no" Save the specified texture to the list of slides, but don't start the slideshow.
overlayslides "alpha=0.5:rampup=5.0:duration=10" Change display parameters for future slides.


On-Text Mesmerizer event

Another new feature in 1.4 is the on-text event.  This is very similar to the existing on-commlink-message event, and will fire each time new text is displayed on-screen via the text or text2 commands.

Sometimes, a novice trancer's first experience with text trancing may be unsuccessful for a very simple reason:  as they begin to drift into trance, many people have a natural tendency to close their eyes.  Obviously this makes reading the hypnotist's words impossible.  Not everyone has this tendency, and it can usually be overcome with practice, but the on-text event allows for a rather simpler work-around.  By defining a trigger-rule for the on-text event, a soothing chime can be played when each message is received, signaling to the sub that they should open their eyes to read it.  If within the trance you refer to the process of the sub opening their eyes upon hearing the chime, and then after they've read the message closing them again and drifting deeper, this can work as a sort-of automatic Elman fractionation process, one that continues throughout the trance.

If used in this way, there may be times during trance when you won't want each text command to have an associated audio cue.  For example, you wouldn't want to continue with fractionation during the wake-up phase of a trance.  Therefore, as with the on-emote and on-chat events, there is a companion hooktext command that takes a Boolean parameter to specify whether the on-text event will fire or not.  hooktext yes will enable the on-text event; hooktext no will disable it.

Enhanced sound control

Related to the above, I've added variants of the play and playloop commands.  While the regular play and playloop commands use the volume level established by the volume command, the new play2 and playloop2 commands take, as an initial parameter, a volume setting for the sound.  This is particularly useful for sounds used as triggers or via the new on-text event, as it can ensure that these sounds will be played at a reasonably low volume, regardless of what the default volume is set to.

So whereas play fe3e1012-a6f7-a671-aa2d-972ce000beea would play a tinkling chime sound at whatever the current volume setting is, play2 3 fe3e1012-a6f7-a671-aa2d-972ce000beea would play the same sound at a predictable volume level of 3.

Emoting and speaking for the sub

The say, whisper, shout and emote commands have existed since the very first pre-release.  Previously, they always "spoke" using the current display name of the sub.  In other words, if a sub's username is "fred12345" and their display name is "Fred", then /99say "Hello there" would cause the following to be spoken in local:

Fred: Hello there

Most of the time this is fine, but sometimes you might want to customize the name used.  The new emotename command allows for this.  It takes a single parameter - the name to be used for subsequent say, whisper, shout and emote commands.  So if Fred's mesmerizer was given the command: emotename "Frederick the great", then the above /99say command would result in the following text:

Frederick the great: Hello there

Specifying an empty string for the emotename parameter will set the name used back to using whatever is the current display name.

One use for the emotename command is to program the Mesmerizer to be a renamer.  To do this, you will need to create a trigger-rule for the on-chat event, something like the following:

on-chat = internal say "$message"

You will also need to issue a hookchat yes command to redirect chat to the on-chat event.

This will cause anything that the sub tries to say in local be issued via the say command instead, which will use whatever name has been set via emotename.  So if Fred says "Suffering Sassafrass", their original speech will be suppressed, and the on-chat trigger will immediately invoke the say command with the same text, resulting in something like:

Frederick the great: Suffering Sassafrass.

Emotes can be similarly renamed with a trigger-rule for the on-emote event that invokes emote, and the hookemotes yes command.  on-emote will receive the emoted text (via the message variable, just like on-chat) with a leading "/me", and the emote command has been enhanced so that it's tolerant of a leading "/me", but doesn't require it.

Multiple .basics for Peanut outfit folders

This feature doesn't yet have a good descriptive name.  It refers to the extension to Peanut Folders that I mentioned at the time of the 1.3 release.   1.3 added the ability to have subfolders within your Peanut Outfits folder, but still required that the .basics and Nothing folders reside inside the Outfits folder itself.  In 1.4, these folders can optionally appear in lower levels in the directory hierarchy as well.

The primary reason for this is to support outfits that use different mesh bodies, since the .basics folder is expected to contain the body (and other body-parts), and the nothing folder should contain the corresponding "glimpse of nothing" object, along with the default hair for that body.  
When wearing an outfit, the .basics and Nothing folders from the closest ancestor of the outfit folder are used.

New Login and RLV Enforcer events

The Enforcer's existing Logon event is perhaps misleadingly named.  What it signals is that the sub's Mesmerizer has logged on to its Hub.  Usually this corresponds to the sub logging into SL; however, if the sub were to log out and then back in again within a few minutes, their Hub session wouldn't be logged out, and so the second log-in would not generate a Logon event in the Enforcer.  This can sometimes be a problem.  For instance, if the Logon event is used to set RLV restrictions, simply relogging would allow these restrictions to be avoided, since they wouldn't be applied to the second login.

To prevent this, Mesmerizer 1.4 adds a new Enforcer event: Login.  The existing Logon event is still generated, and is a suitable place to do things like sending a "message of the day" to the sub.  But actions that should be performed on each and every login to SL should use the new Login event instead.

There are also new Enforcer events corresponding to the on-rlv-enabled and on-rlv-disabled Mesmerizer events.  Within the Enforcer, these events are called RlvEnabled and RlvDisabled.  One of these events should fire on every login, but the RlvDisabled event may be delivered only after a delay, because the only way that the Mesmerizer can tell that RLV isn't enabled is by not receiving a response to an RLV status query within a certain timeout, so it has to wait for at least that timeout interval before signaling the RlvDisabled event.

Miscellaneous bugfixes

In addition to the new features listed above, 1.4 contains some bugfixes, including addressing some issues with the RLV relay around recapture with certain furniture engines.