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

[Script] SheetChangeTracker API

1517838542

Edited 1521579055
Hello everyone! My second script is here -&nbsp;it tracks changes to the character sheets, and makes them visible to the GM (as chat messages). this allows to correct mistakes, or even detect attempts at cheating. How many times have I changed a value on a sheet via missclicks - and then... wait, what was the value again? What about the players changing stuff on the sheet that they should not? Specially beginners can do that easily. And asking players to change their inventory mid-session might seem like a good idea - but then it irks you not being sure if they did it right? Well, SheetChangeTracker comes to the rescue! I think installing and using this script is all pretty simple! Hope it adds to your gaming sessions :) # Functionality information: *&nbsp;it will detect all changes in a character sheet. * it will send messages that make it clear what sheet was changed and what specific attributes were changed. * this script has a default behaviour where it switches between two modes of operation automatically. You can also choose which mode to use, if you want to use only one of them. * mode 1: if one of the GMs is online it will switch to "game session mode". the messages sent about sheet changes will be hidden from players and will be short and concise to avoid spamming the chat too much for the GM. they will have a yellow background. * mode 2: if no GM is online it will switch to "in-between sessions mode". the messages will be visible to everyone, all the players and GMs. they will explain that a change was detected and ask to undo it, if player lacks permission from the GM for these changes. These messages will be orange. * with the command !ON_OFF_SheetChangeTracker the script can be paused. To resume tracking send the command again. * you can look up all tracking done in the past by checking the chat archive: launch your game and at the top of the chat click "View all chat entries for this game »". you can use ctrl+f to find the word "golly", it automatically finds the messages that concern a player changing things while you were offline. * note on the chat archive: no matter if you use this script or not, the chat archive can become so huge that it is not possible to load anymore. to keep the chat archive acessible over time, you should save the chat archive on your PC and then clean the chat archive every once in a while. You can save the chat log before erasing it, by opening the chat archive and copying all its content to a text file you save on your PC. It can be erased on game page under settings, right above the option to erase game - please be careful not to erase your own game, read the confirmation box that pops-up. # Optional Settings * it is possible to stop specific attributes from being tracked. *&nbsp;you can also force the script to only provide concise messages, so mode 1 is always on, no matter who is online. *&nbsp;the message output in mode 2 is personalizable too. # Formatting information: * here are the default messages that will be output: * mode 1: /w gm :::: char name - "[character name]" :::: attribute changed - "[attribute name]" :::: original value (current/max) - "[old value current]"/"[old value max]" :::: changed value (current/max) - "[new value current]"/"[new value max]" :::: * mode 2 : /em Golly, a character sheet change was detected! If GM gave you permission, its all good. If not, change it back please. \n /em Char name - "[character name]" \n /em Attribute changed - "[attribute name]" \n /em Original value (current/max) - "[old value current]"/"[old value max]" \n /em Changed value (current/max) - "[new value current]"/"[new value max]" # Install instructions: 1. add API to library - this means create a New Script, paste in all the code, save script 2. if you want to, change optional settings in the code. Save Script again. 3. script is ready to use! # Link to the script: <a href="https://gist.github.com/gui8311/aac68f3b6f96dacce8" rel="nofollow">https://gist.github.com/gui8311/aac68f3b6f96dacce8</a>... # Feedback and Questions: If you have questions or feedback, write a comment on this thread or create a new post for it and add [gui8312] and [SheetChangeTracker] to the post title, so it is easier to find for me. Any questions, suggestions or bug reports are appreciated! :) # Changelog **v1.0 * release **v1.1 * added more robust error prevention code *&nbsp;more extensive testing done for 5E OGL sheet **v1.2 * script now detects changes on all values in a character sheet - no exceptions. * stability fix, limiting allowed types for chat variables.&nbsp; **v1.3 * added optional settings: ability to stop traking specific attributes * added&nbsp;!ON_OFF_SheetChangeTracker command, allowing for pausing of script * modified the way messages are output, allowing for good formatting for entries with line breaks (like bigger texts describing the backstory of a character, with multiple paragraphs) **v1.3.1 * changed the output message validity check which was crashing the API sandbox upon creating a new character sheet. **v1.3.2 * fixed a problem with identifying if GMs are online **v1.4 * added the possibility to change mode 2 output message (under optional settings). * added the possibility to disable mode 2&nbsp;(under optional settings). **v1.5 * special thanks to The Aaron, who gave me the advice that allowed for this update to be implemented. * GM detection is now fully automatic, no need to set or update GM IDs under the script settings. * Updated install guide. # Tags gui8311 gui8312 script API character sheet change tracker roll20
1517839997
The Aaron
Pro
API Scripter
Cute! You can check if a player is a GM with the playerIsGM() function.&nbsp; Note that this function only returns true for logged in players that are currently logged in as GMs.&nbsp; GMs logged in as players are not considered GMs.&nbsp; You could check if any GMs are logged in with something like this (untested code): const AnyGMLoggedIn = _.chain(findObjs({type:'player',_online: true})) &nbsp; &nbsp; .filter((p)=&gt;playerIsGM(p.id)) &nbsp; &nbsp; .reduce((m,c)=&gt;m || c,false) &nbsp; &nbsp; .value();
thanks Aaron! will look into that :)
1517902106
GiGs
Pro
Sheet Author
API Scripter
This is a brilliant idea for a script. In my first session as GM, I was looking at a players sheet at the same time as they were, and together we managed to mess up the values. It would have been great to have a record of those changes.
1517903534
GiGs
Pro
Sheet Author
API Scripter
Aaron, that GM code snippet you posted: does that produce an array of logged in GMs?&nbsp;
1517903872

