Macro Best Practices

Discuss macro implementations, ask for macro help (to share your creations, see User Creations, probably either Campaign Frameworks or Drop-in Resources).

Moderators: dorpond, trevor, Azhrei, giliath, jay, Mr.Ice

User avatar
Rumble
Deity
Posts: 6235
Joined: Tue Jul 01, 2008 7:48 pm

Re: Macro Best Practices

Post by Rumble »

toyrobots wrote:
Rumble wrote:
zEal wrote:If it's something you're going to do more than once with little variation, create a user defined function.

This is something I rarely do, and should probably do more, but I never saw a substantial need.
Point me to an explanation of User-Defined Functions in Macros?
There's a PDF that Craig put together that has a decent illustration of how they work, but the basic approach is this: normally, you'd call another macro, and you can pass it an argument. The argument can be a string, a number, a JSON object, whatever - it gets sent over to the called macro. You do that with:

Code: Select all

[macro("MacroName@Lib:Token"):arguments]
A user-defined function works like this - you define the function like so:

Code: Select all

[defineFunction("funcName", "MacroName@Lib:Token")]
Then, when you want to call that macro and pass it an argument, you call it like this:

Code: Select all

[funcName(arguments)]
That's the basics. But, there are other new functions that exist to help with that, specifically argCount() and arg(). argCount() lets you check to see if any arguments get passed, which you can use to make sure the function doesn't try to run without important info (assuming it needs any). arg() lets you extract the arguments that are passed, starting at 0 and going until you run out of arguments. So if you called that function like this:

Code: Select all

[funcName(argument1, argument2, argument3)]
In the called macro, you can obtain those arguments with arg(0), arg(1), and arg(2), respectively. I believe you must use argCount() before you can use arg(), but I don't know for sure.

The two benefits:

1. It's easier to write than [macro("macroname@lib:token"):yadda]

2. You can pass multiple arguments easily, just by specifying them. The magic that handles that transfer is inside maptool somewhere; you don't have to worry about it.

The reason I haven't used it is that I've written my macros to pass single arguments (usually, JSON objects) so the only benefit is benefit 1, which wasn't sufficient to get me to rewrite everything. When I do a rewrite, I'll probably plan it out using user-defined functions carefully.

User avatar
zEal
Dragon
Posts: 944
Joined: Sun Mar 22, 2009 2:25 am

Re: Macro Best Practices

Post by zEal »

One of the really cool functions, related to user-defined functions, that slipped by with barely a mention is oldFunction(). The basic usage is pretty obviously, but I'm still trying to wrap my brain around the greatness of the potential 'chaining' of functions.
Last edited by zEal on Thu Apr 16, 2009 4:06 pm, edited 2 times in total.

User avatar
Rumble
Deity
Posts: 6235
Joined: Tue Jul 01, 2008 7:48 pm

Re: Macro Best Practices

Post by Rumble »

zEal wrote:One of the really cool functions, related to user-defined functions, that slipped by with barely a mention is oldFunction(). The basic usage is pretty obviously, but I'm still trying to wrap my brain around the greatness of the potential 'chaining' of functions.

That explanation needs a rewrite, though. I'm on it.

User avatar
zEal
Dragon
Posts: 944
Joined: Sun Mar 22, 2009 2:25 am

Re: Macro Best Practices

Post by zEal »

Rumble wrote:
zEal wrote:One of the really cool functions, related to user-defined functions, that slipped by with barely a mention is oldFunction(). The basic usage is pretty obviously, but I'm still trying to wrap my brain around the greatness of the potential 'chaining' of functions.

That explanation needs a rewrite, though. I'm on it.
Definitely, I think I was running into wiki-burnout when I wrote that. >.>
Rumble wrote:In the called macro, you can obtain those arguments with arg(0), arg(1), and arg(2), respectively. I believe you must use argCount() before you can use arg(), but I don't know for sure.

The two benefits:

1. It's easier to write than [macro("macroname@lib:token"):yadda]

2. You can pass multiple arguments easily, just by specifying them. The magic that handles that transfer is inside maptool somewhere; you don't have to worry about it.

The reason I haven't used it is that I've written my macros to pass single arguments (usually, JSON objects) so the only benefit is benefit 1, which wasn't sufficient to get me to rewrite everything. When I do a rewrite, I'll probably plan it out using user-defined functions carefully.
Nah, using argCount() isn't necessary for using arg(), it is handy though. Another added benefit of user defined functions is that you can create a group of library tokens that depend on one another without having to worry about having them named precisely. If I use [macro():] to call a macro on another library token, I need to know that token's name. If a library token defines its own user defined function, you can call that function from any macro without needing to know where it is.

User avatar
Rumble
Deity
Posts: 6235
Joined: Tue Jul 01, 2008 7:48 pm

Re: Macro Best Practices

Post by Rumble »

zEal wrote:Definitely, I think I was running into wiki-burnout when I wrote that. >.>

Nah, using argCount() isn't necessary for using arg(), it is handy though. Another added benefit of user defined functions is that you can create a group of library tokens that depend on one another without having to worry about having them named precisely. If I use [macro():] to call a macro on another library token, I need to know that token's name. If a library token defines its own user defined function, you can call that function from any macro without needing to know where it is.
Ah, I didn't know that other benefit - that makes it even more attractive. And I didn't mean to bust on you about the description! I understood it, and it is extremely precise, but it's a little confusing. Take a look at the rewrite I put up, see what you think.

User avatar
toyrobots
Dragon
Posts: 278
Joined: Sat Apr 12, 2008 4:17 pm

Re: Macro Best Practices

Post by toyrobots »

By way of keeping things on topic, since I find this thread has been invaluable already:
Be consistent with indentation

Use modular, small segments of code
Separate macros for input, execution and display

Use HTML Comments

Put as much code as possible on Library tokens

Descriptive variable names
Longer is better if it makes it more clear
Token Properties first letter of each word capitalize each word
Local variables lowercase first word first letter, all words first letter capital
Lib Properties all caps, separated by underscore
When asking for help, put in all your variables

If there's something you're going to do more than once with little variation, create a user defined function.

User Defined Functions should always be named starting with a strong verb (because they do something)

Don't rely on the default roll types

Use encode() and decode() for user entered strings

JSON is preferable to List or StrProps.

Use getProperty() and setProperty() when working with properties

Don't use 'magic numbers', if need be create a series of variables at the beginning of your macro that will function as 'constants'.

Don't rely on line-wrapping; try to find ways to break your lines into manageable lengths on your own.

Avoid initiative panel controls, write functions for what you need to do with initiative.
Add, subtract, modify?

User avatar
zEal
Dragon
Posts: 944
Joined: Sun Mar 22, 2009 2:25 am

Re: Macro Best Practices

Post by zEal »

I don't agree with all of them, but as I tried to infer in my first response, that's how it works :P
toyrobots wrote:When asking for help, put in all your variables
I know what jfrazierjr is getting at with this one, but I'd like to expound upon it a bit. I realise it's not always possible, but when it is, if you're able to basically separate the portion of code that you're having difficulties with into a macro you think should be able to run all on its own, you'll probably find it easier to get help. What he meant was that if we have to write a bunch of 'support code' (assigning dummy variables and what not) just to get your snippet to be remotely valid in its own context, a lot of us will be less likely to offer advice.

EDIT: Oh! and don't act like you're coding for the government, tell us what you're actually trying to do in general, sometimes it's easier to tell you a better way to do it than debug what you're attempting.

User avatar
toyrobots
Dragon
Posts: 278
Joined: Sat Apr 12, 2008 4:17 pm

Re: Macro Best Practices

Post by toyrobots »

