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

Change to SetWithWorker ?

March 23 (1 week ago)
Jiboux
Pro
Sheet Author
Compendium Curator

Has there been any recent change of the SetWithWorker function ?

My understanding up to now was :

  • If you used obj.set(value), it would write the value, but not trigger any on(change:objname) event on sheetworker side
  • If you used obj.setWithWorker(valye), it would write the value, and trigger the above event with sourceType:"api". Should there be any log( "event triggered" +JSON.stringify(eventInfo)), this would show in the API console rather than on the VTT console.

I am currently trying to chase down a rather elusive bug, where I suspect a sheetworker function is triggered twice concurrently and writes inconsistent values... This only happens when the triggering attribute modification is done via the API...
I had the surprise to see that when the API was changing this attribute, I had 2 separate logs of the value being changed, one in the API console with sourceType:"API", ( as expected ) but also one in the client side console with sourceType:sheetworker ( which is completely unexpected )...
Also to make things clearer : I have checked if any other sheetworker function could be modifiying the same attribute, and there is nowhere in my code where this attribute is written by the sheetworker...
I also tried with a different attribute and a different function, and could see the same behaviour replicated ( i.e. double triggering, once API, once sheetworker )...
Has something changed ? Was my understanding wrong all the time ?



 


March 23 (1 week ago)
The Aaron
Roll20 Production Team
API Scripter

Nothing has changed in the API function. My guess is that the sheet in the VTT is also picking up the change (probably incorrectly). 

March 23 (1 week ago)
Jiboux
Pro
Sheet Author
Compendium Curator

Thanks,
But canonically how is it supposed to work ? I have tested with another attribute and sheetworker on my code and the same happened.

My understanding is that if you have in the API

let obj=findObjs({ _type: 'attribute', _characterid: cID, name: "test" })
obj.setWithWorker("current", i );

and in the sheetworker