Edited 1517903982
@G G Thanks! :) Yeah the sheets are needed but too finicky... by the way I already tested it for a full session, worked amazing! I could easily follow what players did. Asking them for changing something felt safe. If was so easy to notice that a beginner had clicked the proficiency checkbox instaed of rolling the skill it felt like this was what I always had been missing as a GM haha and it tracks even more then expected, tracks the equipment values too as an example, so I have to investigate and see if everything is tracked, which I did not expect.&nbsp;
1517907455

Edited 1517907721
Jakob
Sheet Author
API Scripter
Aaron, that GM code snippet you posted: does that produce an array of logged in GMs? Let me answer that: no, it produces true or false depending on if any GMs are logged in. If you want an array of logged in GMs, you can just use this part findObjs({type:'player',_online: true}).filter((p)=&gt;playerIsGM(p.id)) (not using Underscore because it's 2018 :P).
1517907530
GiGs
Pro
Sheet Author
API Scripter
Two changes I would suggest: 1) I'd change the message when GM is offline from "Please change it back to avoid gameplay problems" to something like, "Did you mean to change that?"&nbsp; In some games, the players ill have permission to edit their characters, or might have XP to spend out of session, and so on.&nbsp; 2) Look into tracking the number of gms with an array, and using loops to check them, which allows you to have any number of GMs online at once.&nbsp;
1517907612
GiGs
Pro
Sheet Author
API Scripter
Thanks, Jakob!
1517909349

