From 76ee7779851e25b818c25f184ca6f9f7208a70b7 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sun, 12 Jul 2026 00:37:02 +0200 Subject: [PATCH] [Fix] ActiveEffect Mode OneTime Migration (#2078) * Migrations for ActiveEffect Mode * Start on migration handlers --------- Co-authored-by: Carlos Fernandez --- .../migration-handlers/2_5_2.mjs | 38 +++++++++ .../migration-handlers/base.mjs | 77 +++++++++++++++++++ module/systemRegistration/migrations.mjs | 15 +++- 3 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 module/systemRegistration/migration-handlers/2_5_2.mjs create mode 100644 module/systemRegistration/migration-handlers/base.mjs diff --git a/module/systemRegistration/migration-handlers/2_5_2.mjs b/module/systemRegistration/migration-handlers/2_5_2.mjs new file mode 100644 index 00000000..f6d9ea77 --- /dev/null +++ b/module/systemRegistration/migration-handlers/2_5_2.mjs @@ -0,0 +1,38 @@ +import { MigrationHandlerBase } from './base.mjs'; + +export class Migration_2_5_2 extends MigrationHandlerBase { + version = '2.5.2'; + + /** @inheritdoc */ + async updateActiveEffectSource(effectSource, item) { + let shouldUpdate = false; + const newChanges = []; + const srdItem = item?._stats.compendiumSource ? + await foundry.utils.fromUuid(item?._stats.compendiumSource) : + null; + for (let i = 0; i < effectSource.system.changes.length; i++) { + const change = effectSource.system.changes[i]; + const srdEffect = srdItem?.effects.find(x => x.name === effectSource.name); + if (change.type === 'custom') { + const srdChange = srdEffect ? srdEffect.system.changes[i] : null; + if ( + change.key === srdChange.key && + change.value === srdChange.value && + change.type !== srdChange.type + ) { + shouldUpdate = true; + newChanges.push(srdChange); + } + } else { + newChanges.push(change); + } + } + + if (shouldUpdate) { + return { + _id: effectSource._id, + system: { changes: newChanges } + } + } + } +} \ No newline at end of file diff --git a/module/systemRegistration/migration-handlers/base.mjs b/module/systemRegistration/migration-handlers/base.mjs new file mode 100644 index 00000000..7426570d --- /dev/null +++ b/module/systemRegistration/migration-handlers/base.mjs @@ -0,0 +1,77 @@ +/** + * @import DHItem from "../../documents/item.mjs"; + */ + +/** + * The base class of an async migration. + * These are generally run between versions for things that require compendiums or must be done in post. + * The migrate() functions calls the various updateXSource() functions. + * Generally a subclass will override the version and the updateXSource() functions. + */ +export class MigrationHandlerBase { + version = null; + + /** + * Gets change data for an active effect's source, or null if no changes + * @param {object} effectSource + * @param {DHItem} item + * @returns {Promise} + * @protected + */ + async updateActiveEffectSource(effectSource, item) { + return null; + } + + async migrate() { + // todo: handle more than just migrating effects. Right now this can only migrate effects + // NOTE: the preload is hardcoded, we should not hardcode it + + const numActors = game.actors.size; + const numItems = game.items.size; + const finalUpdateProgress = 5; + const DhProgress = game.system.api.applications.ui.DhProgress; + const preRunProgress = game.packs.size; + + const progress = DhProgress.createMigrationProgress( + preRunProgress + numActors + numItems + finalUpdateProgress + ); + + // Preload. Avoid hardcoding in the future + for (const pack of game.packs) { + await pack.getDocuments(); + progress.advance(); + } + + const batch = []; + + const updateItem = async item => { + const itemUpdates = []; + for (const effect of item.effects) { + const changes = await this.updateActiveEffectSource(effect.toObject(), item); + if (changes) itemUpdates.push(changes); + } + if (itemUpdates.length) { + batch.push({ + action: 'update', + documentName: 'ActiveEffect', + updates: itemUpdates, + parent: item + }); + } + }; + + for (const actor of game.actors) { + for (const item of actor.items) { + await updateItem(item); + } + progress.advance(); + } + for (const item of game.items) { + await updateItem(item); + progress.advance(); + } + + await foundry.documents.modifyBatch(batch); + progress.advance({ by: finalUpdateProgress }); + } +} \ No newline at end of file diff --git a/module/systemRegistration/migrations.mjs b/module/systemRegistration/migrations.mjs index 6971c34c..fef97b8f 100644 --- a/module/systemRegistration/migrations.mjs +++ b/module/systemRegistration/migrations.mjs @@ -1,4 +1,5 @@ import { defaultRestOptions } from '../config/generalConfig.mjs'; +import { Migration_2_5_2 } from './migration-handlers/2_5_2.mjs'; export async function runMigrations() { let lastMigrationVersion = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion); @@ -320,7 +321,19 @@ export async function runMigrations() { lastMigrationVersion = '2.1.0'; } - //#endregion await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion, lastMigrationVersion); + + /* -------------------------------------------- */ + /* New Style migrations below this point */ + /* -------------------------------------------- */ + + const migrations = [ + new Migration_2_5_2() + ].filter(m => m.version && foundry.utils.isNewerVersion(m.version, lastMigrationVersion)); + + for (const handler of migrations) { + await handler.migrate(); + await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion, handler.version); + } }