zEal wrote:I don't agree with all of them, but as I tried to infer in my first response, that's how it works :P
Admittedly, some of the above are more like "common practices" than "best practices". Collecting them and commenting on them is the only way to establish which are "best", so even saying what you disagree with and why is highly constructive.

User avatar
walkerp
Cave Troll
Posts: 54
Joined: Thu Mar 26, 2009 9:32 pm
Location: Montréal, Québec, Canada
Contact:

Re: Macro Best Practices

Post by walkerp »

That's a very helpful reference for me, toyrobots. Thank you for doing this.

About the getProperties() and setProperties(), so even a simply macro such as one that reduces a token's hit points, I should be using those two functions instead of just going directly into the token and changing the property? Why is that again?

User avatar
Rumble
Deity
Posts: 6235
Joined: Tue Jul 01, 2008 7:48 pm

Re: Macro Best Practices

Post by Rumble »

walkerp wrote:That's a very helpful reference for me, toyrobots. Thank you for doing this.

About the getProperties() and setProperties(), so even a simply macro such as one that reduces a token's hit points, I should be using those two functions instead of just going directly into the token and changing the property? Why is that again?
I actually don't do that with getProperty() and setProperty() - if it's a token property, I usually just address it directly. getProperty() / setProperty() can ensure that you're accessing a property (or create one if it doesn't exist!), but I don't use them to manipulate properties frequently.

In fact, the only time I use them is to create and change "invisible" properties - things that are not configured in the campaign properties. I don't know why zEal (was it zEal?) recommends only using those, unless it is to remove ambiguity about what variable means what (since case sensitivity is not enforced in variable names).

User avatar
zEal
Dragon
Posts: 944
Joined: Sun Mar 22, 2009 2:25 am

Re: Macro Best Practices

Post by zEal »

Rumble wrote:In fact, the only time I use them is to create and change "invisible" properties - things that are not configured in the campaign properties. I don't know why zEal (was it zEal?) recommends only using those, unless it is to remove ambiguity about what variable means what (since case sensitivity is not enforced in variable names).
I don't necessarily recommend it, it's just the way I do it, and for the very reason you stated. Since I don't use set() in its standard capacity, I've redefined it along with creating a get() that mimics the functionality of getProperty() and setProperty().

