From 5158b19eae220892c381e0d76b43a2e938262734 Mon Sep 17 00:00:00 2001 From: ChasarooniZ Date: Thu, 21 Nov 2024 14:10:46 -0600 Subject: [PATCH] Possible Performance Improvements --- languages/en.json | 4 +-- scripts/hooks.js | 29 ++++++++-------- scripts/module.js | 85 ++++++++++++++++++++++++++++++++++------------- 3 files changed, 79 insertions(+), 39 deletions(-) diff --git a/languages/en.json b/languages/en.json index c4b3734..343a155 100644 --- a/languages/en.json +++ b/languages/en.json @@ -5,7 +5,7 @@ "include-canvas": { "enabled": { "name": "Include Canvas Tokens On Timebased Refreshed", - "hint": "When enabled will not just check the party, but also all tokens on the canvas to see to refresh their uses" + "hint": "When enabled will not just check the party, but also all tokens on the canvas to see to refresh their uses. Note this will have performance implications if you have a large number of actors" } }, "automate-item": { @@ -45,4 +45,4 @@ } } } -} +} \ No newline at end of file diff --git a/scripts/hooks.js b/scripts/hooks.js index 734a4a6..01171a8 100644 --- a/scripts/hooks.js +++ b/scripts/hooks.js @@ -1,6 +1,6 @@ import { MODULE_ID } from "./helper/const.js"; import { getCanvasActors } from "./lib/helpers.js"; -import { getCoolDownTime, updateFrequencyOfActors } from "./module.js"; +import { cooldownCache, getCoolDownTime, updateFrequencyOfActors } from "./module.js"; import { showCooldownsOnSheet } from "./styling.js"; /** @@ -14,14 +14,15 @@ import { showCooldownsOnSheet } from "./styling.js"; export async function updateItem(item, changes, _diff, _userID) { const usesChange = changes?.system?.frequency?.value; const maxUses = item?.system?.frequency?.max; + if (!maxUses) return; // If uses are less than max, update or set cooldown flag if (usesChange < maxUses) { updateCooldownFlag(item); - } - // If uses are at or above max, remove cooldown flag - else if (usesChange && usesChange >= maxUses) { + } else if (usesChange && usesChange >= maxUses) { + // If uses are at or above max, remove cooldown flag item.unsetFlag(MODULE_ID, "cooldown"); + cooldownCache.delete(item.uuid); } } @@ -31,8 +32,8 @@ export async function updateItem(item, changes, _diff, _userID) { * @param {Object} item - The item to update the cooldown flag for. */ function updateCooldownFlag(item) { - const currentFlag = item.getFlag(MODULE_ID, "cooldown"); const frequencyPer = item?.system?.frequency?.per; + const currentFlag = cooldownCache.get(item.uuid) || item.getFlag(MODULE_ID, "cooldown"); const shouldUpdateFlag = !currentFlag || currentFlag.per !== frequencyPer || @@ -42,15 +43,18 @@ function updateCooldownFlag(item) { if (shouldUpdateFlag) { const cooldown = getCoolDownTime(item?.system?.frequency); if (cooldown) { - item.setFlag(MODULE_ID, "cooldown", { + const newFlag = { cooldown, per: frequencyPer, version: game.modules?.get(MODULE_ID)?.version - }); + }; + item.setFlag(MODULE_ID, "cooldown", newFlag); + cooldownCache.set(item.uuid, newFlag); } } } + /** * Updates the world time and refreshes frequency-based abilities for actors. * @@ -58,18 +62,15 @@ function updateCooldownFlag(item) { * @param {number} diff - The difference in time since the last update. */ export async function updateWorldTime(total, diff) { - // Get party members - let actors = game.actors.party.members; + if (!game.user.isGM) return; // Include canvas tokens if the setting is enabled - if (game.settings.get(MODULE_ID, "include-canvas.enabled")) { - const canvasActors = getCanvasActors(); - actors = actors.concat(canvasActors); - } + const actors = game.settings.get(MODULE_ID, "include-canvas.enabled") + ? [...game.actors.party.members, ...getCanvasActors()] + : game.actors.party.members; // Determine the update type based on combat status const updateType = game.combat ? "default" : "updateTime"; - // Update frequency-based abilities for all relevant actors await updateFrequencyOfActors(actors, total, diff, updateType); } diff --git a/scripts/module.js b/scripts/module.js index 56dad64..27f9210 100644 --- a/scripts/module.js +++ b/scripts/module.js @@ -1,6 +1,8 @@ import { DAY, HOUR, MINUTE, MODULE_ID, MONTH, WEEK, YEAR } from "./helper/const.js"; import { combatRound, updateItem, updateWorldTime } from "./hooks.js"; +export const cooldownCache = new Map(); + Hooks.once("ready", function () { console.log("PF2E Uses Updater is Active"); if (!game.user.isGM) return; @@ -21,15 +23,22 @@ Hooks.once("ready", function () { * @param {string} [situation="default"] - The situation context. * @returns {Promise} */ -export async function updateFrequencyOfActors( - party, - total, - diff, - situation = "default" -) { +export async function updateFrequencyOfActors(party, total, diff, situation = "default") { + const updates = []; + const specialCaseUpdates = []; + for (const character of party) { - await updateFrequency(character, total, diff, situation); + const { itemUpdates } = await processCharacterItems(character, total, diff, situation); + updates.push(...itemUpdates); + //specialCaseUpdates.push(...specialCases); } + + if (updates.length > 0) { + await Item.updateDocuments(updates); + } + + // Handle special cases asynchronously + //specialCaseUpdates.forEach(update => update()); } /** @@ -43,7 +52,7 @@ export async function updateFrequencyOfActors( export async function updateFrequency(character, total, diff, situation = "default") { const items = character.items.contents; const relevantItems = items.filter((it) => - isItemRelevant(it, total, diff, situation) + ['action', 'equipment'].includes(it.type) && isItemRelevant(it, total, diff, situation) ); relevantItems.forEach((it) => { it.unsetFlag(MODULE_ID, "cooldown"); @@ -59,6 +68,31 @@ export async function updateFrequency(character, total, diff, situation = "defau } } +async function processCharacterItems(character, total, diff, situation) { + const itemUpdates = []; + const specialCases = []; + const relevantItems = character.items.contents.filter(it => checkSpecialCases(it) || isItemRelevant(it, total, diff, situation)); + + for (const item of relevantItems) { + if (checkSpecialCases(it)) { + //specialCases.push(() => handleSpecialCase(item, total, diff, situation)); + specialCases.push(item) + await handleSpecialCase(item, total, diff, situation) + } else { + + item.unsetFlag(MODULE_ID, "cooldown"); + itemUpdates.push({ + _id: item.id, + "system.frequency.value": item.system.frequency?.max ?? 1 + }); + + + } + } + + return { itemUpdates, specialCases }; +} + /** * Checks if an item is relevant for updating based on cooldown and situation. * @param {Object} item - The item to check. @@ -67,24 +101,25 @@ export async function updateFrequency(character, total, diff, situation = "defau * @param {string} situation - The situation context. * @returns {Promise} */ -export function isItemRelevant(item, total, diff, situation) { - if (!item?.getFlag(MODULE_ID, "cooldown")) updateItem(item, item, diff, null); - const { cooldown } = item?.getFlag(MODULE_ID, "cooldown") || {}; - const isSpecialCase = checkAndHandleSpecialCase(item, total, diff, situation); - if (!cooldown && !isSpecialCase) return false; +function isItemRelevant(item, total, diff, situation) { + if (!cooldownCache.has(item.uuid)) { + const cooldown = item.getFlag(MODULE_ID, "cooldown")?.cooldown; + cooldownCache.set(item.uuid, cooldown); + } + + const cooldown = cooldownCache.get(item.uuid); + if (!cooldown) return false; + + const frequency = item.system.frequency; + if (!frequency || frequency.value >= frequency.max) return false; + switch (situation) { case "updateTime": - return ( - item?.system?.frequency?.value < item?.system?.frequency?.max && - (cooldown <= total || ["turn", "round"].includes(cooldown)) - ); + return cooldown <= total || ["turn", "round"].includes(cooldown); case "endRound": - return false; // TODO replace me with code for possibly handling longer cooldowns coming up mid combat? + return false; // TODO: Implement logic for handling longer cooldowns in combat default: - return ( - item?.system?.frequency?.value < item?.system?.frequency?.max && - cooldown <= total - ); + return cooldown <= total; } } @@ -135,7 +170,7 @@ export function getCombatActor() { * @param {string} _situation - The situation context (unused). * @returns {Promise} */ -export async function checkAndHandleSpecialCase(item, _total, diff, _situation) { +export async function handleSpecialCase(item, _total, diff, _situation) { const slug = item.system.slug; const actor = item.actor; switch (slug) { @@ -170,3 +205,7 @@ export async function checkAndHandleSpecialCase(item, _total, diff, _situation) } return false; } + +export function checkSpecialCases(item) { + return ['aeon-stone-pearly-white-spindle'].includes(item.slug); +}