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

Sheet development - Delaying setAttrs

1673033020
Jiboux
Pro
Sheet Author
Compendium Curator
Hello all, For reasons I will detail further in the post, I want to make a setAttrs after a certain time delay. See below (setAttrsLog is a passthrough of setAttrs with logging the values if we want to.     setTimeout( function() {       if(APIflag_local == APIflag_stored){         log("sendAPIflag sending flag " + APIflag_local);         setAttrsLog({"APIflag" : APIflag_local});         APIflag_local="";       } else { log("sendAPIflag interrupted by other thread " + APIflag_stored); return;}     }, 1000);  I had forgotten I had already tried something like this in the past and couldn't make it work due to the following message: Character Sheet Error: Trying to do setAttrs when no character is active in sandbox. Looks like if you nest a setAttrs in a setTimeOut, it somehow "forgets" who is the character. Has anyone already found a solution to make such delayed setAttrs More details on why I want to do this: We have a Character Sheet that works, with a companion API. Because there are some functions that only the API can do, we have made a communication channel between the sheetworker and the API, with an Attribute called "APIflag", where the Sheetworker can write commands to be executed by the API. This works well, as long as there is one command at a time, but we have now cases where the sheetworker fires various threads that all want to write APIflag, and because of this, the API is missing some of the commands. In the past, when the different sheetworkers were called recursively, I could just have the value travel back to the root of the recursive tree, and have one single setAttrs, but I am now in a case where too many are firing from different places, that such strategy becomes difficult to implement. So I was thinking to make it differently were instead the flag would be buffered on a local var, and a routine would wait 1000 ms were the local buffer would be unmodified before actually writing it to the Firebase attribute...  Nice idea on paper... Just on paper...
1673038384
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
You have two options: Rewrite your API to make it be the event listener (This is what the API is designed to do) Rewrite your sheet code so that it only ever uses a single setAttrs regardless of how many changes are made. #2 is how I recommend writing sheets anyways, and what the K-scaffold automates. #1 is how the API is designed to work; write your API to watch for certain events and do a thing. There's no reason why your API couldn't just watch for attribute changes and do a thing based on what attribute changed instead of locking it to the apiflag attribute.
1673046656
Jiboux
Pro
Sheet Author
Compendium Curator
Hello Scott, 1. Not sure to understand the option... I do believe that is what it already does (i.e. the sheetworker writes an attribute that the API listens to and reacts to) 2. Sure, rewriting everything in a different style would be a solution, and I have already seen this recommandation in the past, thinking that was very neat... But there is already 10.000+ lines of code, and a lot of development so rewriting all is not really the option I was leaning to. Oh, Ok, I think I just understood your point regarding 1 : it s called APIflag, but it actually contains the string of commands we want to be executed. It is not a boolean flag.
1673302276
Oosh
Sheet Author
API Scripter
You could probably use a queue if you have the potential of multiple commands going through within your timer length. And I think Scott was suggesting to use the mod script API to make the change, not setAttrs. setAttrs will only work asynchronously with the async framework hack, and that will probably ruin your sheet's mod API functionality. So something like !my-sheet-api --queue <char-id>|<attributeName>=<newValue> <timestamp> That gets pushed to a queue, the queue worker works the queue by reading the timestamps and sleeping until each one is ready to be processed. The queue worker has to shift() the jobs, and new ones need to be push() 'ed to make sure they're always in chronological order. That avoids the issues with setAttrs, but it does tie you to the mod sandbox, but if I remember rightly this sheet already requires it anyway?
1673303027

Edited 1673303273
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Not quite, I was suggesting that the API should listen for changes to the attributes that it cares about and have defined things it does based on the new value of the attribute and current settings of the sheet in question. The API is really good at watching for a whole host of changes and responding to those changes efficiently. It's also really good at gathering additional information about the game state at run time of the change to control exactly what it does in response. However because of the asynchronous nature of the backend-frontend connection, it is not good at getting that information through a single channel; that just ignores  the strengths of the API environment while magnifying its weaknesses. EDIT: I'll say what I said last time Jiboux, you just need to pay your tech debt and rewrite the pieces of the sheet that don't make sense with how the Roll20 environment is set up.
1673304457
Jiboux
Pro
Sheet Author
Compendium Curator
@Scott C. EDIT: I'll say what I said last time Jiboux, you just need to pay your tech debt and rewrite the pieces of the sheet that don't make sense with how the Roll20 environment is set up. I know, and I understand the recommandation... Just didn't reach the point where I am acting on it... Also for the understanding, we are 2 developpers working on it (me on the HTML side, and another on the API side), so any major rewrite need to get the availability of both... Even a higher step... About the API not being good with single channel, very interesting point. I was thinking that instead of this unique channel we have, we could literally have one APIflag for every repitem that we need but my partner was concerned about the growth of attributes and impacts on performance (the sheet and API cater for a westmarch game with a lot of players and that is facing performance issues)... I'll rethink if it could be the way with good safeguards that if there is no API, these Attributes should not be created (but API stability being what it is, this is always tricky because there is no good detection from sheetworker that API is running) @Oosh : I am still not sure I understand your idea correctly... Where would you implement the queue ? In the Sheetworker ? Or is the sheetworker able to send a message that could be directly read by the API (which would in fact make the whole sibject moot... Instead of trying to send commands to the API via an Attribute, he could directly trigger the API functions)
1673307219
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
There shouldn't be a need for any apiFlag attributes. The API can monitor any and all attributes on the sheet (heck, you have to jump through special hoops to get it to not monitor all attributes). Just have the API watch all attributes, and do things based on what attribute is changed, what the new value is, what the old value was, and what the current values of other attributes on the sheet are. This is what the API is designed for and how event based programming is supposed to work.
1673308737
Jiboux
Pro
Sheet Author
Compendium Curator
I understand that, but should maybe explain a bit more my use case. What I actually do here is send a command from the sheetworker to the API to do a certain action that the sheetworker can't do itself... This has been used for different purposes: 1-Sheetworker can't DELETE an attribute... In our routine that converts a character from an older version to a newer one, the sheetworker will send to the API commands to delete old attributes that became unused. This allows to keep things clean 2-Here in our particular case, we have what we call "links"... In some cases that make sense in the system, we want a certain repitem value to depend on the value of another repitem. For example, when using the "Surprise Strike" Ability, the Rank of Surprise Strike adds to the weapon damage. What we do is that we "link" Surprise Strike to the Weapon so that when rolling Surprise Strike it adds both the surprise strike rank and the linked weapon base damage. When done manually these are all stuff that the user is doing via API buttons, but now that we drop characters from the compendium (and that the compendium drop is managed via the sheetworker), we want the sheetworker when it recognizes a case where he needs a link to request the API to create the link... So it sends a "command" in the APIFlag "Addlink : <RowIdfrom> : <RowIdTo>". (When the user creates the link manually, the button in the chat will send the exact same command to the API parser) As the whole management of the compendium drop is done in the sheetworker, the sheetworker functions are the ones that know what they want to happen... But the API has the routine to actually do it... Hence my question to Oosh : if there was any way that the Sheetworker sends message events (that queue naturally), that would suppress the whole need to create this "communication channel" 
1673311689

Edited 1673312308
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Just don't worry about the extraneous attributes. If you are creating lots of legacy attributes that are not used after a sheet update, and doing that frequently, you need to rethink how your update works.  This can likely be done entirely within the sheetworkers, no API necessary. I have several sheets that do similar things. Linking attributes between sheets for rolls: The Starfinder by Roll20 sheet allows players to enter themselves in a crew position on their starship and use their character's actual stats for starship rolls (working on an improved version of this using CRP for an in development sheet). Fully dynamic modification system: The Starfinder HUD, Starfinder by Roll20, and Genefunk sheets have a dynamic mods system that uses modifications entered in one repeating section to apply to other attributes on the sheet; even adding additional attributes to other attributes (e.g. +@{strength_mod} to wisdom saves). The 5e by Roll20 sheet isn't by me, but it also has a similar system for applying static modifications. Linking multiple drops together so that they are removed as one. The Genefunk sheet also uses compendium created links between repeating section items when a compendium item drops multiple things so that removing the source item removes the children items. Pseudo nested repeating sections: The official Scion (AKA Storypath) sheet has pseudo nested-repeating sections that connect "children" repeating items to their parent item for use in rolls and connecting items together. Moving repeating items between sections: The Backbone sheet allows users to equip/unequip gear, weapons, and even armor (which isn't even a repeating section). When they are unequipped, they are in the stored gear section. When they are equipped they are in their respective section. Doing #2 via sheetworker does likely require a large refactoring of your infrastructure to make it work. However, I frankly don't see a way for you to do what you want without a large refactor anyways, and that refactor can either be to continue using the API system you've put together, or to create a streamlined sheetworker solution that will be usable by any player or gm that uses your sheet. Since you are creating a compendium, I'd highly suggest that you think again about whether putting so much of the sheet's functionality behind the API wall is a good customer experience for those that are going to buy the product.
1673319881
Jiboux
Pro
Sheet Author
Compendium Curator
Thanks Scott. You have my mind blown... Don't know if I'll be able to refactor all, (Have already spent far too much time on the sheet, and am nearly done with current structure), but really mind blown on the possibilities. As said, I didn't originate the project, and some of split between what is API and sheetworker is legacy, but there are a few functionalities that left the API very present: 1-Sequences that involve multiple tokens : with the API, an attacker can roll the attack, the API compares to the defense of the target and adjusts the damage according to the result before applying the damage to the target... Knowing the user has to make choices here, along the way the API allows to do that from the chat (in the example below, after an initial attack, compared to the defense of the target, the attacker can chose additional damages because his roll is good, or do something else, he then rolls damage, and applies the damage to the target) 2-The API allowed for more token-character synchronization. All "favorite" abilities have a token action button created, and the token markers are synchronized with some status/bonus on the sheet. As a DM, I hardly open the character sheet of any of the creatures/npcs during a combat For the links, I believe the reason that made us keep us behind the API is the user interface we needed to actually get the user to input the name of the ability he wanted to link to... But I do believe that now that jquery is available, this could be circunvented... In any case, even if I am very close to release, and therefore not thinking I will right now go month back to overhaul, thanks for all the advice that gives perspective on things that can be done differently (with experience). But we will certainly have to rethink things along the way.