on("change:test", function ocTest( eventInfo) {
log("attribute tests changed " + JSON.stringify(eventInfo)
});

Whenever the API function is triggered there should be one and only one instance of ocTest running, and I should have only in the API console a log

attribute tests changed {sourceAttribute:"test", sourceType:"api" ...}

Is my understanding correct ?


March 23 (1 week ago)
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator

I cannot replicate this with a basic setup. Here's the setup I was using to test:

API Script

on('chat:message',(msg) => {
  if(msg.content !== '!sheet' || !msg.selected?.[0]) return;
  const selectObj = msg.selected[0];
  const token = getObj(selectObj._type,selectObj._id);
  const attribute = findObjs({type:'attribute',characterid:token.get('represents'),name:'test'})[0];
  attribute.setWithWorker({current:`${attribute.get('current')} + new Value`});
})

Sheet

<input type="text" name="attr_test">
<script type="text/worker">
  on('change:test',(event) => {
    console.log('event');
    console.log(JSON.stringify(event));
  })
</script>

Based on this test, I'd guess that there is some duplication of the setting somewhere in your code.

March 23 (1 week ago)
Jiboux
Pro
Sheet Author
Compendium Curator

Thanks for testing... So what you're telling is that canonically I should NOT have the 2 logs, I should have only the one on the API...
As said, very strange, because I have tested with 2 different functions in my hseetworker triggered by 2 different attributes, and both are attributes where I don't have any instance of the sheetworker setting the value... 
I'll keep digging


Scott C. said:

I cannot replicate this with a basic setup. Here's the setup I was using to test:

API Script

on('chat:message',(msg) => {
  if(msg.content !== '!sheet' || !msg.selected?.[0]) return;
  const selectObj = msg.selected[0];
  const token = getObj(selectObj._type,selectObj._id);
  const attribute = findObjs({type:'attribute',characterid:token.get('represents'),name:'test'})[0];
  attribute.setWithWorker({current:`${attribute.get('current')} + new Value`});
})

Sheet

<input type="text" name="attr_test">
<script type="text/worker">
  on('change:test',(event) => {
    console.log('event');
    console.log(JSON.stringify(event));
  })
</script>

Based on this test, I'd guess that there is some duplication of the setting somewhere in your code.




March 23 (1 week ago)

Edited March 23 (1 week ago)
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator

Are you saying that you are getting a log in the API and a log in the browser console when you use the API to cause the change? Or two logs in the API log window?

March 23 (1 week ago)
Jiboux
Pro
Sheet Author
Compendium Curator

I am getting a log in the API and one in the browser console.
I can even see in which order they trigger. The sequence is the following:
1- I use an API function that changes the values of the attribute "wounds"
2-This triggers a function called UpdateGlobalModifiers in the sheet worker that has the on(change:wounds). This instance is with an eventInfo sourceType:"api" and has the sourceAttribute: "Wounds" ( note the cap) in the API console
3-This instance is making some getAttrs of some Attributes, and some setAttrs of some Attributes that log
4- A second instance of UpdateGlobalModifiers is triggered, this time with a sourcetype:"sheetworker", and a sourceAttribute: "wounds" in the browser console

5-the logs of this second instance in the browser demonstrate that the values received by the getAttrs have already been modified by the setAttrs of the first instance.

Needless to say : "Wounds" is not part of the values thathave been written ( which would be a pretty obvious reason why it triggered a second time )


March 23 (1 week ago)
Jiboux
Pro
Sheet Author
Compendium Curator

For Better understanding here are the logs from the API Console

"EarthDawn : UpdateAdjustGlobalModifiers(){\"sourceAttribute\":\"Wounds\",\"sourceType\":\"api\",\"triggerName\":\"wounds\",\"previousValue\":1,\"newValue\":2}"
"UpdateAdjustGlobalModifiers inputs {\"condition-Darkness\":\"0\",\"effectIsAction\":\"0\",\"Wounds\":2,\"Creature-Fury\":\"0\",\"Total-ResistPain\":1,\"combatOption-TailAttack\":\"0\",\"condition-Harried\":\"0\",\"condition-Cover\":\"0\",\"condition-Surprised\":\"0\",\"combatOption-CalledShot\":\"0\",\"condition-RangeLong\":\"0\",\"condition-Blindsided\":0,\"condition-KnockedDown\":\"0\",\"combatOption-DefensiveStance\":\"0\",\"Misc-DefStance-Penalty\":\"-3\",\"Misc-DefStance-Bonus\":\"3\",\"combatOption-AggressiveAttack\":\"0\",\"Misc-AggStance-Bonus\":\"3\",\"Misc-AggStance-Penalty\":\"-3\",\"Creature-Ambushing_max\":\"0\",\"Creature-Ambushing\":\"0\",\"Creature-DivingCharging_max\":\"0\",\"Creature-DivingCharging\":\"0\",\"Adjust-Effect-Tests-Auto\":0,\"Adjust-Effect-Tests-Misc\":\"0\",\"Adjust-Effect-Tests-Total\":0,\"Adjust-All-Tests-Auto\":0,\"Adjust-All-Tests-Misc\":\"\",\"Adjust-All-Tests-Total\":0,\"Adjust-Attacks-Auto\":0,\"Adjust-Attacks-Misc\":\"\",\"Adjust-Attacks-Total\":0,\"Adjust-Attacks-Auto-CC\":0,\"Adjust-Attacks-Total-CC\":0,\"Adjust-Damage-Auto\":0,\"Adjust-Damage-Misc\":\"\",\"Adjust-Damage-Total\":0,\"Adjust-Damage-Auto-CC\":0,\"Adjust-Damage-Total-CC\":0,\"Adjust-Defenses-Auto\":0,\"Adjust-Defenses-Misc\":\"\",\"Adjust-Defenses-Total\":0}"
"UpdateAdjustGlobalModifiers outputs {\"Adjust-All-Tests-Auto\":-1,\"Adjust-All-Tests-Total\":-1,\"Adjust-Effect-Tests-Auto\":-1,\"Adjust-Effect-Tests-Total\":-1,\"Adjust-Attacks-Auto\":-1,\"Adjust-Attacks-Total\":-1,\"Adjust-Attacks-Auto-CC\":-1,\"Adjust-Attacks-Total-CC\":-1,\"Adjust-Damage-Auto\":-1,\"Adjust-Damage-Total\":-1,\"Adjust-Damage-Auto-CC\":-1,\"Adjust-Damage-Total-CC\":-1}"

You can see for example that Adjust-All-Tests-Auto result from the GetAttrs is 0, but the setAttrs is -1
And here are the logs of the browser console

EarthDawn : UpdateAdjustGlobalModifiers(){"sourceAttribute":"wounds","sourceType":"sheetworker","triggerName":"wounds","previousValue":1,"newValue":2}
(...) sheetsandboxworker.js:155 UpdateAdjustGlobalModifiers inputs {"condition-Darkness":"0","effectIsAction":"0","Wounds":2,"Creature-Fury":"0","Total-ResistPain":1,"combatOption-TailAttack":"0","condition-Harried":"0","condition-Cover":"0","condition-Surprised":"0","combatOption-CalledShot":"0","condition-RangeLong":"0","condition-Blindsided":0,"condition-KnockedDown":"0","combatOption-DefensiveStance":"0","Misc-DefStance-Penalty":"-3","Misc-DefStance-Bonus":"3","combatOption-AggressiveAttack":"0","Misc-AggStance-Bonus":"3","Misc-AggStance-Penalty":"-3","Creature-Ambushing_max":"0","Creature-Ambushing":"0","Creature-DivingCharging_max":"0","Creature-DivingCharging":"0","Adjust-Effect-Tests-Auto":-1,"Adjust-Effect-Tests-Misc":"0","Adjust-Effect-Tests-Total":-1,"Adjust-All-Tests-Auto":-1,"Adjust-All-Tests-Misc":"0","Adjust-All-Tests-Total":-1,"Adjust-Attacks-Auto":-1,"Adjust-Attacks-Misc":"0","Adjust-Attacks-Total":-1,"Adjust-Attacks-Auto-CC":-1,"Adjust-Attacks-Total-CC":-1,"Adjust-Damage-Auto":-1,"Adjust-Damage-Misc":"0","Adjust-Damage-Total":-1,"Adjust-Damage-Auto-CC":-1,"Adjust-Damage-Total-CC":-1,"Adjust-Defenses-Auto":0,"Adjust-Defenses-Misc":"0","Adjust-Defenses-Total":0}
sheetsandboxworker.js:155 UpdateAdjustGlobalModifiers outputs {}

Note that Adjust-All-Tests-Auto is now -1 in the inputs proving that the other instance has already run.
Also, I cleaned the browser log, here are the messages in the (...). I have no idea if they are related or how to interprete them

jquery-1.9.1.js:2257 The specified value "None" cannot be parsed, or is out of range.
(anonymous) @ jquery-1.9.1.js:2257
each @ jquery-1.9.1.js:648
each @ jquery-1.9.1.js:270
val @ jquery-1.9.1.js:2228
(anonymous) @ vtt.bundle.1f325618302065e3a9dc.js:51361
L.each.L.forEach @ vtt.bundle.1f325618302065e3a9dc.js:4008
baseUpdateSheetValues @ vtt.bundle.1f325618302065e3a9dc.js:51361
Pe @ vtt.bundle.1f325618302065e3a9dc.js:4008
setTimeout
(anonymous) @ vtt.bundle.1f325618302065e3a9dc.js:4008
updateSheetValues @ vtt.bundle.1f325618302065e3a9dc.js:51356
(anonymous) @ vtt.bundle.1f325618302065e3a9dc.js:51356
trigger @ vtt.bundle.1f325618302065e3a9dc.js:3967
_onModelEvent @ vtt.bundle.1f325618302065e3a9dc.js:3967
trigger @ vtt.bundle.1f325618302065e3a9dc.js:3967
change @ vtt.bundle.1f325618302065e3a9dc.js:3967
set @ vtt.bundle.1f325618302065e3a9dc.js:3967
child_changed @ vtt.bundle.1f325618302065e3a9dc.js:4009
ee @ vtt.bundle.1f325618302065e3a9dc.js:2389
onValue @ vtt.bundle.1f325618302065e3a9dc.js:3502
(anonymous) @ vtt.bundle.1f325618302065e3a9dc.js:3532
Ie @ vtt.bundle.1f325618302065e3a9dc.js:2494
EA @ vtt.bundle.1f325618302065e3a9dc.js:3427
tv @ vtt.bundle.1f325618302065e3a9dc.js:3427
w0 @ vtt.bundle.1f325618302065e3a9dc.js:3427
xC @ vtt.bundle.1f325618302065e3a9dc.js:3442
(anonymous) @ vtt.bundle.1f325618302065e3a9dc.js:3442
onDataPush_ @ vtt.bundle.1f325618302065e3a9dc.js:2749
onDataMessage_ @ vtt.bundle.1f325618302065e3a9dc.js:2749
onDataMessage_ @ vtt.bundle.1f325618302065e3a9dc.js:2659
onPrimaryMessageReceived_ @ vtt.bundle.1f325618302065e3a9dc.js:2659
(anonymous) @ vtt.bundle.1f325618302065e3a9dc.js:2659
appendFrame_ @ vtt.bundle.1f325618302065e3a9dc.js:2629
handleIncomingFrame @ vtt.bundle.1f325618302065e3a9dc.js:2629
mySock.onmessage @ vtt.bundle.1f325618302065e3a9dc.js:2629Understand this warningAI
jquery-1.9.1.js:2257 The specified value "Ask: PD: ?{Target Number, maybe relative to PD|0}" cannot be parsed, or is out of range.
(anonymous) @ jquery-1.9.1.js:2257
each @ jquery-1.9.1.js:648
each @ jquery-1.9.1.js:270
val @ jquery-1.9.1.js:2228
(anonymous) @ vtt.bundle.1f325618302065e3a9dc.js:51361
L.each.L.forEach @ vtt.bundle.1f325618302065e3a9dc.js:4008
baseUpdateSheetValues @ vtt.bundle.1f325618302065e3a9dc.js:51361
Pe @ vtt.bundle.1f325618302065e3a9dc.js:4008
setTimeout
(anonymous) @ vtt.bundle.1f325618302065e3a9dc.js:4008
updateSheetValues @ vtt.bundle.1f325618302065e3a9dc.js:51356
(anonymous) @ vtt.bundle.1f325618302065e3a9dc.js:51356
trigger @ vtt.bundle.1f325618302065e3a9dc.js:3967
_onModelEvent @ vtt.bundle.1f325618302065e3a9dc.js:3967
trigger @ vtt.bundle.1f325618302065e3a9dc.js:3967
change @ vtt.bundle.1f325618302065e3a9dc.js:3967
set @ vtt.bundle.1f325618302065e3a9dc.js:3967
child_changed @ vtt.bundle.1f325618302065e3a9dc.js:4009
ee @ vtt.bundle.1f325618302065e3a9dc.js:2389
onValue @ vtt.bundle.1f325618302065e3a9dc.js:3502
(anonymous) @ vtt.bundle.1f325618302065e3a9dc.js:3532
Ie @ vtt.bundle.1f325618302065e3a9dc.js:2494
EA @ vtt.bundle.1f325618302065e3a9dc.js:3427
tv @ vtt.bundle.1f325618302065e3a9dc.js:3427
w0 @ vtt.bundle.1f325618302065e3a9dc.js:3427
xC @ vtt.bundle.1f325618302065e3a9dc.js:3442
(anonymous) @ vtt.bundle.1f325618302065e3a9dc.js:3442
onDataPush_ @ vtt.bundle.1f325618302065e3a9dc.js:2749
onDataMessage_ @ vtt.bundle.1f325618302065e3a9dc.js:2749
onDataMessage_ @ vtt.bundle.1f325618302065e3a9dc.js:2659
onPrimaryMessageReceived_ @ vtt.bundle.1f325618302065e3a9dc.js:2659
(anonymous) @ vtt.bundle.1f325618302065e3a9dc.js:2659
appendFrame_ @ vtt.bundle.1f325618302065e3a9dc.js:2629
handleIncomingFrame @ vtt.bundle.1f325618302065e3a9dc.js:2629
mySock.onmessage @ vtt.bundle.1f325618302065e3a9dc.js:2629Understand this warningAI
jquery-1.9.1.js:2257 The specified value "None" cannot be parsed, or is out of range.
(anonymous) @ jquery-1.9.1.js:2257
each @ jquery-1.9.1.js:648
each @ jquery-1.9.1.js:270
val @ jquery-1.9.1.js:2228
(anonymous) @ vtt.bundle.1f325618302065e3a9dc.js:51361
L.each.L.forEach @ vtt.bundle.1f325618302065e3a9dc.js:4008
baseUpdateSheetValues @ vtt.bundle.1f325618302065e3a9dc.js:51361
Pe @ vtt.bundle.1f325618302065e3a9dc.js:4008
setTimeout
(anonymous) @ vtt.bundle.1f325618302065e3a9dc.js:4008
updateSheetValues @ vtt.bundle.1f325618302065e3a9dc.js:51356
(anonymous) @ vtt.bundle.1f325618302065e3a9dc.js:51356
trigger @ vtt.bundle.1f325618302065e3a9dc.js:3967
_onModelEvent @ vtt.bundle.1f325618302065e3a9dc.js:3967
trigger @ vtt.bundle.1f325618302065e3a9dc.js:3967
change @ vtt.bundle.1f325618302065e3a9dc.js:3967
set @ vtt.bundle.1f325618302065e3a9dc.js:3967
child_changed @ vtt.bundle.1f325618302065e3a9dc.js:4009
ee @ vtt.bundle.1f325618302065e3a9dc.js:2389
onValue @ vtt.bundle.1f325618302065e3a9dc.js:3502
(anonymous) @ vtt.bundle.1f325618302065e3a9dc.js:3532
Ie @ vtt.bundle.1f325618302065e3a9dc.js:2494
EA @ vtt.bundle.1f325618302065e3a9dc.js:3427
tv @ vtt.bundle.1f325618302065e3a9dc.js:3427
w0 @ vtt.bundle.1f325618302065e3a9dc.js:3427
xC @ vtt.bundle.1f325618302065e3a9dc.js:3442
(anonymous) @ vtt.bundle.1f325618302065e3a9dc.js:3442
onDataPush_ @ vtt.bundle.1f325618302065e3a9dc.js:2749
onDataMessage_ @ vtt.bundle.1f325618302065e3a9dc.js:2749
onDataMessage_ @ vtt.bundle.1f325618302065e3a9dc.js:2659
onPrimaryMessageReceived_ @ vtt.bundle.1f325618302065e3a9dc.js:2659
(anonymous) @ vtt.bundle.1f325618302065e3a9dc.js:2659
appendFrame_ @ vtt.bundle.1f325618302065e3a9dc.js:2629
handleIncomingFrame @ vtt.bundle.1f325618302065e3a9dc.js:2629
mySock.onmessage @ vtt.bundle.1f325618302065e3a9dc.js:2629Understand this warningAI




March 23 (1 week ago)
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator

ok, then something else is going on. This should not be possible as the API is running on the server (and all it's changes to the sheet occur on the server). The browser console log can only show things that happen on your actual client, and so doesn't have access to what is done on the API.

I'd recommend testing with your browser in incognito mode (or equivalent of your browser) in order to eliminate all browser extensions as a possible cause.

March 23 (1 week ago)
Jiboux
Pro
Sheet Author
Compendium Curator

OK, to eliminate the possibility of a browser extension, I went directly to another browser ( Chrome and Safari ) and both have the same behaviour...
Also what I really don't get is that even when an API setWithWorker triggers a sheetworker function, that itself triggers another, my understanding is that it all happens on the server side... So there should not be any cascading event from an API setWithWorker to a sheetworker logged in the browser console.



March 23 (1 week ago)
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator

Jiboux said:

Also what I really don't get is that even when an API setWithWorker triggers a sheetworker function, that itself triggers another, my understanding is that it all happens on the server side... So there should not be any cascading event from an API setWithWorker to a sheetworker logged in the browser console.

Correct, that is what I was saying. The behavior you are seeing should not be possible from a technical point of view. There's no communication path for that data to be communicated between the two environments. There has to be something else happening.

March 23 (1 week ago)
Jiboux
Pro
Sheet Author
Compendium Curator

Correct, that is what I was saying. The behavior you are seeing should not be possible from a technical point of view. There's no communication path for that data to be communicated between the two environments. There has to be something else happening.

I have continued to make experiments, by cutting some of my sheetworker code out and see if the behaviour was still happening... And I could cut to a minimum code where there is more or less only the function I was datalogging, and when I got to this minimal code, the issue of double logging n browser and API disappeared... Until it appeared again...
So I continued loading the code, seeing that I didn't have a double logging, until I got one again...

And now, I would more or less narrow : I have a minimal code that doesn't trigger the double loading if the first thing I do after loading the HTML ( and restarting the API Sandbox) is doing the action that would trigger the double logging...
But if I make any other action in the meantime ( like changing another attribute ), I have the double logging again...

Which leads me to doubt the "There's no communication path for that data to be communicated between the two environments." There is one indeed, because when an Attribute is changed at the API level, your browser must reflect the change. To my understanding this is what the Firebase is, a database of all the attributes and properties that gets replicated between the server and your own client in the browser...

And the above leads me to something I have already been fighting in the past : It seems that the first time you load a sheet ( whether it is because you reloaded the HTML in the Sandbox, or because it is a brand new character which sheet has never ever been closed and reopened ), the Firebase doesn't start immediately ( I had another bug where I could actually track this behaviour ) 
https://app.roll20.net/forum/post/11129192/slug%7D

So this may be linked ?






March 23 (1 week ago)
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator

Yes, there is a path for the data to be shared, but the event firing is controlled by whether a given client was the cause of the event or not. Otherwise, sheetworkers would fire in every client and it'd be impossible to do calculations or have any sort of sensical attribute handling.

I can't replicate this using my minimum recreation of the scenario you have described. Can you share your minimum reproduction code? 

March 23 (1 week ago)
Jiboux
Pro
Sheet Author
Compendium Curator

You could download the code here... Still not completely minimal, but I already removed 90% of the sheetworkers

https://drive.google.com/open?id=1DlhMmAPGhcTVZ0Z9RZ74k-zZNLMjOB7d&usp=drive_fs

The configuration to fire it is a game with the Earthdawn by FASA companion App and this sheet.

On any new character, you an apply Damage by using the upper left button... Select 10 Damage, and this will result in a Wound...

So with this minimum code if you
1- Load the code, then Apply the Damage, you would have only the datalog in the API console as is supposed
2-Load the Code, then change any value, then Apply the Damage, then it would trigger on both the Browser and API console


 

March 23 (1 week ago)
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator

Ok, for it to be a minimum reproduction, the API needs to be cut down to. What is the minimum API code required to replicate this?

March 24 (1 week ago)
Jiboux
Pro
Sheet Author
Compendium Curator

I have been trying to light a bit the API code, but from the 13000 lines of complex architecture, it is very difficult to reduce to a minimal code


March 24 (1 week ago)
Chris D.
Pro
Sheet Author
API Scripter
Compendium Curator

I tried to create some minimalist code to demonstrate the problems that Jiboux has been reporting, but could not actually replicate what he was seeing, but my code did perfectly demonstrate three other problems (one of which was mentioned in this thread), so I started a new thread for those problems.

Two problems with API createObj. API not triggering on first create, and character sheet not updating.

https://app.roll20.net/forum/post/12278369/two-problems-with-api-createobj-api-not-triggering-on-first-create-and-character-sheet-not-updating/?pageforid=12278369#post-12278369

Anyway, Jiboux, you might want use my code as a minimalist base to see if you can demonstrate it with truly minimalist code. 

The only other comment I have on this thread is that in my demo on the other thread, the changes are coming out as source "player" on the eventInfo, where you are seeing source "sheetworker", which might be significant.

March 24 (1 week ago)
Jiboux
Pro
Sheet Author
Compendium Curator

OK, I have been using the minimal code provided by Chris D, and I can confirm that I could see the abnormal behaviour I was referring to.

Follow Chris D's link for reference of the minimal code, but here is a summary of how it works:

3 attributes i1, i2, i3.

on( change:i1 change i2 change i3) sheetworker that logs any change of value of i1, i2 or i3.

on("change:attribute") API function that logs any change to any attribute, but also if there is a change of value of i2, will copy its value to i3.

A button on the sheet sends a command to the API to change the value of i2.

I will not add to the bugs reported by Chris on the other thread, but come back to my bug about double datalogging/double triggering

How it should behave

If a change of i2 is done manually, it should result in:

  1. Change of i2 ( from the sheetworker event) logged in browser console
  2. Change i2 ( from the API event ) logged in API console
  3. API changes the i3 value to match i2
  4. Change i3 ( from the sheetworker event, but triggered by the API setwithworker ) logged in the API console
  5. Change i3 (from the API event ) also logged in the API console

If a change of i2 is done via the API button it should result in:

  1. Change of i2 ( from the sheetworker event, triggered by the API setwithworker) logged in API console
  2. Change i2 ( from the API event ) logged in API console
  3. API changes the i3 value to match i2
  4. Change i3 ( from the sheetworker event, but triggered by the API setwithworker ) logged in the API console
  5. Change i3 (from the API event ) also logged in the API console

How it behaves Sequence 1

  1. Create a Brand new character
  2. Change 1 : Change the i2 value manually to 1.
  3. As already reported by Chris, on this first change, only i2 changes and this change is logged in the browser console. API event doesn't fire
Browser console
Sheetworker i2 triggered
{"sourceAttribute":"i2","sourceType":"player","triggerName":"i2","newValue":"1"}
  1. Change 2: change  i2 a second time manually to 2
  2. As reported by Chris, this is mostly as planned, except that the sheetworker event for i3 in the API console is triggered twice ( for the CreateObj and for the setwithworker) - Also the log says value of i3 has been set to 20 then to 2... but it doesn't display 
Browser Console
Sheetworker i2 triggered
{"sourceAttribute":"i2","sourceType":"player","triggerName":"i2","previousValue":"1","newValue":"2"}
API Console
{"name":"i2","current":"1","max":"","_id":"-OM951m8IHZ5JWg2-Zfz","_type":"attribute","_characterid":"-OM94yNaD_7ivpVafC_c"}
{"name":"i2","current":"2","max":"","_id":"-OM951m8IHZ5JWg2-Zfz","_type":"attribute","_characterid":"-OM94yNaD_7ivpVafC_c"}
"API i2 triggered"
"Sheetworker i3 triggered"
"{\"sourceAttribute\":\"i3\",\"sourceType\":\"api\",\"triggerName\":\"i3\",\"newValue\":20}"
"Sheetworker i3 triggered"
"{\"sourceAttribute\":\"i3\",\"sourceType\":\"api\",\"triggerName\":\"i3\",\"previousValue\":20,\"newValue\":\"2\"}"

  1. Change 3 : Click manually i3 to 3
  2. Note that the Browser console has no PreviousValue, and the API Console doesn't even trigger
Browser Console
Sheetworker i3 triggered
{"sourceAttribute":"i3","sourceType":"player","triggerName":"i3","newValue":"3"}

  1. Change 4 : Change Manually i3 to 4
  2. This time there is a previousvalue in the browser console, and the API Console triggers correctly
Browser Console
Sheetworker i3 triggered
sheetsandboxworker.js:155 {"sourceAttribute":"i3","sourceType":"player","triggerName":"i3","previousValue":"3","newValue":"4"}
API Console
{"name":"i3","current":"3","max":"","_id":"-OM95uRVnOZMam1K3WgV","_type":"attribute","_characterid":"-OM94yNaD_7ivpVafC_c"}
{"name":"i3","current":"4","max":"","_id":"-OM95uRVnOZMam1K3WgV","_type":"attribute","_characterid":"-OM94yNaD_7ivpVafC_c"}
  1. Change 5 : Use the API button to change i2 to 5
  2. The change of i2 is done and triggers sheetworker in API Console, but there is not the subsequent trigger that would copy i2 in i3
API COnsole
{"content":"!demo~ -OM94yNaD_7ivpVafC_c~ 5","playerid":"-MT8xhgWaPwyKrQf-hV0","rolledByCharacterId":"-OM94yNaD_7ivpVafC_c","type":"api","who":"Jiboux (GM)"}
{"name":"i2","current":"2","max":"","_id":"-OM951m8IHZ5JWg2-Zfz","_type":"attribute","_characterid":"-OM94yNaD_7ivpVafC_c"}
"Sheetworker i2 triggered"
"{\"sourceAttribute\":\"i2\",\"sourceType\":\"api\",\"triggerName\":\"i2\",\"previousValue\":\"2\",\"newValue\":\"5\"}"

  1. Change 6 : Change manually i2 to 6
  2. Change i2 is logged in the Browser Console as it should be, and API detects the change of i2, and copies i3, as it should be... BUT BUT BUT : the sheetworker change detection of i3 is both in the Browser console (sourceType:player) and API Console (sourceType:API), where the change being done by setwithworker it should only be in the API
  3. Note also that the findObjs has found several instances of i3 when trying to copy i3, one with value 2, and one with value 4. The one with value 2 is likely the one in change 2 that was never displayed.
Browser Console
Sheetworker i2 triggered
{"sourceAttribute":"i2","sourceType":"player","triggerName":"i2","previousValue":"5","newValue":"6"}
Sheetworker i3 triggered
{"sourceAttribute":"i3","sourceType":"player","triggerName":"i3","previousValue":"2","newValue":"6"}
API Console
{"name":"i2","current":"5","max":"","_id":"-OM951m8IHZ5JWg2-Zfz","_type":"attribute","_characterid":"-OM94yNaD_7ivpVafC_c"}
{"name":"i2","current":"6","max":"","_id":"-OM951m8IHZ5JWg2-Zfz","_type":"attribute","_characterid":"-OM94yNaD_7ivpVafC_c"}
"API i2 triggered"
"2025-3-24 21:48:40 (UTC)  Error Earthdawn:findOrMakeObj() found 2 objects: "
[{"name":"i3","current":"2","max":"","_id":"-OM95MbQUy64U5ur6eiq","_type":"attribute","_characterid":"-OM94yNaD_7ivpVafC_c"},{"name":"i3","current":"4","max":"","_id":"-OM95uRVnOZMam1K3WgV","_type":"attribute","_characterid":"-OM94yNaD_7ivpVafC_c"}]
"removing attr[ 1 ] and keeping attr[ 0]."
"Sheetworker i3 triggered"
"{\"sourceAttribute\":\"i3\",\"sourceType\":\"api\",\"triggerName\":\"i3\",\"previousValue\":\"2\",\"newValue\":\"6\"}"
  1. Change i7 : Change manually i2 to 7
  2. This subsequent change seems to still have the issue of i3 reported on both consoles; even if the duplicate attribute stuff has disappeared
Browser Console
Sheetworker i2 triggered
 {"sourceAttribute":"i2","sourceType":"player","triggerName":"i2","previousValue":"6","newValue":"7"}
Sheetworker i3 triggered
 {"sourceAttribute":"i3","sourceType":"player","triggerName":"i3","previousValue":"6","newValue":"7"}
API Console
{"name":"i2","current":"6","max":"","_id":"-OM951m8IHZ5JWg2-Zfz","_type":"attribute","_characterid":"-OM94yNaD_7ivpVafC_c"}
{"name":"i2","current":"7","max":"","_id":"-OM951m8IHZ5JWg2-Zfz","_type":"attribute","_characterid":"-OM94yNaD_7ivpVafC_c"}
"API i2 triggered"
"Sheetworker i3 triggered"
"{\"sourceAttribute\":\"i3\",\"sourceType\":\"api\",\"triggerName\":\"i3\",\"previousValue\":\"6\",\"newValue\":\"7\"}"

What it shows beyond the bugs reported by Chris

  • It seems there is somehting going on with the initialization of attributes. it looks like When a character is just created, and until some attributes are actually written by the player for the first time, the synchronization of value between the API and the client side is failing.
  • This failed synchronization seems to create a lack of detection on the API side of a change on the player side
  • This failed synchronization also seems to create that a value changed on API side doesn't display on browser side
  • Finally, I can't really build the exact sequence why it is only on change 6, and on none of the others that there is this bug I initially reported of having the double datalogging, but once it is double logged, it seems subsequent changes still double log...

Hope this is useful



March 24 (1 week ago)
Jiboux
Pro
Sheet Author
Compendium Curator

Oh and a complment : Chris had sent me the minimal code, but I had not seen the part where he was saying it was not the exact same one... So here is a copy of the code used for the tests:

<!DOCTYPE html>
<meta charset="UTF-8">

<div>
	<input name="attr_i1" type="number" value="10" />
	<input name="attr_i2" type="number" value="10" />
	<input name="attr_i3" type="number" value="10" />
  <button  name="roll_Demo8" type="roll" title="Demo button, enter a number." value="!demo~ @{character_id}~ ?{any number|4}" >Demo Button</button>
</div>

<script type="text/worker">

    // when i1 or d1 change, copy the new value into i2 or d2.
  on("change:i1 change:i2 change:i3", function test1( eventInfo ) {
        'use strict';
		log( "Sheetworker " + eventInfo[ "sourceAttribute" ] + " triggered"); 
		log( JSON.stringify(eventInfo));
  });		// This is for test1

</script>
//
// Define a Name-space
var Earthdawn = Earthdawn || {};

// Demo8.   Log all changed attributes (prev then new). 
//    There is a sheetworker that changes copies all fields that end in an odd digit to the next field.
//    This .js copies the values of all even fields to the next field.
//    The object is to see which fields get copied. Anything put into field i1, should quickly make its way to i6. 

on("ready", function() {
  'use strict';
  log( "Demo8 ready");
  });

on('chat:message',(msg) => {
log(msg);
  if(msg.content.slice( 0, 5 ) !== '!demo') return;
  let ma = msg.content.split( "~" );
  if( ma.length < 3 ) {
    log( "bad demo msg " + msg.content );
    return;
  }
  let cid = ma[ 1 ].trim(),
      newnum = ma[ 2 ].trim(),
      attrib = Earthdawn.findOrMakeObj({ _type: 'attribute', _characterid: cid, name: 'i2' }, newnum);
log( attrib);
  attrib.setWithWorker({ "current": newnum });
})

on("change:attribute", function (attr, prev) {
  'use strict';
  log( prev);   // use prev["name"] and prev["current"]           // {"name":"Wounds","current":0,"max":8,"_id":"-MlqexKD2f4f744TgzlK","_type":"attribute","_characterid":"-MlqeuXlNO51-RYxmJv8"}
  log( attr);   // use attr.get("name") and attr.get("current").  // {"name":"Wounds","current":"1","max":8,"_id":"-MlqexKD2f4f744TgzlK","_type":"attribute","_characterid":"-MlqeuXlNO51-RYxmJv8"}

  let sa      = attr.get( "name" ),
      cID     = attr.get( "_characterid" ),
      current = attr.get( "current" ),
      num = parseInt( sa.slice( -1 ));

  if( num == 2 ) {
    log( "API " + sa + " triggered");
    let aobj = Earthdawn.findOrMakeObj({ _type: 'attribute', _characterid: cID, name: 'i3' }, 20);
//    log( "before API setWW current = " + JSON.stringify( aobj ));
    aobj.setWithWorker( "current", current );
//    log( "after API setWW current = " + JSON.stringify( aobj ));
//    let bobj = Earthdawn.findOrMakeObj({ _type: 'attribute', _characterid: cID, name: 'i1' }, 20);
//    bobj.set( "current", current );
  }
}); // end  on("change:attribute"



    // Look for an object. If you can't find one, make one and return that.
Earthdawn.findOrMakeObj = function ( attrs, deflt, maxDeflt ) {
  'use strict';
  try {
//log(attrs);
    let obj,
        objs = findObjs( attrs );
    if( objs ) {
      if( objs.length > 1 ) {
        Earthdawn.errorTrace( "Error Earthdawn:findOrMakeObj() found " + objs.length + " objects: " );
        log( objs );
        let keep = 0, maxscore = 0;   // pick one to keep and get rid of the rest. 
        for( let i = 0; i < objs.length; ++i ) {
          let score = 0;
          function scoreit( a, dflt ) {
          'use strict';
            if( a !== undefined && a !== null ) {
              if( typeof a != "string" ) ++score;   // Gain points for not being string, and not being equal to default, and not evaluating to false, on the assumption that something tried to change it to those. Note that these criteria are rather arbitrary, but wanted to make decision based on something other than first or last.
              if( dflt !== undefined && a != dflt ) ++score;
              if( a ) ++score;
          } }
          scoreit( objs[ i ].get( "current" ), deflt );
          scoreit( objs[ i ].get( "max" ), maxDeflt );
          if( score > maxscore ) {
            keep = i;
            maxscore = score;
        } }
        let txt = "";
        for( let i = objs.length -1; i > -1; --i )
          if( i !== keep ) {
            txt += " attr[ " + i + " ],";
            objs[ i ].remove();
          }
        obj = objs[ keep ];
        log( "removing" + txt.slice( 0, -1) + " and keeping attr[ " + keep + "]." );
      } // end found more than one.
      else if( objs.length > 0 )
        obj = objs[ 0 ];
    } // end found one.
    if( obj === undefined && "_type" in attrs ) {   // we did not find any, create one.
      let type = attrs[ "_type" ];
      delete attrs[ "_type" ];
      obj = createObj( type, attrs);
      if( obj && deflt !== undefined && deflt !== null )
        obj.setWithWorker( "current", deflt );
      if( obj && maxDeflt !== undefined && maxDeflt !== null )
        obj.setWithWorker( "max", maxDeflt );
    }
    return obj;
  } catch(err) { Earthdawn.errorTrace( "Earthdawn:findOrMakeObj() error caught: " + err ); }
}; // end findOrMakeObj()


      // Log a programming error and give trace information.
Earthdawn.errorTrace = function( msg ) {
  'use strict';
  try {
    log( Earthdawn.timeStamp() + msg);
    if( Earthdawn.traceStack || Earthdawn.traceInfo )
      log( "Possible " + (Earthdawn.traceStack ? "TraceStack: " + Earthdawn.traceStack : "") + (Earthdawn.traceInfo ? "   TraceInfo: " + Earthdawn.traceInfo: ""));
  } catch(err) { log( "Earthdawn:ErrorTrace( " + msg + " ) error caught: " + err ); }
} // end ErrorTrace()



    // return string with timestamp in it.
Earthdawn.timeStamp = function ()  {
  'use strict';
  try {
    let today = new Date();
    return today.getFullYear() + "-" + (today.getMonth() +1) + "-" + today.getDate() + " " + today.getHours() + ":" + today.getMinutes() + ":" + today.getSeconds() + " (UTC)  ";
  } catch(err) { log( "Earthdawn:timeStamp() error caught: " + err ); }
} // End timeStamp