Yikes!
Since I have been threatened with a dungeon I better make some sort of reply in an attempt to avert it.
I apologise in advance for the size of this post but it covers a lot of ground.
I really haven't put much thought -- nearly none at all -- into what I think
JavaScript macros might look like in 1.4 so I will just try to cover random
parts of posts in this thread with a "off the top of my head this is what I
think". As I am more or less coming up with what I think needs to be done as
I type this I am sure I will miss something, or think of a better way to do
something just after I have posted this. Hopefully this this will in the end
make some sort of sense and not become a rambling mess, it should at least
serve to set some expectations on just how much thought and effort needs to
go into such an endeavor.
For starters I noticed a couple of posts about how it looks pretty easy just
take "this snippet of code" and it will work. This is far from the case, that
is about 1% of the work that needs to be done. Also that is the Java 6 way of
doing things, it may be preferable to do it the Java 5 ways as its not that
much more code, and you don't want a handful of code to force you to have to
adopt Java 6, I know people will be screaming but 1.4 will be Java 6 only!!!
That could quite possibly -- even probably -- be true, but then again the
biggest driver for this would be the vision code, you never know since OpenGL
offers even more that could be the way to go instead, so no longer would Java 6
be absolutely needed, but that possibility is a completely different discussion.
There are other reasons that doing it the Java way is beneficial as it requires
no set up by the user at all, where as the Java 6 method will in the case of
some non Oracle JVMs, while the scripting interface is required for Java 6 the
JavaScript implementation is not.
I know that the Java 5 method of including JavaScript is specific to the
JavaScript engine and does not apply to other scripting engines, unlike the
Java 6 method which is similar code for many other languages. I do not see
this as a problem, as its very unlikely that there will be more than one
"officially supported" scripting language for macros -- thats right no ruby,
no python, only JavaScript. This is not because anyone has something against
these other languages or I know something Trevor has said that no one else does,
but simply because the amount of work. Regardless what most people think,
adding another language is not simple, JavaScript and Java don't always see
eye to eye and some coercion is required. Add another language into the mix,
and suddenly you have another set of coercion required, whats worse a bridge
between the 2 languages, after all if you pick up a library for say DnD of
macros, do you want to find that it was written in Lisp so suddenly your
Python macros suddenly need to know how to manipulate Lisp data structures?
My guess is no you don't, the more languages in the mix the worse it gets,
the more support the RPTools team has to do for that other languages and the
testing for 2 languages is probably at least 3 times that of 1, and it gets
much worse from there. Hence no matter what language you like the most its
best to have one standard macro language. If 1.4 supports plugins then I am
sure someone will add X language to the macro language, but then thats for
them to sort out, not necessarily something that the RPTools Team would have
tool maintain.
Ugh thats a lot of typing so far and I haven't even addressed a small fraction
of the comments.
So first off to ally any fears, there will be a lot of functions to do many
of the things that you can currently do. At a guess though I would say that
you will find some largish differences. This is because apart from no overall
design for the current macro language it is for a large part heavily influenced
by the limitations in place. For example since the current macro language has
no null value or way of capturing an error there are several functions that
can be used to check for potential error conditions or nulls, these most likely
would have no analog as there is no need for them. Another property of the
current macro language is it really "feels" like an interpreted language, there
are several functions that do a lot of processing inside the function which
are quite possible to do in the macro language, but would be a lot less
efficient -- look at the getToken() function for example! A well designed API
for a non interpreted language -- such as JavaScript in our case -- would tend
to have more functions that try to do less things, I am a big fan of the Unix
philosophy of "write programs that do one thing and do it well", and this
equally applies to methods/functions, especially in an API. So the API should
contain smaller functions that work together to achieve what the current
API does with large complex functions. For the non programmers out there this
may sound scary because you are thinking "oh no I have to remember more
function names to do the same things?", well there will be more functions but
they should be consistently named -- so we wont be using Visual Basic as a
model -- which will make it clear which one you need to use without having
to remember the rather esoteric syntax required for the parameters of some of
the current functions.
The next thing to consider is security and permissions. There are two sides
of this to look at, and when thinking about these questions do not think
about them as the group that knows each other well and always plays together,
think about the GM/players in one off pickup games because they have the
highest security requirements.
1) As GM how do I limit what players can do?
2) As a player how do I limit what the GM can do on my machine?
To address the first one there are 2 options,
a) The trusted frame work is pushed down a level into the JavaScript macros
where each trusted JavaScript method checks to see if the macro is
running as trusted and fails if required.
b) JavaScript macros are GM only, players can not run their own JavaScript,
instead they run simple expressions -- think of anything you could
currently run in a { }. JavaScript could then be used to add callable
functions that players can run if they tweak their own macros.
I favor option b over option a because its cleaner, easier to implement, and
consistent, these are all good things when talking about security. JavaScript
objects would be able to export certain methods -- similar to how user
defined functions work now -- a exported function could be exported as
trusted only so that any call to it from an untrusted macro would fail.
The second you may be thinking "wait players limiting what the GM can do",
I am not talking about what the GM can do to players tokens, or maps or what
ever. What I am talking about is other things, like the GM shouldn't just be
able to open files on the users PCs, download arbitrary files outside of
MapTool and all kinds of other things. So MapTool has to defines some pretty
strict limits to what JavaScript macros can do, and that means limiting
what Java classes it can access. My preference would be to err on the side
of being more strict than less, so limit it to
- java.lang excluding things like System, Runtime, SecurityManager, Process etc.
- java.util
- java.util.regex
- java.math
- java.text
From the standard Java packages
And
net.rptools.maptool.macro.api (or what ever package the API lives in)
From MapTool
Everything -- out side of those core Java ones -- needs an API hook sitting
inside net.rptools.macro.api. Now this is obviously going to be slightly
different if OSGI is being used, but the concept will generally be the same,
macros will only be able to access those core Java classes and other plugins
would export a small API that could be access via JavaScript macros.
I am aware that I left out all of GUI creation classes, really any GUI frames/dialogs
should be created though the API similar to what happens now. Anything that requires
really fine grained control of how GUIs etc are created should probably be a plugin
and not a macro. So since everything that a JavaScript macro does to interact with
MapTool anyway requires an API to be written it starts to give you an idea of how
big a job it will be.
The API will more than likely be split into two parts, part of it will be in Java,
this will be the part that exposes certain parts of MapTool, and then there will
be the other part which is a JavaScript wrapper around certain areas that will
do the coercion between Java and JavaScript objects, rather than people writing
macros having do deal with this as it can get messy. Its also possible that standard
higher level routines are build using the lower level API to make certain things
easier while still retaining the possibility for people to use the lower level ones.
Its not a good idea to just expose the current API used by MTScript for the JavaScript
API, as the MTScript API is significantly hobbled by the fact that there is no built in
support for data structures -- and getting your data structures right is the most
important part of programing
. So the JavaScript macros have a great scope for
potential improvement.
Another thing that has been discussed a lot is the ability for macros to be triggered
on events. To support this a we need to come up with a framework for macros to resgister
them selves when they want to be informed about events and for MapTool to send them those
events. This brings up another issue, concurrency and synchronisation. JavaScript macros
should never need to worry about either, which of course means that the implementation
would have to take care of any potential issues so that macro writers do not have to.
At the moment all macros run on the GUI thread, this is more of an accident of the fact
that macros were originally only intended to very limited things. Now that macros
do a lot and can run for quite a while the GUI thread is not really the best place for
them to run, while a macro runs it stops the GUI from doing
any processing. Using
JavaScript macros this is going to get worse as people write more and more complex macros.
Also you can see some of the side effects of this decision for the need to use deferred
macros for some changes, or how some changes just seem to overwrite others. Jfrazierjr
ran into another of the disadvantages of this design when doing some targeting stuff,
he was unable to pop up a message saying something like "choose your targets" and then
let the user choose targets and continue executing after this, this was because no
GUI processing like responding to mouse events while a macro is running.
Some events -- timers for example -- wont even happen on the GUI thread so the possibility
that a macro might be fired off from somewhere other than the GUI would exist. So how
can you handle this? The GUI thread is actually a pretty good thing to mimic in this case.
Have a single dispatch thread that runs all macros, be the fired off by user typing,
pushing a button, or any event. Any macros that are run at added to a queue, and the
macro thread then processes this queue and runs any macros on it. When I say run macros
I also mean parse macros and anything between [ ] or { } not just JavaScript macros.
This of course would require careful planing on the MapTool side to deal with the
concurrency issues, but this is far preferable to making macro writers perform this task.
Some times you wont be able to get away with doing something on the GUI thread, but
that is ok, the API can handle that, pausing the macro while it waits for the GUI
thread to do the work -- if required -- and most importantly keeping the amount of work
done at a time on the GUI thread small.
So above there is a basic overview of the internal frame work required, but it still
really does describe what JavaScript macros will look like to the people using them.
Thats what my next post will be for