Add-On Libraries
MapTool 1.11 introduces add-on libraries which are intended to be an easier to work with replacement for Lib:Tokens while also offering a lot more functionality. Lib:Tokens will still function the way that they currently do in MapTool 1.11 and future versions, but will not be getting a lot of the new features that add-on libraries will have, so it is recommended that framework developers transition to add-on libraries if supporting MapTool 1.11 and above.
I have a very sparse and contrived add-on library I have been using for testing available at test-maptool-add-on-lib
#
Managing the add-on libraries for your campaign.The dialog to manage the add-on libraries for your campaign can be reached using the add-on menu item in file menu.
This dialog can be used to add or remove add-on libraries, view the details of all add-ons in the campaign and view their license/read me files.
#
Format of add-on library filesAdd-On libraries can be shared in a .mtlib file. This file is a zip file with a specific structure and content. You can import these libraries with the File -> Import Add-On Library menu option.
library.json <-- Configuration information for the add-on librarymts_properties.json <-- Properties for macro script functions in libraryevents.json <-- Event definition for functions in the librarylibrary/ <-- Content of the librarylibrary/public <-- Content of the library acessable via `lib:// URI`library/mtscript <-- MTSCript fileslibrary/mtscript/public <-- MTSCript files that can be called via `[macro(): ]` outside of the library.
#
format of the configuration fileThe library.json configuration file is a json file with the following structure.
{ "name": "test-library", "version": "1.0.0", "website": "www.rptools.net", "gitUrl": "github.com/RPTools/test-library", "authors": [ "RPTools Team" ], "license": "GPL 3.0", "namespace": "net.rptools.maptool.test-library", "description": "My new test library for stuff", "shortDescription": "test library", "allowsUriAccess": true, "readMeFile": "readme.md", "licenseFile": "license.txt"}
The properties in this file are
- name - The name of the add-on
- version - The version of the add-on, this should use semantic versioning if you want to take advantage of future functionality
- website - A website for your add-on
- gitUrl - the url for the git source repository.
- authors - An array of the authors of the add-on.
- license - Name or short description of the license.
- namespace - The name of the add-on, you should use reverse hostname to ensure there are no conflicts.
- description - The description of the add-on.
- shortDescription - The short description of the add-on.
- allowsUriAccess - Whether to allow URI access to contents in the add-on.
- readMeFile - path to the readme file for the add-on.
- licenseFile - path to the license file for the add-on.
ReadMe and License files can be plain text, HTML, or Markdown (GitHub Flavored). These can be viewed from the add-on dialog.
note
GitHub provides a way to host your own static site at <username>.github.io, if you do not have your own host this can be used for both your add-on webstite and reversed for namespace. There is no requirement fo there to be an actual website running at the address used for the namespace.
#
format of the events configuration fileThis json file contains which files should be run for certain events.
{ "events": [ { "name": "onFirstInit", "mts": "onFirstInit" }, { "name": "onInit", "mts": "onInit"} ], "legacyEvents": [ { "name": "onInitiativeChangeRequest", "mts": "onInitiativeChangeRequest" }, { "name": "onInitiativeChange", "mts": "onInitiativeChange" }, { "name": "onTokenMove", "mts": "onTokenMove" }, { "name": "onMultipleTokensMove", "mts": "onMultipleTokensMove"} ]}
Add ons do not respond to the "onCampaignLoad" event, instead they have 2 new events.
- onFirstInit - This is called only once, when the add on is added. Add the same add on a second time overwriting the existing one it will not be called again unless the add-on is removed first.
- onInit - This is called every time the campaign is loaded (including after the inital onFirstInit event), and on the client when sent to a client. This functionality is very much similar to onCampaignLoad.
The other events must be in the "legacyEvents" section, as the name implies these events are now considered to be legacy events, new events will be added in the future to replace these (these will not be removed though).
Currently only macro scripts are supported, in the future JavaScript scripts will also be supported.
#
MTScript macros- library/public is only exposed via lib:// URI if
allowsUriAccess
is set (see configuration file) - MTScript macros must all end with the file extension .mts to be recognised.
- Any
Only MTScript files in
content/mtscript/public
can be called using[macro():]
from outside of the add-on
The path of the file becomes that macroname for [macro(): ]
the namespace of the add-on library
is used for the @
portion.
Add-On libraries support both public and private macro functions. Public macro functions must
reside in the mtscript/public and can be called from anywhere (chat, other add-ons, lib:tokens, macro buttons).
You can call them using the following syntax
[macro("mtscript1@lib:net.rptools.maptool.test-library")]
executes MTScript macro in the file
content/mtscript/public/mtscript1.mts
.
note
The "public/" is ommited from the macro name when calling it. You can also use subdirectories to organise your macros and would call them like [macro("subdir/script@lib:net.rptools.maptool.test-library")]
The @this
shorthand can also be used for calling a macro from within the same add-on, similar to how it works
for lib:Tokens. For example
[macro("mtscript2@this")]
Macro script files that are not in the "public/" directory can only be called from within the add-on itself or
by events.
Given a library with the namespace net.mylib.addon
with the following files.
mtsscript/func1.mtsmtsscript/public/func2.mts
[macro("func1@lib:net.mylib.addon")]
can be called from anywhere, but [macro("func2@lib:net.mylib.addon")]
can only be called from a macro that is on the net.mylib.addon
add-on.
note
Since the "public/" is not required, if you have to files with the same name excluding the "public/" part, for example mtscript/public/funct1.mts mtscript/funct1.mts
Then only the one in public/ will be able to be executed, you will not be able to call the other macro
The above works not just with `[macro():]
but the other places you would expect it to as well such as
defineFunction() for user defined functions and macro links.
#
mts_properties.json fileThe mts_properties.json file contains property information about macro scripts, it is not required and currrently only allows you to set properties used in macro links.
{ "properties": [ { "filename": "public/auto_exec.mts", "autoExecute": true, "description": "Auto executable macro link" }, { "filename": "public/myUDF.mts", "description": "My Test UDF in a drop in lib." } ]}
Where
- filename is the path of the file for the MacroScript function (excluding mtscript/).
- autoExecute determines if a macro link created for this macro will be auto executable or not.
- description is the description that will appear in the UDF listing, unlike Lib:Token this is just a plain string and not evaluated if it contains
[]
#
public/ directoryThe contents of this directory are exposed as a lib:// URI as long as the allowsUriAccess is set to true in the configuration file. The public directory part of the filename is discared, for example public/myhttml.html -> lib://net.myaddons.addon1/myhtml.html
You can add images to this directory and use src="lib://" in image tags in HTML. It will eventually work with audio (probably aleady does but I haven't tested it yet so not claining it will yet :) )
These assets will be included correctly in the campaign file when saving, so you do not need to add them to image tables or image tokens or any other tricks to make sure that they are included.
#
New MTScript Library functions- library.listAddOnLibraries() Lists the add on libraries
- library.getInfo(namespace) Gets information about a library (either add on or lib:token)
- library.listTokenLibraries(namespace) Lists the Lib:tokens in the campaign
- library.getContents(namespace) Lists the contents of a library (trusted)
#
Add-On Libraries and DataAs Add Ons do not live on a token there are no token properties for them. Instead there has been a new area created for storing data that is a lot more flexible -- and will allow data to be stored agisnt other entities like maps in the future. The existing lib property functions below have been modified to work with this new data store if called with an add-on as the target.
- setLibProperty
- getLibProperty
- getLibPropertyNames
- getMatchingLibProperties
There are some differences to be aware of when using lib property support for add-ons.
- The name is case sensitive, unles tokens where it is not case sensitive.
- The values stored do not need to be be converted to/from strings like they do with lib:tokens so in many cases for large json values this should result in a speed improvement.
- The default properties list for the campain are not present for Add-Ons as they are not tokens unlike Lib:Tokens
#
Macro functionsThere is a new function to access te static data within add-ons.
- data.getStaticData(namespace, path)
If called from within code in the add-on then it will be able to access any file, otherwise it will only be able to access the files in the public/ directory. This can be used to access text files, JSON, or even images within the add-on. images are returned in the asset:// format so they can be used in image tags.
#
Converting Lib:tokensThe token popup menu includes a way to export your existing library tokens to an add-on. This is useful for starting the conversion of an existing token library to an add-on but in all but the simplest lib:tokens you will want edit the extracted data.
#
Things you will want to change- You should probably change the namespace in the library.json file to something that is unlikely to conflict with other users. Its a good practice to use a reversed hostname + add on name for this.
- All macros (except event based ones) are created in mtscript/public with the pattern macro_{number>.mts, this is because macro names have many things that might make them invalid -- or worse dangerous -- filenames. There is a macro_script_map.txt file saved in the top level which contains the names of your macros and the filename that they were saved in.
- Not all macro buttons on lib:tokens always contain MT Script macros, they are used for CSS etc as well so you will probably want to rename and move them to the library/public/ directory.
- The onCampaignLoad macro will be saved as onInit
- All properties are saved in the library/properties directory. These are saved with the names prop_{number}.txt and a mapping file prop_file_map.txt is created to map these. (this may change slightly when data access is introduced, more the location)
note
After doing the above you should REALLY take the opportunity to source control your Add-On...
#
Probably added/addressed in 1.12- There is only minimal checking of data when importing add-ons so error reporting is not great.
- Expanding of JavaScript API which will make this much more useful will be part of another change.
- Start of support for Add-Ons to to be able to replace "standard" functionality, first things wil be
- Add-On support for stat sheets (on mouseover on tokens)
- Add-On support for map selection UI
- Way to create user defined functions through property file (so you dont have to call defineFunction in onInit)
- Providing an equivalent to lib:token buttons
#
Probably added/addressed in 1.13- Ability to check URL for later version and update from that (most likely github to start with)
- Link Maps to required Add-Ons when exporting/importing
- Dcoumentation / Procuedures for creating a GitHub release for your Add-On.
- The data store could be slightly smatter about large text blocks that remain static and attempt to cache them
#
Other miscellaneous stuff / insanityOne thing to realise is this change allows serveral things that are not immediately obvious. For example, experiment with frame5/dialog5 and lib:// URI support and ability to store images, you would be surprised how close it is to devloping a standard web site. Ass a proof of concept I have even been able to get a small React App written in TypeScript running realtively easily by using webpack to bundle it and copy it into the public directory. (Note the SVG for the beginner React app doesn't work correctly but replacing it with a png works).