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

long getSectionIDs

1722290880

Edited 1722300160
Hi, i'm troubleshooting a lag on my sheet, and after debugging i found something strange, when i get section ids on repeating sections, one specifically is getting a hell of a long time to respond I use the Roll20Async library(kind of), but here is the code and here is the debug i get in chrome i've spent time checking if the problem was on my side but i can't find a reason why ... and it's the same problem on each sheet with this repeating section can someone help me ? Have a great day and thanks for your time !
1722367841

Edited 1722367898
GiGs
Pro
Sheet Author
API Scripter
you'll need to post all code relevant to that repeating section. Also have you tried removing that repeating section in its entirety, just to check lag, to see if the sheet speeds up?
I assumed getting the id of a section would not trigger anything but, there is all the code triggered on ( 'change:repeating_martialarts remove:repeating_martialarts change:repeating_weapon:repweapondef change:repeating_weapon:repweaponabi remove:repeating_weapon' , TAS . _fn ( updateParry ));     on ( 'change:qc-parry' , TAS . _fn ( updateParry ));     async function updateParry ( e ) {         if ( e . sourceType !== "player" && e . triggerName !== 'wound-penalty' ) {             if ( debug >= 2 ) TAS . debug ( `updateParry:: TRIGGER FROM SCRIPT => CANCEL` );             return ;         }         if ( debug >= 2 ) TAS . debug ( 'updateParry:: e=' + JSON . stringify ( e ));         const setObj = await ParryUpdater . getUpdatedParryObj ();         if ( debug >= 2 ) TAS . debug ( 'updateParry:: setObj=' , setObj );         setAttrs ( setObj );     }     class ParryUpdater {         #maIds ;         #weaponIds ;         constructor () {}         async getAttrToBeRetrieved ( maIds , weaponIds ) {             let time = new TimeCounter ( 'ParryUpdater::getAttrToBeRetrieved' );             if ( debug === 3 ) TAS . debug ( `ParryUpdater::getAttrToBeRetrieved maIds= ${ maIds } , weaponIds= ${ weaponIds } ` );             this . #maIds = maIds || await getSectionIDsAsync ( 'martialarts' );             time . lap ( 'after AWAIT getMA_ID' );             this . #weaponIds = weaponIds || await getSectionIDsAsync ( 'weapon' );             time . lap ( `after AWAIT getWEAP_ID, size= ${this . #weaponIds . length } ` );             TAS . debug ( `ParryUpdate::getAttrToBeRetrieved #weaponIds= ${ JSON . stringify ( this . #weaponIds ) } ` );             const attrList = [ 'qc' , 'brawl' , 'melee' , 'dexterity' , 'qc-parry' , 'max-ma' , ... defAddedAttrs ];             time . lap ( 'after constructing attrList' );             this . #maIds . forEach ( id => attrList . push ( `repeating_martialarts_ ${ id } _repmartialarts` ));             time . lap ( 'after forEach on MA' );             this . #weaponIds . forEach ( id => attrList . push (                 `repeating_weapon_ ${ id } _repweapondef` ,                 `repeating_weapon_ ${ id } _repweaponabi` ,                 `repeating_weapon_ ${ id } _repweaponparry` ,                 `repeating_weapon_ ${ id } _repweaponparryspe`             ));             time . lap ( 'after forEach on WEAP' );             maAttrsArray . forEach ( attr => attrList . push ( attr ));             time . lap ( 'after forEach on MA LIST' );             return attrList ;         }         updateObj ( values , toBeUpdated = {}) {             const getFromUpdatedOrRetrieve = ( attrStr ) => ( toBeUpdated && attrStr in toBeUpdated ) ? toBeUpdated [ attrStr ] : values [ attrStr ];             let ma = 0 ;             for ( const maName of maAttrsArray )                 ma = Math . max ( ma , Number ( getFromUpdatedOrRetrieve ( maName )));             const dex = Number ( getFromUpdatedOrRetrieve ( 'dexterity' )),                 brawl = Number ( getFromUpdatedOrRetrieve ( 'brawl' )),                 melee = Number ( getFromUpdatedOrRetrieve ( 'melee' )),                 qcParry = Number ( getFromUpdatedOrRetrieve ( 'qc-parry' )),                 isQc = Number ( getFromUpdatedOrRetrieve ( 'qc' )),                 finalObj = JSON . parse ( JSON . stringify ( toBeUpdated )),                 addedDef = calcDefAddedAndSetOnslaughtApplied ( values , finalObj ),                 correspondingTable = { 'brawl' : brawl , 'melee' : melee };             for ( const maName of maAttrsArray ) correspondingTable [ maName ] = Number ( getFromUpdatedOrRetrieve ( maName ));             if ( debug >= 2 ) TAS . debug ( `ParryUpdater:updateObj:: values= ${ JSON . stringify ( values ) } ` );             ma = this . #maIds . reduce (( acc , id ) => Math . max ( acc , Number ( getFromUpdatedOrRetrieve ( `repeating_martialarts_ ${ id } _repmartialarts` ))), ma );             if ( debug === 3 ) TAS . debug ( `ParryUpdater:updateObj:: Max MA= ${ ma } , isQc= ${ isQc } , qcParry= ${ qcParry } ` );             if ( debug === 3 ) TAS . debug ( 'ParryUpdater:updateObj:: Unarmed Parry calc:' , ( isQc ? qcParry : 'Math.ceil(' + dex + ' + ' + brawl + ') / 2)' ));             var newParry = ( isQc ? qcParry : Math . ceil (( dex + brawl ) / 2 ));             if ( debug === 3 ) TAS . debug ( 'ParryUpdater:updateObj:: Unarmed Parry w/specialty calc:' , ( isQc ? qcParry + 1 : 'Math.ceil(' + dex + ' + ' + brawl + ' + 1) / 2)' ));             var newParrySpe = ( isQc ? qcParry + 1 : Math . ceil (( dex + brawl + 1 ) / 2 ));             if ( debug === 3 ) TAS . debug ( `ParryUpdater:updateObj:: applying addedDef: ${ addedDef } ` );             newParry += addedDef ;             newParrySpe += addedDef ;             if ( debug === 3 ) TAS . debug ( 'ParryUpdater:updateObj:: setAttrs!parry=' + newParry + ', parry-specialty=' + newParrySpe );             verifyAndSetIfDifferent ( values , finalObj , 'parry' , newParry );             verifyAndSetIfDifferent ( values , finalObj , 'parry-specialty' , newParrySpe );             verifyAndSetIfDifferent ( values , finalObj , 'max-ma' , ma );             if ( debug === 3 ) TAS . debug ( 'ParryUpdater:updateObj:: Updating Weapon Parry value' );             for ( const id of this . #weaponIds ) {                 let def = getFromUpdatedOrRetrieve ( `repeating_weapon_ ${ id } _repweapondef` ) || 0 ,                     abi = getFromUpdatedOrRetrieve ( `repeating_weapon_ ${ id } _repweaponabi` ) || 'brawl' ;                 const weapParry    = abi === 'noParry' ? - 420 : Math . ceil (( dex + ( Object . keys ( correspondingTable ). includes ( abi ) ? correspondingTable [ abi ] : Number ( abi ))    ) / 2 ) + Number ( def ) + addedDef ;                 const weapParrySpe = abi === 'noParry' ? - 420 : Math . ceil (( dex + ( Object . keys ( correspondingTable ). includes ( abi ) ? correspondingTable [ abi ] : Number ( abi )) + 1 ) / 2 ) + Number ( def ) + addedDef ;                 verifyAndSetIfDifferent ( values , finalObj , `repeating_weapon_ ${ id } _repweaponparry` , weapParry );                 verifyAndSetIfDifferent ( values , finalObj , `repeating_weapon_ ${ id } _repweaponparryspe` , weapParrySpe );             }             if ( debug >= 2 ) TAS . debug ( 'ParryUpdater:updateObj:: UPDATE WEAPONS PARRY' , finalObj );             return finalObj ;         }         async getUpdatedObj ( toBeUpdated = {}) {             const values = await getAttrsAsync ( await this . getAttrToBeRetrieved ());             return this . updateObj ( values , toBeUpdated );         }         static async getUpdatedParryObj ( toBeUpdated = {}) {             return await new ParryUpdater (). getUpdatedObj ( toBeUpdated );         }     } I also tried to remove the fieldset in the html, reflects in roll20 visually (no weapons anymore) but no change in script "lag"
1722372168

Edited 1722372311
same debug as previously without the fieldset in html edit: sorry for the multi posts but, the forum crashed 3 times while i was posting the above message so i split it
1722372258

Edited 1722373064
Also, this "lag" appeared at some time in the 2 last monthes i'd say, i didn't update the sheet in this time and when my players told me this i investigated recently to understand why/how ...
1722400560
GiGs
Pro
Sheet Author
API Scripter
I might have to bow out of this one.I dont use TAS, and it isn't possibl;e to understand what's going on there without also examining TAS. Can you recreate what you are doing without that? Also this line raises an eyebrow: async function updateParry(e) { Roll20 character sheets don't support async (except in startRoll custom roll parsing), so Im wondering why that is there.
1722403570

Edited 1722403936
i'll remove all i can later after sleep and post here but, TAS hasn't evolved and the problem wasn't there previously, so i don't know about that about the async, its my own code so that i can use promises and streamline using asynchronous functions, i use this bit of code ( <a href="https://github.com/onyxring/orcsAsync" rel="nofollow">https://github.com/onyxring/orcsAsync</a> ) in the sheetworker to make roll20 functions async, so i'm tagging my own functions too to use the await keyword what makes you think sheets don't suppor async ? i've used this syntax for monthes now since i found the github i linked you :o
1722404631

Edited 1722404827
GiGs
Pro
Sheet Author
API Scripter
Groch said: ... what makes you think sheets don't suppor async ? i've used this syntax for monthes now since i found the github i linked you :o This is the whole reason the onyxring code was created: to allow for async and await in character sheets, where it's not supported. I can't remember where it's specifically stated, but it's been known for a long time, and the roll20 devs themselves made a big deal of startRoll allowing for async code as a deviation from the way it's not normally possible, so you'll probably find it mentioned in the initial Custom Roll Parsing release notification. All of this is moot though, since you said that you are using onyxring. That does enable async and await, so your sheet will enable it. (That said, it means even more of your code is missing!) I would try reproducing what you are doing there without any async code, then without TAS, and see if this clears up the lag issue. At least as a proof of concept, to see where issues might lie.
i've tried to remove most and lag is gone, i'm digging my way back to see when this happen but it's weird X_x&nbsp; (well, i'll do after sleep lol now that i tried removing almost everything)
Ok i've moved some little part of the code to be done later, which is not directly related to the section that had lag, and now the long delay is on another getSectionId ... i'll keep digging but searching a quantic issue is not simple x)
1722534989

Edited 1722546969
GiGs
Pro
Sheet Author
API Scripter
can you remove all GetSectionIds, and then add them back one at a time without the async code?
1722651867

Edited 1722653874
ok, it looks like it's because of the&nbsp; setSectionOrder i do at some point which make all the sheet refresh while code is happening too ... i'll disable the sorting of health bars for now but i'll have to find a way weird edit: even when i do the function at the end, after the setAttrs, i assumed it was that but well ... removing 5-25s lag per click feels good, but it's tedious to look for new bugs appearing from time to time T_T
1722656981

Edited 1722656993
GiGs
Pro
Sheet Author
API Scripter
Bug-hunting and cleaning up a sheet is very tedious! You have a potentially 25 second lag? yikes. Causing the entire sheet to refresh means every sheet worker must run again, and if there any cascading effects (change events being triiggered by setAttrs), you might have some multiple of them. Having multiple getAttrs and SetAttrs running is what causes the lag. Every one of them must contact the roll20 servers and wait for a response. You can perform multiple attribute changes in the same worker. So, if you can, you should probably have a special worker that runs instead of your sheet refresh: this worker then grabs ALL of the attributes you use in ALL of the calculations (thats just one getAttrs to get all stats), then do all of the calculations, and have a single setAttrs to set all the updated attributes, with a {silent:true} parameter, which means it wont trigger any new attribute changes. You'll have to careful that any functions this master-worker runs do not themselves contain getAttrs or setAttrs functions. This is a lot of work, and requires special handling (you need to get the ordering off all the calculations correct, and make sure you are using the updated values of the attributes), but makes the code run blisteringly fast.
1722657122
GiGs
Pro
Sheet Author
API Scripter
Groch said: edit: even when i do the function at the end, after the setAttrs, i assumed it was that but well Doing that just makes the function order properly, but if that function includes another getAttrs and/or setAttrs, those actions are still performed and its those that cuse the lag. Putting the function at the end, after the setAttrs, is a way to manage ordering of functions, but does not help lag at all. That's not what its for.
GiGs said: Groch said: edit: even when i do the function at the end, after the setAttrs, i assumed it was that but well Doing that just makes the function order properly, but if that function includes another getAttrs and/or setAttrs, those actions are still performed and its those that cuse the lag. Putting the function at the end, after the setAttrs, is a way to manage ordering of functions, but does not help lag at all. That's not what its for. I know it should not reduce lag, lag wasn't there since start of sheet developpement, i'm testing things to see what improves this quantic bug
GiGs said: Bug-hunting and cleaning up a sheet is very tedious! You have a potentially 25 second lag? yikes. Yeah; each time i had a player checking the health box corresponding to a wound penalty; even when there is no setAttr to be done after verifying all is already in place/same value
GiGs said: You can perform multiple attribute changes in the same worker. So, if you can, you should probably have a special worker that runs instead of your sheet refresh: this worker then grabs ALL of the attributes you use in ALL of the calculations (thats just one getAttrs to get all stats), then do all of the calculations, and have a single setAttrs to set all the updated attributes, with a {silent:true} parameter, which means it wont trigger any new attribute changes. Last "big" sheet update i did was this kind of rework, there is still some getAttrs but i'll merge them into only one, working on it, and there is only 1 setAttrs, but before i had this setSectionOrder lag, everything was kind of quick script wise :/
1722721688

Edited 1722721735
GiGs
Pro
Sheet Author
API Scripter
Groch said: Last "big" sheet update i did was this kind of rework, there is still some getAttrs but i'll merge them into only one, working on it, and there is only 1 setAttrs, but before i had this setSectionOrder lag, everything was kind of quick script wise :/ I have been operating on the assumption that there are a lot of callback functions (getAttrs, setAttrs, and yes getSectionIDs) that you may not be considering. If that is NOT going on, there is a bug in your sheet and only the roll20 devs can help you. I havent mentioned cuttiing down getSectionIDs because usually you need to do at least one per repeating section that crops up in the fucntion, and usually that's onle one. But if you are doing a single master function to cut down getAttrs and setAttrs, you might be accessing multiple sections, so you should also take this into account with getSectionIDs and make sure each section is called only once. I have a worker function that can be used for that, here: <a href="https://cybersphere.me/multiple-repeating-sections-custom-function/" rel="nofollow">https://cybersphere.me/multiple-repeating-sections-custom-function/</a> and a follow-up article to make sure the sections are ordered: <a href="https://cybersphere.me/ordering-repeating-sections/" rel="nofollow">https://cybersphere.me/ordering-repeating-sections/</a> Maybe they will help.
1722731275

Edited 1722731592
GiGs said: I havent mentioned cuttiing down getSectionIDs because usually you need to do at least one per repeating section that crops up in the fucntion, and usually that's onle one. But if you are doing a single master function to cut down getAttrs and setAttrs, you might be accessing multiple sections, so you should also take this into account with getSectionIDs and make sure each section is called only once. Yeah i'll work toward that it's my next goal, but when it's done and i have only 1 getAttrs, 1 getSectionIDs per section and the setAttrs, if i still have the bug on setSectionOrder, where should i post about that ? in the "pro" forum ? Actually, not on sheet opening but when wound penalty change (on ticking health), i think i have like 2 getAttrs, not much more, 4 or 5 getSectionIDs (but no double of the same section), and only 1 setAttrs that is done only if there is modified data to set
GiGs said: I have a worker function that can be used for that, here: <a href="https://cybersphere.me/multiple-repeating-sections-custom-function/" rel="nofollow">https://cybersphere.me/multiple-repeating-sections-custom-function/</a> and a follow-up article to make sure the sections are ordered: <a href="https://cybersphere.me/ordering-repeating-sections/" rel="nofollow">https://cybersphere.me/ordering-repeating-sections/</a> Maybe they will help. Oh, interesting ! thanks again for all the help
1722738729

Edited 1722738899
GiGs
Pro
Sheet Author
API Scripter
Groch said: Yeah i'll work toward that it's my next goal, but when it's done and i have only 1 getAttrs, 1 getSectionIDs per section and the setAttrs, if i still have the bug on setSectionOrder, where should i post about that ? in the "pro" forum ? Actually, not on sheet opening but when wound penalty change (on ticking health), i think i have like 2 getAttrs, not much more, 4 or 5 getSectionIDs (but no double of the same section), and only 1 setAttrs that is done only if there is modified data to set There's a bugs forum specifically for this kind of thing, but you can also contact them directly through their help portal. I'd do both. In your position, I'd be looking very closely at that health button and trying to work out every worker it can trigger (that's often not easy with cascading effects), and see if there's an easy way to streamline things. Because if you are getting multi-second lag (assuming there's no bug), something must be going on with those functions that you aren't seeing. There are other things that can cause lag (like a very heavy HTML sheet), but if it's related specifically to a change in an attribute (like health), this must be what's causing it.