Finish the one-time migration script for loading old worlds

This commit is contained in:
Oliver 2026-04-20 23:13:10 -06:00
parent 6b6bb261f8
commit 4e1714ee93
3 changed files with 66 additions and 34 deletions

View file

@ -1,7 +1,7 @@
import { __ID__ } from "../consts.mjs"; import { __ID__ } from "../consts.mjs";
const { Actor } = foundry.documents; const { Actor } = foundry.documents;
const { hasProperty, setProperty } = foundry.utils; const { deepClone, hasProperty, setProperty } = foundry.utils;
export class TAFActor extends Actor { export class TAFActor extends Actor {
@ -33,16 +33,6 @@ export class TAFActor extends Actor {
super._onEmbeddedDocumentChange(...args); super._onEmbeddedDocumentChange(...args);
this.#sortedTypes = null; this.#sortedTypes = null;
}; };
static migrateData(data, options) {
if (options.partial) { return }
console.log(`Actor#migrateData`, foundry.utils.deepClone(data), options);
if (Object.keys(data.system?.attr ?? {}).length > 0) {
console.log(`attributes exist`)
setProperty(data, `flags.${__ID__}.convertAttributesIntoItems`, true);
};
return data;
};
// #endregion Lifecycle // #endregion Lifecycle
// #region Token Attributes // #region Token Attributes
@ -116,4 +106,35 @@ export class TAFActor extends Actor {
return this.#sortedTypes = types; return this.#sortedTypes = types;
}; };
// #endregion Getters // #endregion Getters
// #region Data Migration
/**
* This checks and performs all data migrations that the system requires, some
* of these are one-time only migrations, others of them will happen every time
* an Actor is updated.
*/
static migrateData(data, options) {
this.#migrateToAttributeItems(data, options);
return super.migrateData(data, options);
};
/**
* This method handles checking if the Actor has attributes within it's raw
* system data model, which was where attributes were stored originally, if
* it detects the need for a migration, it stores the existing attribute data
* into a flag so that the v3.0.0 migration script can handle creating the
* data and removing the property from the Actor.
*/
static #migrateToAttributeItems(data, options) {
if (options.partial) { return }
const attr = data.system?.attr ?? {};
if (Object.keys(attr).length > 0) {
setProperty(
data,
`flags.${__ID__}.convertAttributesIntoItems`,
deepClone(attr),
);
};
};
// #endregion Data Migration
}; };

View file

@ -25,7 +25,7 @@ export async function migrateCollection(
const toMigrate = collection const toMigrate = collection
.filter(doc => doc.getFlag(__ID__, flag)) .filter(doc => doc.getFlag(__ID__, flag))
.map(doc => { .map(doc => {
const update = convertor(doc) ?? {}; const update = convertor(doc, options) ?? {};
update[`_id`] = doc._id; update[`_id`] = doc._id;
// v13/v14+ compatibility shim // v13/v14+ compatibility shim
@ -85,4 +85,21 @@ export function shouldMigrateCompendium(pack, types = [`Actor`, `Item`]) {
// Module compendiums should only be migrated if they don't have a download or manifest URL // Module compendiums should only be migrated if they don't have a download or manifest URL
const module = game.modules.get(pack.metadata.packageName); const module = game.modules.get(pack.metadata.packageName);
return !module.download && !module.manifest; return !module.download && !module.manifest;
} };
export function finishMigrationWarning(warning, version) {
warning.update({ pct: 1 });
setTimeout(
() => {
warning.remove();
ui.notifications.success(
`taf.notifs.success.migration-successful`,
{
localize: true,
format: { version },
},
);
},
3_000,
);
};

View file

@ -1,5 +1,6 @@
import { __ID__ } from "../consts.mjs";
import { Logger } from "../utils/Logger.mjs"; import { Logger } from "../utils/Logger.mjs";
import { migrateCollection, shouldMigrateCompendium } from "./utils.mjs"; import { finishMigrationWarning, migrateCollection, shouldMigrateCompendium } from "./utils.mjs";
const flag = `convertAttributesIntoItems`; const flag = `convertAttributesIntoItems`;
const operations = []; const operations = [];
@ -10,15 +11,14 @@ export async function migrateTo3_0_0() {
const packsToMigrate = game.packs.filter( const packsToMigrate = game.packs.filter(
(pack) => shouldMigrateCompendium(pack, [`Actor`]), (pack) => shouldMigrateCompendium(pack, [`Actor`]),
); );
const intervalSize = 1 / (packsToMigrate.length + 1);
const warning = ui.notifications.warn( const warning = ui.notifications.warn(
"taf.notifs.warn.migration-in-progress", "taf.notifs.warn.migration-in-progress",
{ {
format: { version: `v3.0.0` }, localize: true,
format: { version: `3.0.0` },
progress: true, progress: true,
permanent: true, permanent: true,
console: false,
}, },
); );
@ -30,55 +30,49 @@ export async function migrateTo3_0_0() {
{ update: false, }, { update: false, },
), ),
); );
warning.update({ pct: warning.pct + intervalSize }); warning.update({ pct: 0.25, });
for (const pack of packsToMigrate) { for (const pack of packsToMigrate) {
await pack.getDocuments(); await pack.getDocuments();
const wasLocked = pack.config.locked; const wasLocked = pack.config.locked;
if (wasLocked) pack.configure({ locked: false }); if (wasLocked) await pack.configure({ locked: false });
compendiumOperations.push( compendiumOperations.push(
...await migrateCollection( ...await migrateCollection(
pack, pack,
flag, flag,
handleMigratingActor, handleMigratingActor,
{ pack, update: false, }, { pack: pack.collection, update: false, },
), ),
); );
// foundry.documents.modifyBatch(compendiumOperations); await foundry.documents.modifyBatch(compendiumOperations);
console.log(`compendiumOperations`, compendiumOperations);
if (wasLocked) await pack.configure({ locked: true }); if (wasLocked) await pack.configure({ locked: true });
compendiumOperations = []; compendiumOperations = [];
warning.update({ pct: warning.pct + intervalSize });
}; };
// TODO: re-lock packs here? warning.update({ pct: 0.8 });
warning.update({ pct: 1 }); await foundry.documents.modifyBatch(operations);
// TODO: create the item documents (batch them if possible) finishMigrationWarning(warning, `3.0.0`);
Logger.debug(`Finished v3.0.0 migration, resulting operations:`);
console.log(operations);
// Use: foundry.documents.modifyBatch
// await foundry.documents.modifyBatch(operations);
}; };
function handleMigratingActor(actor) { function handleMigratingActor(actor, options) {
console.log(actor);
const operation = { const operation = {
action: `create`, action: `create`,
broadcast: true,
documentName: `Item`, documentName: `Item`,
parent: actor, parent: actor,
pack: options.pack,
noHook: true, noHook: true,
data: [], data: [],
}; };
const attrs = actor.system?.attr ?? {}; const attrs = actor.getFlag(__ID__, flag) ?? {};
for (const [ key, attr ] of Object.entries(attrs)) { for (const [ key, attr ] of Object.entries(attrs)) {
operation.data.push(convertToItem(key, attr)); operation.data.push(convertToItem(key, attr));
}; };