Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×
Advertisement Create a free account

[Meta Script] Plugger - give scripts and plugins zero-order priority

1618937281

Edited 1620074557
timmaugh
Pro
API Scripter
Plugger FILE LOCATION:   My Personal Repo (until one-click submission) META SCRIPT:  This script is a meta-script, and part of the ZeroFrame Meta Toolbox . It can be used on its own, or in conjunction with ZeroFrame and the other meta-toolbox scripts, to modify  other  scripts' API commands. ABSTRACT:  Plugger gives you the ability to run small scriptlets (or scripts) and grant them zero-order capability (letting them shin-kick their way to the front of the line, no matter when they are installed). With ZeroFrame installed, you can schedule/prioritize the plugin evaluation in the loop, giving you even finer control. The plugins can offer anything from custom dice extraction from an inline roll to distance detection -- anything you might want a script to do in game. Introduction You can wrap other scripts you want to run in EVAL tags, and Plugger will evaluate the contents to run that command line. These could be standalone scripts or scripts registered to Plugger as a plug-in. If the scripts is registered as a plugin, you can retrieve whatever data you need to from the game or campaign and manipulate the command line in real time. Effectively, this gives you the ability to run code with the same shin-kicking, queue jumping trick that powers the meta-toolbox. Although Plugger includes a library with a few built-in functions, the real strength of this sort of plug-ability is that 3rd-party scripters (or even you) can write scriptlets to allow this real-time return of game data to your command line. Need a PageID? Write a script and plug it in. Need the closest X number of tokens to a given token? Or all of the tokens within some given range? Write a script! (Or buy your local scripter a coffee and have them write it for you!) Syntax Highlights {& eval}scriptlet(--arg1|val --arg2|val){& /eval} Using the EVAL Tags EVAL tags are represented by {& eval} ... {& /eval}. The script command you want to execute would go between these tags. A full EVAL block would be structured using one of these formats: {& eval} scriptname(arguments for script){& /eval} {& eval} scriptname arguments for script{& /eval} {& eval} !scriptname arguments for script{& /eval} The scriptname can represent one of the library of plugin scriptlets, or it could be another script in the game. Everything between the parentheses is passed to that script as arguments. Use the first form when you want to access a plug-in designed to return information to the line. Plug-ins are built very similarly to normal scripts. In fact, a full-fledged script can be co-opted to return a value, if the developer wishes (more on building plugins later). The last two forms are functionally the same as each other (the third line simply includes the leading exclamation point), but they differ from the first form in that they do not return anything to the command line. These forms are intended solely for launching other scripts (not plugins) that have nothing to do with returning a value to the command line. The EVAL tag will launch the other script and consume itself, leaving a zero-length footprint behind. EXAMPLE: All tokens in range Your character has an attack spell that affects anyone within some given range. Your scripter friend writes a script that returns the IDs of all tokens within that range, telling you that to use it, you would use a command syntax of: !withinrange range source delimiter So a typical call might be: !withinrange 3 @{selected|token_id} , …returning a comma-separated list of tokens within 3 units of the given source token. Your scripter friend also tells you that they have built it as a plug-in for Plugger, so now you can use the same scriptlet in-line with another command to substitute in that comma-separated list: !some-other-script --targets|{& eval}withinrange(3 @{selected|token_id} ,){& /eval} --damage|tons By the time Plugger gets the message, the token id of the selected token will have been evaluated, and by the time the  some-other-script  API picks up the message, the EVAL tag will have been evaluated and will now the comma-separated list of tokens within the given 3 unit range. Nest-able EVAL tags are nest-able, and are processed from inside-out, allowing you to use one EVAL tag to return a piece of data that would be used as an argument in an enclosing EVAL tag: {& eval}withinrange(     {& eval}         rangecalc(Bob the Slayer, Supernova)     {& /eval}     -M1234567890abcdef     , {& /eval} In that example, the the imagined (and custom built)  rangecalc  scriptlet figures out the range of the Supernova spell, and returns it to the outer EVAL block’s  withinrange  scriptlet. Available Built-In Scriptlet Functions At the time of writing this, there are two built-in functions, with more coming: getDiceByVal()  will retrieve dice from an inline roll that match a given set of value parameters (i.e., 2|5-6|>=10). Output options are a  count  of the dice, a  total  of the dice, or a  list  separated by a delimiter of your choice. More on the required syntax for this plugin in a later post. getDiceByPos()  will retrieve dice from an inline roll based on the position of the dice based on a set of position parameters (i.e., 2|5-6|>=10). Output options are a  count  of the dice, a  total  of the dice, or a  list  separated by a delimiter of your choice. More info on the required syntax in a later post. Installing 3rd Party Scriptlets If you or your local scripter has written a scriptlet to plug into Plugger (which is easy enough to do), you only need to install it as you would any other script. Provided the script author implemented the syntax to register that scriptlet with Plugger, it will be available to you as soon as your sandbox restarts.
1618937291
timmaugh
Pro
API Scripter
Updates and Releases
1618937301
timmaugh
Pro
API Scripter
Advanced Usage and Tricks
1618937343