That also answers toyrobot's query about which ones I don't agree with, as the variable naming is all. That isn't to say the method RPTroll and Veggiesama use is wrong in any way, it's just personal preference... and I actually agree with them in regards to local variables; camel case is the way to go(unless it's a variable that I want to consider a constant, even thought we really don't have constants, then it's all caps).

SpaceShot
Kobold
Posts: 20
Joined: Mon Jun 16, 2008 8:53 am

Re: Macro Best Practices

Post by SpaceShot »

Perhaps someone with experience in the code can help here.

My group (five players and a DM) recently had to rewrite large portions of a common framework because it was too laggy for us.

I did use eclipse to step into the macro execution code, and best I could tell, the problem stemmed from just calling too many macros one inside the other. It seemed that a lot of data was pushed onto the stack again. I don't claim to have thoroughly followed it through.

The point is, I rewrote a large number of macros to both reduce functionality we weren't using but also to consolidate code from separate macros into one call. I also greatly reduced the size of the jsons passed around.

The result? We are now running lag free.

Now, I point this out here because a best practice was listed that code for input, display, and so on should be separated into smaller macros. I have found that to be absolutely the kiss of death and it makes the whole application crash a few of our player's machines.

(Note: these players have, what they self-describe as, very very very old PCs. No idea if it's related.)

So I have actually, as a best practice, not made lots of small component like macros as I would in my regular programming. The reason is simply that those macros (with their general rather than specific behavior) were larger and passed more data from call to call.

I will not quibble with how MapTool really works, because I don't pretend to know the guts. I have access to the source code but haven't possibly come close to being an amateur at it's design. This is just my experience. Maybe it is something we are doing wrong. It's just the reality of our group.

User avatar
Rumble
Deity
Posts: 6235
Joined: Tue Jul 01, 2008 7:48 pm

Re: Macro Best Practices

Post by Rumble »

SpaceShot wrote:Perhaps someone with experience in the code can help here.

My group (five players and a DM) recently had to rewrite large portions of a common framework because it was too laggy for us.

I did use eclipse to step into the macro execution code, and best I could tell, the problem stemmed from just calling too many macros one inside the other. It seemed that a lot of data was pushed onto the stack again. I don't claim to have thoroughly followed it through.

The point is, I rewrote a large number of macros to both reduce functionality we weren't using but also to consolidate code from separate macros into one call. I also greatly reduced the size of the jsons passed around.

The result? We are now running lag free.

Now, I point this out here because a best practice was listed that code for input, display, and so on should be separated into smaller macros. I have found that to be absolutely the kiss of death and it makes the whole application crash a few of our player's machines.

(Note: these players have, what they self-describe as, very very very old PCs. No idea if it's related.)

So I have actually, as a best practice, not made lots of small component like macros as I would in my regular programming. The reason is simply that those macros (with their general rather than specific behavior) were larger and passed more data from call to call.

I will not quibble with how MapTool really works, because I don't pretend to know the guts. I have access to the source code but haven't possibly come close to being an amateur at it's design. This is just my experience. Maybe it is something we are doing wrong. It's just the reality of our group.
Quick question - what framework? If it was mine, I think I stumbled upon the same discovery - I rewrote a portion of my attack processing macros to avoid calling macros one inside the other as much as it did, I suddenly lost a ton of lag. I mention it in another thread here.

I don't have much to say beyond "I think you're right" but I was just curious if you were working with my framework, because I found the same thing independently.

SpaceShot
Kobold
Posts: 20
Joined: Mon Jun 16, 2008 8:53 am

Re: Macro Best Practices

Post by SpaceShot »

Well, I didn't want anyone to feel like they were being called out. So let me praise the framework first.

If not for the Veggiesama framework, my group would not be playing D&D online, across the country, once a week... and having an absolute blast! That framework got us excited to play and the sample code it provided me was more than enough to be inspired to condense and rewrite. Sure, I eliminated a LOT of functionality and you HAVE to be a programmer to understand it (mainly one friend and I do all the macro work now), but we were having major lag problems.

As I stepped through MapTool code, I guessed (and it was a pure guess) that the culprit was passing a json from macro to macro in some of the calls. Now, in many environments this is great. I can't say for sure if that json is being "passed by reference" or "passed by value" (which is an implementation detail), but I noted when I reduced the data being passed around as much as possible and made chunkier macros, the lag disappeared and the crashing stopped.

There is also the factor of the very very old computers in play. When my programmer friend and I tested, we never had problems. Was that due to connection, computer, size of group at the time, or the macro coder? <shrug>

So, that is why I am careful to say this is merely my experience.

So, as a professional in the field, I had to agree with the solid programming practice of abstraction, abstraction, abstraction. I just found in practice, I couldn't do it here. And I figured if this was a best practice thread, I should let others benefit from my experience.

I don't mean that as a slight in any way. Once again, if not for MapTool and that framework, my group would not have gotten excited about gaming again and we would not be gaming every week.

I am not a Java programmer, but I am a Windows/.NET programmer. I am still looking to this project as being a nice entry point into Java for me. It is an environment I want to learn more about and get some expertise in, but that's for another forum. :)

User avatar
RPTroll
TheBard
Posts: 3159
Joined: Tue Mar 21, 2006 7:26 pm
Location: Austin, Tx
Contact:

Re: Macro Best Practices

Post by RPTroll »

I've got a lag problem in my framework as well and I suspect this is the culprit. Its the macros that have the deepest nesting.

Frak!
ImageImage ImageImageImageImage
Support RPTools by shopping
Image
Image

Post Reply

Return to “Macros”