Performance and 0.9.0 prep (#24)

* significant performance improvements

* added notes to change log

* spelling mistake 1

* spelling mistake 2
features_update
ZoltanTheDM 2021-09-06 19:34:03 -07:00 committed by GitHub
parent 71d9fd7584
commit ef024fbb89
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 213 additions and 233 deletions

View File

@ -41,6 +41,11 @@
0.4.5b Show compendium source in results issue#11 0.4.5b Show compendium source in results issue#11
Try showing compendium in the image mouseover Try showing compendium in the image mouseover
12-Jun-2021 0.5.0 Test for Foundry 0.8.x in which creature type is now data.details.type.value 12-Jun-2021 0.5.0 Test for Foundry 0.8.x in which creature type is now data.details.type.value
9-Spt-2021 CHANGES Removed functions that are disabled in Foundry 0.9.0
Speed up on spells by using queries
Stops already in progress searches if a new one is started
Handles monster types from older revisions
Uses some built-ins for minor performance improvement
*/ */
const CMPBrowser = { const CMPBrowser = {
@ -49,6 +54,8 @@ const CMPBrowser = {
MAXLOAD : 500, //Default for the maximum number to load before displaying a message that you need to filter to see more MAXLOAD : 500, //Default for the maximum number to load before displaying a message that you need to filter to see more
} }
const STOP_SEARCH = 'StopSearchException';
class CompendiumBrowser extends Application { class CompendiumBrowser extends Application {
static get defaultOptions() { static get defaultOptions() {
@ -149,7 +156,7 @@ class CompendiumBrowser extends Application {
let itemId = $(ev.currentTarget).parents("li").attr("data-entry-id"); let itemId = $(ev.currentTarget).parents("li").attr("data-entry-id");
let compendium = $(ev.currentTarget).parents("li").attr("data-entry-compendium"); let compendium = $(ev.currentTarget).parents("li").attr("data-entry-compendium");
let pack = game.packs.find(p => p.collection === compendium); let pack = game.packs.find(p => p.collection === compendium);
pack.getEntity(itemId).then(entity => { pack.getDocument(itemId).then(entity => {
entity.sheet.render(true); entity.sheet.render(true);
}); });
}); });
@ -469,6 +476,10 @@ class CompendiumBrowser extends Application {
console.time("loadAndFilterItems"); console.time("loadAndFilterItems");
await this.checkListsLoaded(); await this.checkListsLoaded();
const seachNumber = Date.now();
this.CurrentSeachNumber = seachNumber;
const maxLoad = game.settings.get(CMPBrowser.MODULE_NAME, "maxload") ?? CMPBrowser.MAXLOAD; const maxLoad = game.settings.get(CMPBrowser.MODULE_NAME, "maxload") ?? CMPBrowser.MAXLOAD;
//0.4.1: Load and filter just one of spells, feats, and items (specified by browserTab) //0.4.1: Load and filter just one of spells, feats, and items (specified by browserTab)
@ -476,72 +487,124 @@ class CompendiumBrowser extends Application {
let numItemsLoaded = 0; let numItemsLoaded = 0;
let compactItems = {}; let compactItems = {};
try{
//Filter the full list, but only save the core compendium information + displayed info //Filter the full list, but only save the core compendium information + displayed info
for (let pack of game.packs) { for (let pack of game.packs) {
if (pack['metadata']['entity'] === "Item" && this.settings.loadedSpellCompendium[pack.collection].load) { if (pack['metadata']['entity'] === "Item" && this.settings.loadedSpellCompendium[pack.collection].load) {
//FIXME: How much could we do with the loaded index rather than all content? //can query just for spells since there is only 1 type
//OR filter the content up front for the decoratedItem.type?? let query = {};
await pack.getContent().then(content => { if (browserTab === "spell"){
for (let item5e of content) { query = {type: "spell"};
let compactItem = null; }
//FIXME: How much could we do with the loaded index rather than all content?
//OR filter the content up front for the decoratedItem.type??
await pack.getDocuments(query).then(content => {
if (browserTab == "spell"){
content.reduce(function(itemsList, item5e){
if (this.CurrentSeachNumber != seachNumber) throw STOP_SEARCH;
numItemsLoaded = Object.keys(itemsList).length;
if (maxLoad <= numItemsLoaded) {
if (updateLoading) {updateLoading(numItemsLoaded);}
throw STOP_SEARCH;
}
const decoratedItem = this.decorateItem(item5e); const decoratedItem = this.decorateItem(item5e);
if (decoratedItem) {
switch (browserTab) { if(decoratedItem && this.passesFilter(decoratedItem, this.spellFilters.activeFilters)){
case "spell": itemsList[item5e.id] = {
if ((decoratedItem.type === "spell") && this.passesFilter(decoratedItem, this.spellFilters.activeFilters)) {
compactItem = {
compendium : pack.collection, compendium : pack.collection,
name : decoratedItem.name, name : decoratedItem.name,
img: decoratedItem.img, img: decoratedItem.img,
data : { data : {
level : decoratedItem.data?.level, level : decoratedItem.data?.level,
components : decoratedItem.data?.components components : decoratedItem.data?.components
},
id: item5e.id
};
} }
}
}
break;
case "feat": return itemsList;
if (["feat","class"].includes(decoratedItem.type) && this.passesFilter(decoratedItem, this.featFilters.activeFilters)) { }.bind(this), compactItems);
compactItem = {
}
else if(browserTab == "feat"){
content.reduce(function(itemsList, item5e){
if (this.CurrentSeachNumber != seachNumber) throw STOP_SEARCH;
numItemsLoaded = Object.keys(itemsList).length;
if (maxLoad <= numItemsLoaded) {
if (updateLoading) {updateLoading(numItemsLoaded);}
throw STOP_SEARCH;
}
const decoratedItem = this.decorateItem(item5e);
if(decoratedItem && ["feat","class"].includes(decoratedItem.type) && this.passesFilter(decoratedItem, this.featFilters.activeFilters)){
itemsList[item5e.id] = {
compendium : pack.collection, compendium : pack.collection,
name : decoratedItem.name, name : decoratedItem.name,
img: decoratedItem.img, img: decoratedItem.img,
classRequirementString : decoratedItem.classRequirementString classRequirementString : decoratedItem.classRequirementString
};
} }
}
break;
case "item": return itemsList;
//0.4.5: Itm type for true items could be many things (weapon, consumable, etc) so we just look for everything except spells, feats, classes }.bind(this), compactItems);
if (!["spell","feat","class"].includes(decoratedItem.type) && this.passesFilter(decoratedItem, this.itemFilters.activeFilters)) {
compactItem = { }
else if(browserTab == "item"){
content.reduce(function(itemsList, item5e){
if (this.CurrentSeachNumber != seachNumber) throw STOP_SEARCH;
numItemsLoaded = Object.keys(itemsList).length;
if (maxLoad <= numItemsLoaded) {
if (updateLoading) {updateLoading(numItemsLoaded);}
throw STOP_SEARCH;
}
const decoratedItem = this.decorateItem(item5e);
if(decoratedItem && !["spell","feat","class"].includes(decoratedItem.type) && this.passesFilter(decoratedItem, this.itemFilters.activeFilters)){
itemsList[item5e.id] = {
compendium : pack.collection, compendium : pack.collection,
name : decoratedItem.name, name : decoratedItem.name,
img: decoratedItem.img, img: decoratedItem.img,
type : decoratedItem.type type : decoratedItem.type
} }
} }
break;
default: return itemsList;
break; }.bind(this), compactItems);
} }
if (compactItem) { //Indicates it passed the filters numItemsLoaded = Object.keys(compactItems).length;
compactItems[decoratedItem._id] = compactItem;
if (numItemsLoaded++ >= maxLoad) break;
//0.4.2e: Update the UI (e.g. "Loading 142 spells")
if (updateLoading) {updateLoading(numItemsLoaded);} if (updateLoading) {updateLoading(numItemsLoaded);}
}
}
}//for item5e of content
}); });
}//end if pack entity === Item }//end if pack entity === Item
if (numItemsLoaded >= maxLoad) break;
}//for packs }//for packs
}
catch(e){
if (e === STOP_SEARCH){
//stopping search early
}
else{
throw e;
}
}
// this.removeDuplicates(compactItems);
/* /*
if (unfoundSpells !== '') { if (unfoundSpells !== '') {
console.log(`Load and Fliter Items | List of Spells that don't have a class associated to them:`); console.log(`Load and Fliter Items | List of Spells that don't have a class associated to them:`);
console.log(unfoundSpells); console.log(unfoundSpells);
@ -553,150 +616,12 @@ class CompendiumBrowser extends Application {
return compactItems; return compactItems;
} }
async loadItems(numToPreload=CMPBrowser.PRELOAD) {
console.log('Item Browser | Started loading items');
console.time("loadItems");
await this.checkListsLoaded();
this.itemsLoaded = false;
let unfoundSpells = '';
let numSpellsLoaded = 0;
let numFeatsLoaded = 0;
let numItemsLoaded = 0;
let items = {
spells: {},
feats: {},
items: {}
};
for (let pack of game.packs) {
if (pack['metadata']['entity'] === "Item" && this.settings.loadedSpellCompendium[pack.collection].load) {
await pack.getContent().then(content => {
for (let item5e of content) {
let item = item5e.data;
if (item.type === 'spell') {
//0.4.1 Only preload a limited number and fill more in as needed
if (numSpellsLoaded++ > numToPreload) continue;
item.compendium = pack.collection;
// determining classes that can use the spell
let cleanSpellName = item.name.toLowerCase().replace(/[^一-龠ぁ-ゔァ-ヴーa-zA-Z0-9---9々〆〤]/g, '').replace("'", '').replace(/ /g, '');
//let cleanSpellName = spell.name.toLowerCase().replace(/[^a-zA-Z0-9\s:]/g, '').replace("'", '').replace(/ /g, '');
if (this.classList[cleanSpellName]) {
let classes = this.classList[cleanSpellName];
item.data.classes = classes.split(',');
} else {
unfoundSpells += cleanSpellName + ',';
}
// getting damage types
item.damageTypes = [];
if (item.data.damage && item.data.damage.parts.length > 0) {
for (let part of item.data.damage.parts) {
let type = part[1];
if (item.damageTypes.indexOf(type) === -1) {
item.damageTypes.push(type);
}
}
}
items.spells[(item._id)] = item;
} else if (item.type === 'feat' || item.type === 'class') {
//0.4.1 Only preload a limited number and fill more in as needed
if (numFeatsLoaded++ > numToPreload) continue;
item.compendium = pack.collection;
// getting damage types
item.damageTypes = [];
if (item.data.damage && item.data.damage.parts.length > 0) {
for (let part of item.data.damage.parts) {
let type = part[1];
if (item.damageTypes.indexOf(type) === -1) {
item.damageTypes.push(type);
}
}
}
// getting class
let reqString = item.data.requirements?.replace(/[0-9]/g, '').trim();
let matchedClass = [];
for (let c in this.subClasses) {
if (reqString && reqString.toLowerCase().indexOf(c) !== -1) {
matchedClass.push(c);
} else {
for (let subClass of this.subClasses[c]) {
if (reqString && reqString.indexOf(subClass) !== -1) {
matchedClass.push(c);
break;
}
}
}
}
item.classRequirement = matchedClass;
item.classRequirementString = matchedClass.join(', ');
// getting uses/ressources status
item.usesRessources = item5e.hasLimitedUses;
item.hasSave = item5e.hasSave;
items.feats[(item._id)] = item;
} else {
//0.4.1 Only preload a limited number and fill more in as needed
if (numItemsLoaded++ > numToPreload) continue;
item.compendium = pack.collection;
// getting damage types
item.damageTypes = [];
if (item.data.damage && item.data.damage.parts.size > 0) {
for (let part of item.data.damage.parts) {
let type = part[1];
if (item.damageTypes.indexOf(type) === -1) {
item.damageTypes.push(type);
}
}
}
// getting pack
let matchedPacks = [];
for (let pack of Object.keys(this.packList)) {
for (let packItem of this.packList[pack]) {
if (item.name.toLowerCase() === packItem.toLowerCase()) {
matchedPacks.push(pack);
break;
}
}
}
item.matchedPacks = matchedPacks;
item.matchedPacksString = matchedPacks.join(', ');
// getting uses/ressources status
item.usesRessources = item5e.hasLimitedUses
items.items[(item._id)] = item;
}
}//for item5e of content
});
}
if ((numSpellsLoaded >= numToPreload) && (numFeatsLoaded >= numToPreload) && (numItemsLoaded >= numToPreload)) break;
}//for packs
if (unfoundSpells !== '') {
console.log(`Item Browser | List of Spells that don't have a class associated to them:`);
console.log(unfoundSpells);
}
this.itemsLoaded = true;
console.timeEnd("loadItems");
console.log(`Item Browser | Finished loading items: ${Object.keys(items.spells).length} spells, ${Object.keys(items.feats).length} features, ${Object.keys(items.items).length} items `);
return items;
}
async loadAndFilterNpcs(updateLoading=null) { async loadAndFilterNpcs(updateLoading=null) {
console.log('NPC Browser | Started loading NPCs'); console.log('NPC Browser | Started loading NPCs');
const seachNumber = Date.now();
this.CurrentSeachNumber = seachNumber;
console.time("loadAndFilterNpcs"); console.time("loadAndFilterNpcs");
let npcs = {}; let npcs = {};
@ -704,38 +629,57 @@ class CompendiumBrowser extends Application {
let numNpcsLoaded = 0; let numNpcsLoaded = 0;
this.npcsLoaded = false; this.npcsLoaded = false;
try{
for (let pack of game.packs) { for (let pack of game.packs) {
if (pack['metadata']['entity'] == "Actor" && this.settings.loadedNpcCompendium[pack.collection].load) { if (pack['metadata']['entity'] == "Actor" && this.settings.loadedNpcCompendium[pack.collection].load) {
await pack.getContent().then(async content => { await pack.getDocuments().then(async content => {
for (let npc of content) { content.reduce(function(actorsList, npc5e){
let compactNpc = null; if (this.CurrentSeachNumber != seachNumber) {throw STOP_SEARCH;}
const decoratedNpc = this.decorateNpc(npc);
if (decoratedNpc && this.passesFilter(decoratedNpc, this.npcFilters.activeFilters)) { numNpcsLoaded = Object.keys(npcs).length;
//0.4.2: Don't store all the details - just the display elements
compactNpc = { if (maxLoad <= numNpcsLoaded) {
if (updateLoading) {updateLoading(numNpcsLoaded);}
throw STOP_SEARCH;
}
const decoratedNpc = this.decorateNpc(npc5e);
if (decoratedNpc && this.passesFilter(decoratedNpc, this.npcFilters.activeFilters)){
actorsList[npc5e.id] = {
compendium : pack.collection, compendium : pack.collection,
name : decoratedNpc.name, name : decoratedNpc.name,
img: decoratedNpc.img, img: decoratedNpc.img,
displayCR : decoratedNpc.displayCR, displayCR : decoratedNpc.displayCR,
displaySize : decoratedNpc.displaySize, displaySize : decoratedNpc.displaySize,
displayType: decoratedNpc.data?.details?.type, displayType: this.getNPCType(decoratedNpc.data?.details?.type),
orderCR : decoratedNpc.data.details.cr, orderCR : decoratedNpc.data.details.cr,
orderSize : decoratedNpc.filterSize orderSize : decoratedNpc.filterSize
};
} }
if (compactNpc) {
npcs[decoratedNpc._id] = compactNpc; return actorsList;
//0.4.2 Don't load more than maxLoad; display a message to filter }.bind(this), npcs);
if (numNpcsLoaded++ > maxLoad) break;
//0.4.2e: Update the UI (e.g. "Loading 142 NPCs") numNpcsLoaded = Object.keys(npcs).length;
if (updateLoading) {updateLoading(numNpcsLoaded);} if (updateLoading) {updateLoading(numNpcsLoaded);}
}
}
}
}); });
} }
//0.4.1 Only preload a limited number and fill more in as needed //0.4.1 Only preload a limited number and fill more in as needed
if (numNpcsLoaded >= maxLoad) break; }
}
catch(e){
if (e == STOP_SEARCH){
//breaking out
}
else{
console.timeEnd("loadAndFilterNpcs");
throw e;
}
} }
this.npcsLoaded = true; this.npcsLoaded = true;
@ -1119,9 +1063,22 @@ class CompendiumBrowser extends Application {
} }
} }
//handle poorly constructed npc
if (decoratedNpc.data?.details?.type && !(decoratedNpc.data?.details?.type instanceof Object)){
decoratedNpc.data.details.type = {value: decoratedNpc.data?.details?.type};
}
return decoratedNpc; return decoratedNpc;
} }
getNPCType(type){
if (type instanceof Object){
return type.value;
}
return type;
}
filterElements(list, subjects, filters) { filterElements(list, subjects, filters) {
for (let element of list) { for (let element of list) {
let subject = subjects[element.dataset.entryId]; let subject = subjects[element.dataset.entryId];
@ -1189,6 +1146,29 @@ class CompendiumBrowser extends Application {
return true; return true;
} }
//incomplete removal of duplicate items
removeDuplicates(spellList){
//sort at n log n
let sortedList = Object.values(spellList).sort((a, b) => a.name.localeCompare(b.name));
//search through sorted list for duplicates
for (let index = 0; index < sortedList.length - 1;){
//all duplicates will be next to eachother
if (sortedList[index].name == sortedList[index + 1].name){
//duplicate something is getting removed
//TODO choose what to remove rather then the second
let remove = index + 1;
delete spellList[sortedList[remove].id];
sortedList.splice(remove, 1);
}
else{
index++;
}
}
}
clearObject(obj) { clearObject(obj) {
let newObj = {}; let newObj = {};
for (let key in obj) { for (let key in obj) {