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

Improperly Deleted Items?

So I now have an issue with a value that is persisting between sessions, even though it should not. General.GetOrCreateHandout = function(handoutName){ var handout = findObjs({ type: 'handout', name : handoutName })[0]; log(handoutName+ ':' + JSON.stringify(handout)); if(!handout){ handout = createObj('handout', { name: handoutName }); } return handout; } Initializer.Initialize = function(){ var LoadItems = []; var installedListHandout = General.GetOrCreateHandout('Installed Modules'); installedListHandout.get('notes',function(installedTable){ var installedArray = []; if(installedTable){ installedArray = General.TableToJSONArray(installedTable); } var installList = _.reject(Initializer.Installer.ToDoList,function(rejectItem){ return _.filter(installedArray,function(filterItem){ return filterItem.Name == rejectItem.Name && parseInt(filterItem.Version) == parseInt(rejectItem.Version); }).length > 0; }); log(installList); LoadItems.concat(installList); installedTable = ''; }); _.each(Initializer.ToDoList,function(pendingModule){ pendingModule.LoadOrder = parseFloat(pendingModule.LoadOrder) + parseFloat(0.1); LoadItems.push(pendingModule); }); var tableString = '<table><tbody><tr><td>Name</td><td>Version</td></tr>'; _.each(Initializer.Installer.ToDoList,function(item){ tableString += '<tr><td>' + item.Name + '</td><td>' + item.Version + '</td></tr>'; }); tableString +='</tbody></table>'; installedListHandout.set('notes',tableString); LoadItems.sort(function(a,b){ return a.LoadOrder - b.LoadOrder; }); _.each(LoadItems,function(item){ item.Execute(); }); sendChat(Initializer.Key,"The API has completed the initialization process and is ready to use."); } function KillAllTheEffingHandouts(){ _.each(findObjs({ type: 'handout' }),function(item){ item.remove(); }); } It seems no matter what I try, even if there are no handouts in the campaign, var installedListHandout = General.GetOrCreateHandout('Installed Modules'); => "{\"name\":\"Installed Modules\",\"notes\":\"\",\"gmnotes\":\"\",\"inplayerjournals\":\"\",\"archived\":false,\"controlledby\":\"\",\"_type\":\"handout\",\"_id\":\"-JpUSB0YIw7W2QlUJOZE\",\"avatar\":\"\"}" installedListHandout.get('notes',function(installedTable){ => "<table><tbody><tr><td>Name</td><td>Version</td></tr><tr><td>CurriculumVitae</td><td>1</td></tr></tbody></table>" I have no idea how this is happening. That string should not be there. It should be empty. And since it IS there, it's filtering the module out of the installer's ToDoList, preventing it from creating the necessary handouts.
1431830999
Lithl
Pro
Sheet Author
API Scripter
You say that there are no handouts in the campaign (even archived), and yet line 2 of the GetOrCreateHandout function is logging a handout?
It's creating a blank handout, since no handout with that name exists. The logging shows the notes property of the handout is an empty string.
1431832419
The Aaron
Pro
API Scripter
For me, it's showing it as a number, which I assume is the reference ID for some blob in the storage: "Installed Modules:{\"name\":\"Installed Modules\",\"notes\": 1431831926595 ,\"gmnotes\":\"\",\"inplayerjournals\":\"\",\"archived\":false,\"controlledby\":\"\",\"_type\":\"handout\",\"_id\":\"-JpUoyBwPtNoqQE5M7OJ\",\"avatar\":\"\"}" It's creating the handout fine if it doesn't exist, and listing it again on a restart.
Right, except when I delete all of the handouts, that handout retains it's value. Witch it should not. In my last code block, I'm showing that the handout created has nothing in the notes field, but the variable passed to the callback function on the next line has an entire table string in it. If that function creates a handout, which it does, and that handout has an empty notes string, which it does, how does installedTable have a value?
1431833628
The Aaron
Pro
API Scripter
So, to duplicate what you're doing, would I all KillAllTheEffingHandouts() in the script, then restart it, or go and delete the handout manually?
I deleted the handout manually. I wrote that function to make sure it was gone.
1431834667
The Aaron
Pro
API Scripter
OK, when I do that and restart the script, I get: Spinning up new sandbox... "Installed Modules:undefined" "ERROR: You cannot set the imgsrc or avatar of an object unless you use an image that is in your Roll20 Library. See the API documentation for more info." [] Is that undefined next to Installed Modules: what you're talking about?
1431834847
The Aaron
Pro
API Scripter
Oh, I bet you're talking about the log(installList) call. I added a log(installedTable) just inside the get('notes'), and it shows me the table text.
That indicates the handout had to be created by the function. The error line verifies that the handout was created, but no imgsrc was provided for the object upon creation. The only difference is that when I add a log statements after var installedListHandout = General.GetOrCreateHandout('Installed Modules'); and installedListHandout.get('notes',function(installedTable){, i get values that don't line up. The log statements verify that the handout was created, and that it possesses no value in the notes property. However, the second log statement shows installTable as having a value of "<table><tbody><tr><td>Name</td><td>Version</td></tr><tr><td>CurriculumVitae</td><td>1</td></tr></tbody></table>"
I've implemented a workaround for the time being, which is just a !cvreinstall command that re-runs the first-run setup process. But I'd like to figure out if the problem is in my code or just a roll20 bug.
1431835693
The Aaron
Pro
API Scripter
Ok. I think I understand what you're seeing and what is happening. Here's my code: General = {}; Initializer = {}; General.GetOrCreateHandout = function(handoutName){ var handout = findObjs({ type: 'handout', name : handoutName })[0]; log(handoutName+ ':' + JSON.stringify(handout)); if(!handout){ handout = createObj('handout', { name: handoutName }); } return handout; } General.TableToJSONArray = function(){}; Initializer.Installer = {}; Initializer.Key = ''; Initializer.Initialize = function(){ var LoadItems = []; var installedListHandout = General.GetOrCreateHandout('Installed Modules'); installedListHandout.get('notes',function(installedTable){ log(installedTable); // [[ 1 ]] var installedArray = []; if(installedTable){ installedArray = General.TableToJSONArray(installedTable); } var installList = _.reject(Initializer.Installer.ToDoList,function(rejectItem){ return _.filter(installedArray,function(filterItem){ return filterItem.Name == rejectItem.Name && parseInt(filterItem.Version) == parseInt(rejectItem.Version); }).length > 0; }); log(installList); LoadItems.concat(installList); installedTable = ''; }); _.each(Initializer.ToDoList,function(pendingModule){ pendingModule.LoadOrder = parseFloat(pendingModule.LoadOrder) + parseFloat(0.1); LoadItems.push(pendingModule); }); var tableString = '<table><tbody><tr><td>Name</td><td>Version</td></tr>'; _.each(Initializer.Installer.ToDoList,function(item){ tableString += '<tr><td>' + item.Name + '</td><td>' + item.Version + '</td></tr>'; }); tableString +='</tbody></table>'; log('About to set'); // [[ 2 ]] installedListHandout.set('notes',tableString); log('Set happened'); // [[ 3 ]] LoadItems.sort(function(a,b){ return a.LoadOrder - b.LoadOrder; }); _.each(LoadItems,function(item){ item.Execute(); }); sendChat(Initializer.Key,"The API has completed the initialization process and is ready to use."); } function KillAllTheEffingHandouts(){ _.each(findObjs({ type: 'handout' }),function(item){ item.remove(); }); } on('ready',function(){ Initializer.Initialize(); }) I added 3 log messages above, numbered 1-3 in the comments. Here's the output, with <<#>> added before each output: Spinning up new sandbox... "Installed Modules:undefined" "ERROR: You cannot set the imgsrc or avatar of an object unless you use an image that is in your Roll20 Library. See the API documentation for more info." <<2>>"About to set" <<1>>"<table><tbody><tr><td>Name</td><td>Version</td></tr></tbody></table>" [] <<3>>"Set happened" Notice that they are out of order from the code. <handout>.get('notes',function(){}) is asynchronous. When you execute it, it will return immediately and keep on executing your other code while the asynchronous code waits to be executed. There is no guarantee of when this will be, just that it will happen. In this case, it's happening AFTER the set (Or more likely, batched as part of the set). There are many ways you an fix this (Brian's gonna jump in here and talk semaphores, you wait!), the easiest in your case is to move the code that follows the get inside the callback. Strangely, when I do that it runs the callback twice... on to the next bug! =D
I didn't realize it was asynchronous. Though that makes sense if it needs to go back to the server for the blob. It worked (or at least appeared to work) synchronously throughout the entire day and then just randomly stopped. I'll rework the code to move the rest of the procedure into the async callback.
1431837928
The Aaron
Pro
API Scripter
Probably night brought increased load, causing the async nature to show up. :) Cheers!
Thanks for the help. :D
1431870467
The Aaron
Pro
API Scripter
No problem. =D
I'm not sure the answer is that the solution is asynchronous. I pulled the semaphore out of the cookbook, and implemented it. // Requires General //noinspection JSUnusedAssignment var Initializer = Initializer || {}; Initializer.Key = "Initializer"; Initializer.Version = 1; Initializer.LoadOrder = 0; Initializer.ToDoList = Initializer.ToDoList || []; Initializer.Installer = Initializer.Installer || {}; Initializer.Installer.ToDoList = Initializer.Installer.ToDoList || []; Initializer.Initialize = function(){ var lock = new Semaphore(function(installedArray){ var LoadList = _.filter(Initializer.Installer.ToDoList,function(ToDoItem) { return _.contains(_.pluck(installedArray,'Name'),ToDoItem.Name); }); _.each(Initializer.ToDoList,function(ToDoItem){ ToDoItem.LoadOrder += 0.1; LoadList.push(ToDoItem); }); LoadList = LoadList.sort(function(a,b){ return a.LoadOrder - b.LoadOrder; }); _.each(LoadList,function(LoadItem){ LoadItem.Execute(); }); var tableString = '<table><tbody><tr><td>Module</td><td>Version</td></tr>'; _.each(Initializer.Installer.ToDoList,function(item){ tableString += '<tr><td>' + item.Name + '</td><td>' + item.Version + '</td></tr>'; }); tableString += '</tbody></table>'; installedListHandout.set('notes',tableString); },1); var installedListHandout = General.GetOrCreateHandout('Installed Modules'); installedListHandout.get('notes',function(installedTable){ var installedArray = []; log(installedTable); if(installedTable) { installedArray = General.TableToJSONArray(installedTable); } lock.p(installedArray); }); }; on('ready',function(){ sendChat(Initializer.Key,"The API has begun initializing."); setTimeout(Initializer.Initialize,2000); }); The log statement in the get('notes') callback prints... <table><tbody><tr><td>Module</td><td>Version</td></tr><tr><td>CurriculumVitae</td><td>1</td></tr><tr><td>TurnTable</td><td>1</td></tr></tbody></table> ...even after deleting the handout.
1432359457
The Aaron
Pro
API Scripter
With this source: function Semaphore(callback, initial, context) { this.lock = parseInt(initial, 10) || 0; this.callback = callback; this.context = context || callback; this.args = _.rest(arguments,3); } Semaphore.prototype = { v: function() { this.lock++; }, p: function() { var parameters; this.lock--; if (this.lock === 0 && this.callback) { // allow sem.p(arg1, arg2, ...) to override args passed to Semaphore constructor if (arguments.length > 0) { parameters = arguments; } else { parameters = this.args; } this.callback.apply(this.context, parameters); } } }; // Requires General var General = {}; General.GetOrCreateHandout = function(handoutName){ var handout = findObjs({ type: 'handout', name : handoutName })[0]; log('General.GetOrCreateHandout:: '+handoutName+ ':' + JSON.stringify(handout)); if(!handout){ handout = createObj('handout', { name: handoutName }); } return handout; }; General.TableToJSONArray = function(){}; //noinspection JSUnusedAssignment var Initializer = Initializer || {}; Initializer.Key = "Initializer"; Initializer.Version = 1; Initializer.LoadOrder = 0; Initializer.ToDoList = Initializer.ToDoList || []; Initializer.Installer = Initializer.Installer || {}; Initializer.Installer.ToDoList = Initializer.Installer.ToDoList || []; Initializer.Initialize = function(){ var lock = new Semaphore(function(installedArray){ var LoadList = _.filter(Initializer.Installer.ToDoList,function(ToDoItem) { return _.contains(_.pluck(installedArray,'Name'),ToDoItem.Name); }); _.each(Initializer.ToDoList,function(ToDoItem){ ToDoItem.LoadOrder += 0.1; LoadList.push(ToDoItem); }); LoadList = LoadList.sort(function(a,b){ return a.LoadOrder - b.LoadOrder; }); _.each(LoadList,function(LoadItem){ LoadItem.Execute(); }); var tableString = '<table><tbody><tr><td>Module</td><td>Version</td></tr>'; _.each(Initializer.Installer.ToDoList,function(item){ tableString += '<tr><td>' + item.Name + '</td><td>' + item.Version + '</td></tr>'; }); tableString += '</tbody></table>'; installedListHandout.set('notes',tableString); },1); var installedListHandout = General.GetOrCreateHandout('Installed Modules'); installedListHandout.get('notes',function(installedTable){ var installedArray = []; log('installedListHandout.get:: '+installedTable); if(installedTable) { installedArray = General.TableToJSONArray(installedTable); } lock.p(installedArray); }); }; on('ready',function(){ sendChat(Initializer.Key,"The API has begun initializing."); setTimeout(Initializer.Initialize,2000); }) With a handout, I am getting this output, which seems fine.: "General.GetOrCreateHandout:: Installed Modules:{\"name\":\"Installed Modules\",\"notes\":1432358018841,\"gmnotes\":\"\",\"inplayerjournals\":\"\",\"archived\":false,\"controlledby\":\"\",\"_type\":\"handout\",\"_id\":\"-JpzBCvy3Pkoaq8w1XO6\",\"avatar\":\"\"}" "installedListHandout.get:: <table><tbody><tr><td>Module</td><td>Version</td></tr></tbody></table>" If I delete the handout, I'm getting this output?!?!?: "General.GetOrCreateHandout:: Installed Modules:undefined" "ERROR: You cannot set the imgsrc or avatar of an object unless you use an image that is in your Roll20 Library. See the API documentation for more info." "installedListHandout.get:: null" "installedListHandout.get:: <table><tbody><tr><td>Module</td><td>Version</td></tr></tbody></table>" Note the bold lines, where the get() callback is being called twice, once with a null, then again with the notes that are set in the semaphored function. I'm not really sure what to say about all that. Certainly, it seems like there is some sort of race condition when a handout is created. I added additional logging and found that one of the get() callback calls occurs before the semaphore, and the other occurs after it. Furthermore, if you comment out the set() operation inside the semaphored function, the second get() callback does not occur. I think there must be some sort of issue where the set() operation is causing the get() callback to be executed.
// Requires General //noinspection JSUnusedAssignment var Initializer = Initializer || {}; Initializer.Key = "Initializer"; Initializer.Version = 1; Initializer.LoadOrder = 0; Initializer.ToDoList = Initializer.ToDoList || []; Initializer.Installer = Initializer.Installer || {}; Initializer.Installer.ToDoList = Initializer.Installer.ToDoList || []; Initializer.Initialize = function(){ var lock = new Semaphore(function(installedArray){ log(installedArray); var LoadList = _.filter(Initializer.Installer.ToDoList,function(ToDoItem) { return _.contains(_.pluck(installedArray,'Name'),ToDoItem.Name); }); _.each(Initializer.ToDoList,function(ToDoItem){ ToDoItem.LoadOrder += 0.1; LoadList.push(ToDoItem); }); LoadList = LoadList.sort(function(a,b){ return a.LoadOrder - b.LoadOrder; }); _.each(LoadList,function(LoadItem){ LoadItem.Execute(); }); var tableString = '<table><tbody><tr><td>Module</td><td>Version</td></tr>'; _.each(Initializer.Installer.ToDoList,function(item){ tableString += '<tr><td>' + item.Name + '</td><td>' + item.Version + '</td></tr>'; }); tableString += '</tbody></table>'; /* var handout = General.GetOrCreateHandout('Installed Modules'); handout.set('notes',tableString);*/ },1); General.GetOrCreateHandout('Installed Modules').get('notes',function(installedTable){ var installedArray = []; log(installedTable); if(installedTable) { installedArray = General.TableToJSONArray(installedTable); } lock.p(installedArray); }); }; on('ready',function(){ sendChat(Initializer.Key,"The API has begun initializing."); setTimeout(Initializer.Initialize,2000); }); Oddly enough, when using the following code (with the setter commented out), log(installedTable) returns null, and log(installedArray) returns []. However, the install statements don't execute. They should be creating additional handouts.
Ok, solved that little blip. The _.filter on the LoadList declaration should be _.reject.
1432361426

Edited 1432361436
The Aaron
Pro
API Scripter
Man, if I had a nickel for every time I'd reversed those...!
It looks like after making that change, the script runs as intended. Until I have a better solution (that doesn't somehow run the get callback twice), though, I've included install commands that runs the install function as well.