Edited 1517910762
GiGs
Pro
Sheet Author
API Scripter
Maybe put the attribute changing code in a try catch block, something like try { // code explained: get info about character that just changed, including the name of said character var char_ID = obj.get("_characterid"); var Character = findObjs({_type: "character", _id: char_ID})[0]; var Character_Name = Character.get("name") } catch (err) { sendChat("SheetChange","/w GM Something went wrong. " ); sendChat("SheetChange","/w GM " + err.name + "; " + err.message); return; // to ensure the rest of the code doesnt run when there's an error. } I'd move the obj.get and related statements from the sendchat statements below up into that try block, and save them into a string variable.&nbsp; Then the sendchat statements later would just be used to construct the printout, but not perform the analysis. Having the same calculation done in two separate places in the script is bad for code maintenance.&nbsp;
1517910397

Edited 1518020033
EDIT: Fixed in v1.2 Tested the 5E OGL Sheet: seems to work with all field pretty much, as long as they have been used once. Example: bio field - first time typing and nothing happens, update that info and it will be logged by the script. not ideal, bit still very useful for me. Example2: class change - will not log the class change the very first time, but will log the profeciency changes and they were already set up when I first typed in the attributs. so, if anything new I do updates any of the values I already use it will also warn me. the script seems to protect whatever we have prepared before (the values we care about I think).
1517910503
GiGs
Pro
Sheet Author
API Scripter
I wonder if that's because there's no previous value.&nbsp; You could probably edit the code so that it prints out if there's no previous value.
1517910861

Edited 1517910913
GiGs
Pro
Sheet Author
API Scripter
Ah in that case,&nbsp; there must be some way to test if the attribute was just created. By the way, in case you missed it,&nbsp;I edited this earlier post with some suggestions:&nbsp; <a href="https://app.roll20.net/forum/permalink/6053737/" rel="nofollow">https://app.roll20.net/forum/permalink/6053737/</a> Nvm, you ninja'd me :)
try { // code explained: get info about character that just changed, including the name of said character var char_ID = obj.get("_characterid"); var Character = findObjs({_type: "character", _id: char_ID})[0]; var Character_Name = Character.get("name") } catch (err) { sendChat("SheetChange","/w GM Something went wrong. " ); sendChat("SheetChange","/w GM " + err.name + "; " + err.message); return; // to ensure the rest of the code doesnt run when there's an error. } I assume this is a sandbox within the sandbox? it tries it out to see if any errors would come out and then avoids API crashing if an error would pop up?
1517911458
GiGs
Pro
Sheet Author
API Scripter
yes, pretty much. It runs the code inside the try section. If an error occurs there, it then jumps to the catch block and runs the code there.&nbsp;If there's no error in the try block, the catch block does not run. The code skips over it. If there's also a separate error in the catch block, that error won't be caught (there's more advanced checking to catch that, but you shouldn't need that here as long as those sendchat statements are properly formed). So you can avoid sandbox crashes with this approach, and perform more diagnostics.
1517911540

Edited 1517911698
GiGs
Pro
Sheet Author
API Scripter
I'd change the try block to something like this var char_ID, Character, Character_Name, attrName, prevCurrent, prevMax, newCurrent, newMax ; try { &nbsp; &nbsp; // code explained: get info about character that just changed, including the name of said character char_ID = obj.get("_characterid"); Character = findObjs({_type: "character", _id: char_ID})[0]; Character_Name = Character.get("name") attrName = obj.get("name"); prevCurrent = prev["current"]; prevMax = prev["max"]; newCurrent = obj.get("current"); newMax = obj.get("max"); &nbsp; &nbsp; } catch (err) { sendChat("SheetChange","/w GM Something went wrong. " ); sendChat("SheetChange","/w GM " + err.name + "; " + err.message); return;&nbsp; // to ensure the rest of the code doesnt run when there's an error. } And replace the later sendchat statements like this &nbsp;if(GM1_online == true || GM2_online == true || GM3_online == true || GM4_online == true){ &nbsp; &nbsp; &nbsp; &nbsp; sendChat("GM","/w gm :::: char name - " + Character_Name + " :::: attribute changed - " + attrName + " :::: original value (current/max) - " + prevCurrent + "/" + prevMax + " :::: changed value (current/max) - " + newCurrent + "/" +newMax + " ::::" ); &nbsp; &nbsp; } &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; // code explained: "in-between sessions mode"; the script works more visibly: a message makes it clear to players that stats are being tracked and that something changed.&nbsp; &nbsp; &nbsp; // code explained: the GM can ctrl+f search for golly on the chat archive to see if anything was changed when he was away. &nbsp; &nbsp; else{ &nbsp; &nbsp; &nbsp; &nbsp; sendChat("","/em Golly! the GM is not here and it seems like you changed something with your character."); &nbsp; &nbsp; &nbsp; &nbsp; sendChat("","/em Please change it back to avoid gameplay problems (change information follows below)."); &nbsp; &nbsp; &nbsp; &nbsp; sendChat("","/em No worries, worst case scenario all changes are logged and the GM can fix it for you if needed."); &nbsp; &nbsp; &nbsp; &nbsp; sendChat("","/em Char name - " + Character_Name ); &nbsp; &nbsp; &nbsp; &nbsp; sendChat("","/em Attribute changed - " + attrName ); &nbsp; &nbsp; &nbsp; &nbsp; sendChat("","/em Original value (current/max) - " + prevCurrent + "/" + prevMax ); &nbsp; &nbsp; &nbsp; &nbsp; sendChat("","/em Changed value (current/max) - " + newCurrent+ "/" + newMax); &nbsp; &nbsp; }
1517911747
GiGs
Pro
Sheet Author
API Scripter
I quickly edited my last post - just remembered you need to declare the variables before the try block, if you're using them later.
1517912334

Edited 1517912393
G G said: I quickly edited my last post - just remembered you need to declare the variables before the try block, if you're using them later. I was just doing what you suggested on my own and was wondering exactly if the vars stay. seriously appreciate the help! EDIT: but hey the code ran! even with the vars defined inside of the try block
1517912564

Edited 1518020096
EDIT: Fixed in v1.2 the error repeated itself, now I wrote it down. again from equipment management. I changed the name of an equipment slot. Could not determine result type of: [{"type":"M","expr":"0+-2"},{"type":"L","text":"STR"}] undefined EDIT: it did not trigge the catch block :( if anyone can even tell me what type of problem this is, much appreciated. link to current code (work in progress) <a href="https://gist.github.com/gui8311/c9b36be65ad160a4a23a12b84f101e7d" rel="nofollow">https://gist.github.com/gui8311/c9b36be65ad160a4a23a12b84f101e7d</a>
why not run everything inside a try block?
1517913487
GiGs
Pro
Sheet Author
API Scripter
If you're not sure where the error is, you can stick everything in the try block. It's not very elegant though.
1517913938
GiGs
Pro
Sheet Author
API Scripter
I'm not familiar with the sheet you are testing, but that error suggest one of the attributes might be storing an array of objects, not a simple string. It occurs to me that your sendchat statements are expecting simple attributes, so you may need to test for data type of prev and obj, and convert them to strings before you can print them out.&nbsp; This is getting into areas I'm not that confident about, and I'm about to go to bed. Hopefully someone else has some suggestions before I return!
1517913976

Edited 1518020132
EDIT: Fixed in v1.2 at least its a quick fix for a sandbox crash. and the error seems to happen in a very specific situation only, that is hard to even reproduce on purpose. Using a try command probably also makes code resource intensive, I would assume it first runs the code with a different level and then runs it again fully if there are no errors.
1518012725

Edited 1518013874
Update: **V1.2 * script now detects changes on all values in a character sheet - no exceptions. * stability fix, limiting allowed types for chat variables.
1518047503
GiGs
Pro
Sheet Author
API Scripter
What was causing the error?
1518051380

Edited 1518052796
The missing typeof check was allowing the crash with the sendChat function when something too weird got sent there (sheet formatting piece of code is my best guess - like background values changed when someone re-orders the items in the equipment). Now all value fields are logged, but not background pieces of code from sheet formatting or other deep level stuff. which works out great, filters out info GMs do not care about and makes script way more stable. and the lack of add:attribute event tracking was not allowing for new attribtes to be registered, done that too. now it does not matter what field is changed and under which conditions, it tracks it all.&nbsp; by the way, considering adding a few filtering possibilities, if GMs do not want to track something in specific or even groups of values. like the whole bio tab. and also a switch could be nice, as well as automatic GM detection as The Aaron suggested.
1518058975
GiGs
Pro
Sheet Author
API Scripter
The filtering and auto GM detection are both good ideas. One thing i noticed was sheets with lots of sheet events linked to attributes can trigger a huge cascade of messages (DEX changed, so now dex mod changes, and these saving throws and skills all change, and AC ... and so on). It might be an idea to reign that in somehow, through either not triggering on cascading changes, or grouping all changes that occur within a short time frame into a single formatted output message. i havent thought about how easy that would be to pull off, but one approach would involve temporarily storing changes in the state variable.&nbsp;
1518082739

Edited 1518083158
Yeah I know about those cascading events... and I think it's essential to track every single change, even those that are consequence of another change - also helps with knowing how sheets work. But I may give a setting to toggle cascading&nbsp;events&nbsp;into a mode 3, with things like, attribute names: (1) Dexterity (2) dex_save (3) AC :::: old value (current/max): (1) 10(2) 5 (3) AC 8 Thanks for the suggestion! :) by the way the visible message now says sonething along the lines of "if the GM did not give you permission to change things, please change the value back to avoid gameplay problems." compared to the previous message that assumed it to be a problem always.
1518099487

Edited 1518612700
Update: **v1.3 * added optional settings: ability to stop traking specific attributes * added !ON_OFF_SheetChangeTracker command, allowing for pausing of script * modified the way messages are output, allowing for good formatting even for values with line breaks (like bigger texts describing the backstory of a character, with multiple paragraphs) EDIT: **v1.3.1 * changed the output message validity check, which was creashing the API sandbox upon creating a new character sheet. EDIT2: **v1.3.2 * fixed a problem with identifying if GMs are online
1518137124

Edited 1519566451
This seems like a stable and useful version, so i will probbaly keep the script in v1.3.2 for a good while. With this in mind, let me just prepare for when this forum post closes: if you have questions or feedback, create a new post for it and add&nbsp;[SheetChangeTracker] to the post title, so it is easier to find for me.
For some reason your script isn't detecting me when I'm logged on. I've used the command and cut and paste the exact code into the API script.
1519563051

Edited 1519566486
@DMhyde hm... you need to paste the ID, and leave the quotation marks in the code. does it at least output the orange messages? so, mode 2 messages? EDIT: I just reinstalled the script in my game, works fine for me...
1519566384

Edited 1520305123
@DMhyde I just updated the Script! My suggestion: get the new version (v1.4) and worst case scenario set the optional setting variable "onlyConcise" to true. This way the messages are always hidden from players, and GM online status is not relevant anymore.
1520526558

Edited 1520528079
Update: **v1.5 * special thanks to The Aaron, who gave me the advice that allowed for this update to be implemented. * GM detection is now fully automatic, no need to set GM IDs under the script settings. * Updated install guide!