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 .
×
Create a free account

API Request: getDiceRoll(s)

I've used the sendChat() command to process a dice roll. The problem I have is that its function runs asynchronously, which means that any processing that I do in that function happens after my current operation. I need the result of the dice roll before I can continue my operations, so I have been using randomInteger() but of course this does not handle all of the elegance that roll20 has built into its dice roll processing. It would be nice to have a simple getDiceRoll(s) API call that I can make that will process 's' as if it were an inline dice roll. It would be great if it could handle the @ and # parameters as well, but mainly it would just be nice to be able to send in something like "1d20rokh3+4", and have it return a value. If there I have missed another way of making a dice roll, please tell me. Thanks, Andrew
1426078135
The Aaron
Pro
API Scripter
Dealing with the asynchronous calls can be a bit frustrating, but it shouldn't prevent you from doing anything. If you want to post your code maybe there is something we can do to help you. If you want a function like you describe, the suggestions and ideas forum is the place to post it.
The call has to be asynchronous because the functionality that generates the dice results is itself an asynchronous process. So I'm afraid that it is impossible to make it synchronous. As Aaron said, that shouldn't keep you from doing anything you need to do, so just let us know if we can help.
1426085874
Lithl
Pro
Sheet Author
API Scripter
If you need your code to execute in the order A->B->C, but B is asynchronous, you basically just need to call C from B. Your code will still execute in the same order, there's just a pause as the asychronous process is performed. At its most basic level, you'd get something like this: on('chat:message', function(msg) { // Perform step A sendChat('', '/roll 1d20rokh3+4', function(ops) { // Perform step B stepC(); }); }); function stepC() { // Perform step C } There are ways to streamline this sort of thing (for example, using a semaphore ), but this is the gist of it.
Brian said: There are ways to streamline this sort of thing (for example, using a semaphore ), but this is the gist of it. Busy waiting is evil and should be avoided; for there be shoggoths down that pit. So I've been starting to work my way through that O'rielly book, and have found that asynchronous actions, should be embraced, not shunned in JS (you don't want to hang a webpage on a DB call!). It reminds me of the old single vs mutli-threaded argument back in the day. As far as this is concerned, it makes sense for an immediate call back, but you're dealing with a remote fetch here; and javascript is single threaded, so the two don't mix. What I do for my blaster script is that I do all relevant logic within the callback routine, including calling other functions as subroutines to aid in whatever I'm doing there. Outside the call back it simply ends after delegating the remainder of the work to be done by the callbacks. Just be aware that whenever there's a fetch failure, the API server will have an internal 'Quantum Roll Fetch Failure' and will crash your script. Currently there's no way to catch this as it's server side so there's no recovery mechanism.
1426117525
The Aaron
Pro
API Scripter
Ken L. said: Brian said: There are ways to streamline this sort of thing (for example, using a semaphore ), but this is the gist of it. Busy waiting is evil and should be avoided; for there be shoggoths down that pit. I thought the same thing at first, but Brian's semaphore isn't busy waiting, it's more like _.after() in semaphore's clothing. It delays calling a callback until after so many actions are taken. I suppose it feels more synchronous, though it's still about the same, and probably more confusing than just doing the async to begin with.
1426133938
Lithl
Pro
Sheet Author
API Scripter
Ken L. said: Brian said: There are ways to streamline this sort of thing (for example, using a semaphore ), but this is the gist of it. Busy waiting is evil and should be avoided; for there be shoggoths down that pit. Okay? What I'm talking about isn't busy-wait. (Also, in point of fact, the update-draw loop employed by most video games is basically a busy-wait. It has its place.)
My fault for not reading it, but I've had some bad experience with someone putting a busy wait into a semaphore before, odd thing was is that this was in C so he could have just used sleep/noitify. Oh yea, off topic, but for the script overview template mind, if I just duplicate that and strip out all the auto-linking? It breaks for scripts that aren't on the offfical API repository.
1426138485
The Aaron
Pro
API Scripter
Brian said: (Also, in point of fact, the update-draw loop employed by most video games is basically a busy-wait. It has its place.) I wouldn't call that busy waiting. Usually we'd wait for vertical refresh, or keep working calculations until we had to update the frame. Not really just spinning for an interrupt...
1426139626
Lithl
Pro
Sheet Author
API Scripter
The Aaron said: Brian said: (Also, in point of fact, the update-draw loop employed by most video games is basically a busy-wait. It has its place.) I wouldn't call that busy waiting. Usually we'd wait for vertical refresh, or keep working calculations until we had to update the frame. Not really just spinning for an interrupt... I guess it's not the greatest comparison. Ken L. said: Oh yea, off topic, but for the script overview template mind, if I just duplicate that and strip out all the auto-linking? It breaks for scripts that aren't on the offfical API repository. Please clarify what you mean. Obviously the automatic link to the repo isn't going to work if the script isn't on the repo, but I don't know what you mean by "break" other than that, and you said you removed the auto-linking from your copy.
1426165225

Edited 1426197028
Wow. Thanks for all the feedback everyone. First, I agree that async operations should be embraced lovingly, but sometimes they make simple tasks more difficult than they should be. Usually I find myself writing synchronous functions that have an asynchronous wrapper for use as often as plausible. Basically, I'm writing a bunch of scripts to help me DM my DnD5e games. When writing the script below, all I wanted to do was roll initiative for every character in the turn order, and then sort the order. I didn't want to do the work of sorting and setting the turn order until after every character had rolled. I found it inconvenient to deal with the sendChat async to parse a roll, so I just did it manually. The next API I wrote was to roll up random loot. Obviously, there were many more roll definitions in my tables, and instead of keeping a value of "1d6" I had to keep a diceCount: 1, and diceSize: 6 to roll up my own dice. Basically, I wrote my own getDiceRoll() routine...it just doesn't handle all of the fanciness of Roll20's roll parser, so I thought it would be a nice API call to be able to make, but if it can't be done...it can't be done. --- var chatCommands = { dnd5e_autoinitiative: function(args, msg) { if ( ! playerIsGM(msg.playerid) ) return; var npcOnly = (args !== undefined) && (args.length > 0) && (args[0] === 'npconly'); var turnorder = Campaign().get('turnorder'); if (turnorder === undefined) return; turnorder = JSON.parse(turnorder); _.each(turnorder, function(to) { var token = getObj('graphic', to.id); if(token === undefined) return; var represents = token.get('represents'); if(represents === '') return; var isNpc = getAttrByName(represents, 'is_npc'); isNpc = ( (isNpc !== undefined) && (isNpc === '1') ); var dex = 10; var ini = 0; if(isNpc) { dex = parseInt(getAttrByName(represents, 'npc_dexterity')); ini = parseInt(getAttrByName(represents, 'npc_initiative')); } else if(npcOnly) { return; } else { dex = parseInt(getAttrByName(represents, 'dexterity')); ini = parseInt(getAttrByName(represents, 'initiative')); } var mod = Math.floor((dex-10)/2); var rollValue = randomInteger(20) + mod + ini; var tieBreaker = randomInteger(99); to.pr = parseFloat(rollValue + '.' + mod + (tieBreaker < 10 ? '0' : '') + tieBreaker); }); turnorder.sort(compareTurnOrders); Campaign().set('turnorder', JSON.stringify(turnorder)); } }; Thanks again, Andrew
1426171771
The Aaron
Roll20 Production Team
API Scripter
( Moved to API. Code Block is in the ¶ menu at the top left when editing. )
1426172533
The Aaron
Pro
API Scripter
Ironically, it's actually on my list to do pretty much what you're doing in my GroupInitiative script. (using sendChat() to roll, as opposed to randomInteger()). My plan is to parse all the items that need to be rolled, building up a list of multiple inline rolls (maintaining the order), then execute all of them in one sendChat(), then harvest the results in the correct order. I already do this for the PowerCard PreProcessor script I've posted a few places. It's not terribly difficult, particularly with _.chain(), _.map(), _.reduce() and the like. That takes your asynchronous concerns down to only one callback. I can see how it would be daunting to feel like you need to have some massively nested call chain of asynchronous callbacks. Another method that could be used to handle this is to embrace something similar to Brian's semaphore technique. The basic idea would be 1) know how many things you have to process, 2) fire off the processing for all of them. 3) keep a score card for what has been processed, 4) when the last thing is processed, call your finalize code. Brian's semaphore basically does this with it's initialized count. You can do the same thing seamlessly with the _.after() function, provided you know the count. I'm happy to talk about either of those techniques ad nauseam, just let me know. =D
Thanks for moving the thread for me...and for pointing me to the code block :) Yeah, if I actually needed the impressive functionality of Roll20s roll engine, then I would turn to the semaphore method. Luckily all I really needed was a random number from 1 to x, and randomInteger is there for me to use for that. Actually, I would probably just write my own routine to wrap the sendChat into the semaphore and return the value to me. Thanks again for all the feedback.