Edited 1620101209
timmaugh
Pro
API Scripter
Included Plugger Scriptlets The following plugins ship are included in the latest version of the Plugger script. getDiceByVal Retrieves a subset of dice from an inline roll based on testing them against a series of pipe-separated value ranges. Outputs either a  count  of the number of dice (the default), or a  total  of the dice, or a delimited  list  of the dice values (delimiter default is a comma). BASIC SYNTAX getDiceByVal( roll rules dicecategory output) roll can be an inline roll equation (i.e., [[1d10]] ) or a roll marker (i.e., $[[0]]) rules are a pipe-separated list of comparisons for the dice values (see example below) dicecategory is one of the sorts of dice returned by the inline roll; default = included ; can be one of the following: all - all dice included - only the dice that are included (i.e., not dropped, rerolled, etc.) success - critical success dice crit - critical success dice fail - critical failure dice fumble - critical failure dice dropped - the dice that are not included (i.e., those that were rerolled or not kept) output - whether to output a count , total , or delimited list of the retrieved dice; default = total ; declaring this argument requires the dicecategory be explicitly included, as well EXAMPLE getDiceByVal( $[[0]] <=2|6-7|>10 included total) The above would retrieve the included dice from the first inline roll ($0) that were either less-than-or-equal-to 2, between 6 and 7 (inclusive), or greater than 10. It would output the total of those dice. If you choose a list output, the default delimiter is a comma. You can alter this by using a pipe character followed by the delimiter you wish to include. If your delimiter includes a space, you must enclose it in either single-quotation marks, double-quotation marks, or tick characters. getDiceByVal( $[[1]] 1|3|5|7|9 dropped list) The above would output a comma-separated list of odd value die results from the dropped dice of the second inline roll ($1). The following table shows how the delimiter changes based on altering the ‘list’ argument: ARG | EXAMPLE OUTPUT ------------|--------------------- list | 3,7,5,9 list|", " | 3, 7, 5, 9 list|+ | 3+7+5+9 list|` + ` | 3 + 7 + 5 + 9 list| | 3759 getDiceByPos Retrieves a subset of dice from an inline roll based on testing them against a series of pipe-separated position ranges. Outputs either the  total  of the number of dice (the default), or a  count  of the dice (seems pointless, but it’s available), or a delimited  list  of the dice values (delimiter default is a comma). Dice position is 0-based, so the first die is in position 0, the second in position 1, etc. BASIC SYNTAX getDiceByPos( roll rules dicecategory output) roll  can be an inline roll equation (i.e., [[1d10]] ) or a roll marker (i.e., $[[0]]) rules  are a pipe-separated list of comparisons for the dice position (see example below) dicecategory  is one of the sorts of dice returned by the inline roll; default = included ; can be one of the following: all  - all dice included  - only the dice that are included (i.e., not dropped, rerolled, etc.) success  - critical success dice crit  - critical success dice fail  - critical failure dice fumble  - critical failure dice dropped  - the dice that are not included (i.e., those that were rerolled or not kept) output  - whether to output a  count ,  total , or delimited  list  of the retrieved dice; default = total ; declaring this argument requires the dicecategory be explicitly included, as well EXAMPLE getDiceByPos( $[[0]] <=2|6) The above would retrieve included dice from the first inline roll ($0) that were in positions 0, 1, 2, or 6. It would output the total of those dice. The same guidelines apply for the list delimiter as for the  getDiceByVal  plugin, above.
1618937375
timmaugh
Pro
API Scripter
Writing Your Own Plugin for Plugger The EVAL tag allows for anyone with a little coding experience to provide an infinite number of extensible features. The EVAL tag will run the script as designated, looking first in its bank of registered plugins. If the script isn’t found there, Plugger will send the script call to the chat to have the script fire that way before continuing with its own processing of the original message. Remember, only plugins registered to Plugger are handled in sequence, with their result substituted into the command line. If nothing is returned, an empty string will be substituted in place of the EVAL block. Only after the plugin code finishes does Plugger take over again. Calls to outside scripts, on the other hand, are not guaranteed to finish before Plugger moves on. So, how do you write a scriptlet and register it to APILogic? Accept a Message A plugin for Plugger should accept a message object, just as any function that answers a chat event (i.e., handleInput). In fact, your script can also answer a chat event if you like (more on that under Who Called?). The message object will be identical to a message that would be received from a user – it will have properties of  who ,  playerid ,  content , etc. If there were any inline rolls, it will have an  inlinerolls  array. This will be a copy of the message data that is in Plugger, with the content replaced to be the reconstructed command line that the user would have sent had they invoked your script directly from chat. In other words, the  withinrange  script (mentioned above) might require a command line like the following, if it were to be invoked from the chat interface: !withinrange 3 -M1234567890abcdef , When a user places that in an EVAL tag block, they would write: {& eval}withinrange(3 -M1234567890abcdef ,){& /eval} If Plugger detects withinrange as a registered plugin in that game, it will hand off a message with the former command line. Accepting a message might look like this: const withinrange = (m) => { log(m.who); // logs who sent the message }; Parse the  content  String As you would with any script, parse the command line to extract the data you require to perform your calculations. If you intend to allow the scriptlet to be called from the command line separate from Plugger, make sure that you verify ownership of the message, as well. This might look like: const withinrange = (m) => {     // verify ownership     if (m.type !== 'api' || !/^!withinrange\s/.test(m.content)) return; // parse arguments let [range, sourcetoken, delim] = m.content.split(/\s+/).slice(1); log(range); log(sourcetoken); log(delim); }; Perform Calculations and Return Code as you normally would to calculate and arrive at the data you are looking for. When you are done, if you want something to be substituted into the original command line (where Plugger called your plugin), return a  string ,  number ,  bigint , or  boolean . Anything else (including no return or an  undefined  return) will be replaced with an empty string. Who Called? A message that comes from Plugger to a plugin scriptlet will have one property that a chat-interface-generated or API-generated call will not have:  eval . If you want your script to answer both a straight invocation as well as an Plugger implementation, you can differentiate your return based on if you detect this property. For instance, if a user invokes  withinrange  from the chat interface, maybe we want to display a small panel of information regarding the tokens that are in the specified range – including their image, name, etc. However, if the call comes from Plugger, you only want to return the token IDs in a delimited string. In that case, once you have arrived at the data, you could test for existence of the  eval  property, and return accordingly: let tokens = getTheTokens(); if (msg.eval) return tokens.join(delim); // if the code continues, you're dealing with a call from the chat interface, not Plugger // so proceed to build the panel output... Register to Plugger The step that turns your script into a Plugger plugin is when your script implements the  Plugger.RegisterRule()  function in an  on('ready'...)  block. Here is an example: on('ready', () => {     try {         Plugger.RegisterRule(withinrange);     } catch (error) {         log(error);     } }); The  RegisterRule()  function can take any number of functions as parameters, so tack on as many plugins as you’ve written: Plugger.RegisterRule(withinrange, getclosest, getpageforchar); Install your script as you normally would, restart your sandbox, and you can immediately start using your script as a meta script.
1620096408
timmaugh
Pro
API Scripter
Version 1.0.1 Released May 3, 2021 ADDED : Native handling of line-breaks Update Notes Since this meta-script can be used with or without the ZeroFrame Meta Toolbox, it needs to be able to handle line-breaks natively. This update fixes a problem where this script would not have processed correctly were it used in a multi-line macro without ZeroFrame installed, too.
1620419686

Edited 1620743359
timmaugh
Pro
API Scripter
Version 1.0.3 Released May 7, 2021 FIXED/ADDED : Tag buffering with parentheses of brace-enclosed tags, i.e., ({& eval }) ADDED: EVAL tags now take escape/deferral character strings between parentheses Update Notes Buffering With Parentheses - ({&eval}) This fix gives an option if the command line might result in a series of brace characters that would be inadvertently eaten by Roll20 parsers. In that case, you can enclose the syntax token in parentheses to break up that series of characters. This only matters for brace-enclosed tags: {& eval} or ({& eval}) Using Escape/Deferral Strings Normally, syntax structures that are a part of the meta-toolbox are detected and run in whatever loop order you have established (if you have ZeroFrame installed). If you instead want to defer the resolution of a syntax structure until the script inside the EVAL tag runs, you can now include a character string inside of parentheses as a part of the EVAL tag: {& eval(^)}getInfoFromClosest({^& if @{selected|bar1}>0}... There, the ^ character was used as the escape string. The presence of the character in the IF block breaks up the syntax so that the IF isn't caught and parsed within the main message. Plugger will catch the EVAL tag and remove the ^ from the supplied command line before sending that message out to the intended script. By removing the escape character string from the command line, the syntax structure is now detectable when the new message comes through: {^& if becomes {& if
1620743399
timmaugh
Pro
API Scripter
Plugger has been submitted to the one-click and should be in the next merge.