From b694f20e1ce11e516ffd71a737f389b5ac18bc26 Mon Sep 17 00:00:00 2001 From: Johannes Loher Date: Thu, 8 Jul 2021 02:32:25 +0200 Subject: [PATCH] Update migrations for 0.8.x --- src/module/migrations.ts | 8 +- src/module/migrations/001.ts | 20 ++- src/module/migrations/002.ts | 174 ++++----------------- src/module/migrations/003.ts | 167 ++++---------------- src/module/migrations/004.ts | 179 ++++------------------ src/module/migrations/migrationHelpers.ts | 174 +++++++++++++++++++++ yarn.lock | 4 +- 7 files changed, 277 insertions(+), 449 deletions(-) create mode 100644 src/module/migrations/migrationHelpers.ts diff --git a/src/module/migrations.ts b/src/module/migrations.ts index f392aa5..9dfb122 100644 --- a/src/module/migrations.ts +++ b/src/module/migrations.ts @@ -5,9 +5,9 @@ import { getGame } from "./helpers"; import logger from "./logger"; import { migrate as migrate001 } from "./migrations/001"; -// import { migrate as migrate002 } from "./migrations/002"; -// import { migrate as migrate003 } from "./migrations/003"; -// import { migrate as migrate004 } from "./migrations/004"; +import { migrate as migrate002 } from "./migrations/002"; +import { migrate as migrate003 } from "./migrations/003"; +import { migrate as migrate004 } from "./migrations/004"; import notifications from "./ui/notifications"; async function migrate(): Promise { @@ -78,7 +78,7 @@ function getTargetMigrationVersion(): number { return migrations.length; } -const migrations: Array<() => Promise> = [migrate001 /* migrate002, migrate003, migrate004 */]; +const migrations: Array<() => Promise> = [migrate001, migrate002, migrate003, migrate004]; function isFirstWorldStart(migrationVersion: number): boolean { return migrationVersion < 0; diff --git a/src/module/migrations/001.ts b/src/module/migrations/001.ts index e77877c..c8e2f7b 100644 --- a/src/module/migrations/001.ts +++ b/src/module/migrations/001.ts @@ -2,15 +2,18 @@ // // SPDX-License-Identifier: MIT -import { getGame } from "../helpers"; -import logger from "../logger"; +import { + getCompendiumMigrator, + getSceneUpdateDataGetter, + migrateActors, + migrateCompendiums, + migrateScenes, +} from "./migrationHelpers"; export async function migrate(): Promise { - for (const a of getGame().actors?.contents ?? []) { - const updateData = getActorUpdateData(); - logger.info(`Migrating actor ${a.name}`); - await a.update(updateData); - } + await migrateActors(getActorUpdateData); + await migrateScenes(getSceneUpdateData); + await migrateCompendiums(migrateCompendium); } function getActorUpdateData(): Record { @@ -33,3 +36,6 @@ function getActorUpdateData(): Record { }; return updateData; } + +const getSceneUpdateData = getSceneUpdateDataGetter(getActorUpdateData); +const migrateCompendium = getCompendiumMigrator({ getActorUpdateData, getSceneUpdateData }); diff --git a/src/module/migrations/002.ts b/src/module/migrations/002.ts index d983e2b..93ac406 100644 --- a/src/module/migrations/002.ts +++ b/src/module/migrations/002.ts @@ -2,153 +2,33 @@ // // SPDX-License-Identifier: MIT -import logger from "../logger"; +import { + getActorUpdateDataGetter, + getCompendiumMigrator, + getSceneUpdateDataGetter, + migrateActors, + migrateCompendiums, + migrateItems, + migrateScenes, +} from "./migrationHelpers"; -// export async function migrate(): Promise { -// await migrateItems(); -// await migrateActors(); -// await migrateScenes(); -// await migrateCompendiums(); -// } +export async function migrate(): Promise { + await migrateItems(getItemUpdateData); + await migrateActors(getActorUpdateData); + await migrateScenes(getSceneUpdateData); + await migrateCompendiums(migrateCompendium); +} -// async function migrateItems() { -// for (const item of getGame().items?.contents ?? []) { -// try { -// const updateData = getItemUpdateData(item.toObject()); -// if (updateData) { -// logger.info(`Migrating Item entity ${item.name} (${item.id})`); -// await item.update(updateData), { enforceTypes: false }; -// } -// } catch (err) { -// err.message = `Error during migration of Item entity ${item.name} (${item.id}), continuing anyways.`; -// logger.error(err); -// } -// } -// } +function getItemUpdateData( + itemData: Partial, +): DeepPartial | undefined { + if (!["equipment", "trinket"].includes(itemData.type ?? "")) return undefined; + return { type: itemData.type === "equipment" ? ("loot" as const) : ("equipment" as const) }; +} -// function getItemUpdateData(itemData: foundry.data.ItemData["_source"]) { -// if (!["equipment", "trinket"].includes(itemData.type ?? "")) return undefined; -// return { type: itemData.type === "equipment" ? ("loot" as const) : ("equipment" as const) }; -// } - -// async function migrateActors() { -// for (const actor of getGame().actors?.contents ?? []) { -// try { -// const updateData = getActorUpdateData(actor.toObject()); -// if (updateData) { -// logger.info(`Migrating Actor entity ${actor.name} (${actor.id})`); -// await actor.update(updateData); -// } -// } catch (err) { -// err.message = `Error during migration of Actor entity ${actor.name} (${actor.id}), continuing anyways.`; -// logger.error(err); -// } -// } -// } - -// function getActorUpdateData(actorData: foundry.data.ActorData["_source"]) { -// let hasItemUpdates = false; -// const items = actorData.items.map((itemData: foundry.data.ItemData["_source"]) => { -// const update = itemData ? getItemUpdateData(itemData) : undefined; -// if (update) { -// hasItemUpdates = true; -// return { ...itemData, ...update }; -// } else { -// return itemData; -// } -// }); -// return hasItemUpdates ? { items } : undefined; -// } - -// async function migrateScenes() { -// for (const scene of getGame().scenes?.contents ?? []) { -// try { -// const updateData = getSceneUpdateData(scene.toObject()); -// if (updateData) { -// logger.info(`Migrating Scene entity ${scene.name} (${scene.id})`); -// await scene.update(updateData); -// } -// } catch (err) { -// err.message = `Error during migration of Scene document ${scene.name} (${scene.id}), continuing anyways.`; -// logger.error(err); -// } -// } -// } - -// function getSceneUpdateData(scene: Scene) { -// let hasTokenUpdates = false; -// // TODO: Continue from here -// const tokens = scene.tokens.map((tokenData: foundry.data.TokenData["_source"]) => { -// if (!tokenData.actorId || tokenData.actorLink) { -// tokenData.actorData = {}; -// } else if (!getGame().actors?.has(tokenData.actorId)) { -// tokenData.actorId = null; -// tokenData.actorData = {}; -// } else if (!tokenData.actorLink) { -// const actorData = duplicate(tokenData.actorData); -// actorData.type = token.actor?.type; -// } - -// if (!tokenData.actorId || tokenData.actorLink || tokenData.actorData.data) { -// tokenData.actorData = {}; -// hasTokenUpdates = true; -// return tokenData; -// } -// const token = new Token(tokenData); -// if (!token.actor) { -// tokenData.actorId = null as unknown as string; -// tokenData.actorData = {}; -// hasTokenUpdates = true; -// } else if (!tokenData.actorLink) { -// const actorUpdateData = getActorUpdateData(token.data.actorData); -// tokenData.actorData = mergeObject(token.data.actorData, actorUpdateData); -// hasTokenUpdates = true; -// } -// return tokenData; -// }); -// if (!hasTokenUpdates) return undefined; -// return hasTokenUpdates ? { tokens } : undefined; -// } - -// async function migrateCompendiums() { -// for (const compendium of getGame().packs ?? []) { -// if (compendium.metadata.package !== "world") continue; -// if (!["Actor", "Item", "Scene"].includes(compendium.metadata.entity)) continue; -// await migrateCompendium(compendium); -// } -// } - -// async function migrateCompendium(compendium: Compendium) { -// const entityName = compendium.metadata.entity; -// if (!["Actor", "Item", "Scene"].includes(entityName)) return; -// const wasLocked = compendium.locked; -// await compendium.configure({ locked: false }); - -// const content = await compendium.getContent(); - -// for (const entity of content) { -// try { -// const getUpdateData = (entity: Entity) => { -// switch (entityName) { -// case "Item": -// return getItemUpdateData(entity._data); -// case "Actor": -// return getActorUpdateData(entity._data); -// case "Scene": -// return getSceneUpdateData(entity._data as Scene.Data); -// } -// }; -// const updateData = getUpdateData(entity); -// if (updateData) { -// logger.info(`Migrating entity ${entity.name} (${entity.id}) in compendium ${compendium.collection}`); -// await compendium.updateEntity({ ...updateData, _id: entity._id }); -// } -// } catch (err) { -// err.message = `Error during migration of entity ${entity.name} (${entity.id}) in compendium ${compendium.collection}, continuing anyways.`; -// logger.error(err); -// } -// } - -// await compendium.migrate({}); -// await compendium.configure({ locked: wasLocked }); -// } +const getActorUpdateData = getActorUpdateDataGetter(getItemUpdateData); +const getSceneUpdateData = getSceneUpdateDataGetter(getActorUpdateData); +const migrateCompendium = getCompendiumMigrator( + { getItemUpdateData, getActorUpdateData, getSceneUpdateData }, + { migrateToTemplateEarly: false }, +); diff --git a/src/module/migrations/003.ts b/src/module/migrations/003.ts index 7c537f6..0735a6e 100644 --- a/src/module/migrations/003.ts +++ b/src/module/migrations/003.ts @@ -2,146 +2,33 @@ // // SPDX-License-Identifier: MIT -import logger from "../logger"; +import { + getActorUpdateDataGetter, + getCompendiumMigrator, + getSceneUpdateDataGetter, + migrateActors, + migrateCompendiums, + migrateItems, + migrateScenes, +} from "./migrationHelpers"; -// export async function migrate(): Promise { -// await migrateItems(); -// await migrateActors(); -// await migrateScenes(); -// await migrateCompendiums(); -// } +export async function migrate(): Promise { + await migrateItems(getItemUpdateData); + await migrateActors(getActorUpdateData); + await migrateScenes(getSceneUpdateData); + await migrateCompendiums(migrateCompendium); +} -// async function migrateItems() { -// for (const item of getGame().items?.entities ?? []) { -// try { -// const updateData = getItemUpdateData(item._data); -// if (updateData) { -// logger.info(`Migrating Item entity ${item.name} (${item.id})`); -// await item.update(updateData), { enforceTypes: false }; -// } -// } catch (err) { -// err.message = `Error during migration of Item entity ${item.name} (${item.id}), continuing anyways.`; -// logger.error(err); -// } -// } -// } +function getItemUpdateData(itemData: Partial) { + if (!["loot"].includes(itemData.type ?? "")) return undefined; + return { + "-=data.equipped": null, + }; +} -// function getItemUpdateData(itemData: DeepPartial) { -// if (!["loot"].includes(itemData.type ?? "")) return undefined; -// return { -// data: { -// "-=equipped": null, -// }, -// }; -// } - -// async function migrateActors() { -// for (const actor of getGame().actors?.entities ?? []) { -// try { -// const updateData = getActorUpdateData(actor._data); -// if (updateData) { -// logger.info(`Migrating Actor entity ${actor.name} (${actor.id})`); -// await actor.update(updateData, { enforceTypes: false }); -// } -// } catch (err) { -// err.message = `Error during migration of Actor entity ${actor.name} (${actor.id}), continuing anyways.`; -// logger.error(err); -// } -// } -// } - -// function getActorUpdateData(actorData: DeepPartial) { -// let hasItemUpdates = false; -// const items = actorData.items?.map((itemData) => { -// const update = itemData ? getItemUpdateData(itemData) : undefined; -// if (update) { -// hasItemUpdates = true; -// return mergeObject(itemData, update, { enforceTypes: false, inplace: false }); -// } else { -// return itemData; -// } -// }); -// return hasItemUpdates ? { items } : undefined; -// } - -// async function migrateScenes() { -// for (const scene of getGame().scenes?.entities ?? []) { -// try { -// const updateData = getSceneUpdateData(scene._data); -// if (updateData) { -// logger.info(`Migrating Scene entity ${scene.name} (${scene.id})`); -// await scene.update(updateData, { enforceTypes: false }); -// } -// } catch (err) { -// err.message = `Error during migration of Scene entity ${scene.name} (${scene.id}), continuing anyways.`; -// logger.error(err); -// } -// } -// } - -// function getSceneUpdateData(sceneData: Scene.Data) { -// let hasTokenUpdates = false; -// const tokens = sceneData.tokens.map((tokenData) => { -// if (!tokenData.actorId || tokenData.actorLink || tokenData.actorData.data) { -// tokenData.actorData = {}; -// hasTokenUpdates = true; -// return tokenData; -// } -// const token = new Token(tokenData); -// if (!token.actor) { -// tokenData.actorId = null as unknown as string; -// tokenData.actorData = {}; -// hasTokenUpdates = true; -// } else if (!tokenData.actorLink) { -// const actorUpdateData = getActorUpdateData(token.data.actorData); -// tokenData.actorData = mergeObject(token.data.actorData, actorUpdateData); -// hasTokenUpdates = true; -// } -// return tokenData; -// }); -// if (!hasTokenUpdates) return undefined; -// return hasTokenUpdates ? { tokens } : undefined; -// } - -// async function migrateCompendiums() { -// for (const compendium of getGame().packs ?? []) { -// if (compendium.metadata.package !== "world") continue; -// if (!["Actor", "Item", "Scene"].includes(compendium.metadata.entity)) continue; -// await migrateCompendium(compendium); -// } -// } - -// async function migrateCompendium(compendium: Compendium) { -// const entityName = compendium.metadata.entity; -// if (!["Actor", "Item", "Scene"].includes(entityName)) return; -// const wasLocked = compendium.locked; -// await compendium.configure({ locked: false }); - -// const content = await compendium.getContent(); - -// for (const entity of content) { -// try { -// const getUpdateData = (entity: Entity) => { -// switch (entityName) { -// case "Item": -// return getItemUpdateData(entity._data); -// case "Actor": -// return getActorUpdateData(entity._data); -// case "Scene": -// return getSceneUpdateData(entity._data as Scene.Data); -// } -// }; -// const updateData = getUpdateData(entity); -// if (updateData) { -// logger.info(`Migrating entity ${entity.name} (${entity.id}) in compendium ${compendium.collection}`); -// await compendium.updateEntity({ ...updateData, _id: entity._id }); -// } -// } catch (err) { -// err.message = `Error during migration of entity ${entity.name} (${entity.id}) in compendium ${compendium.collection}, continuing anyways.`; -// logger.error(err); -// } -// } - -// await compendium.migrate({}); -// await compendium.configure({ locked: wasLocked }); -// } +const getActorUpdateData = getActorUpdateDataGetter(getItemUpdateData); +const getSceneUpdateData = getSceneUpdateDataGetter(getActorUpdateData); +const migrateCompendium = getCompendiumMigrator( + { getItemUpdateData, getActorUpdateData }, + { migrateToTemplateEarly: false }, +); diff --git a/src/module/migrations/004.ts b/src/module/migrations/004.ts index d942f74..6dc396f 100644 --- a/src/module/migrations/004.ts +++ b/src/module/migrations/004.ts @@ -2,157 +2,38 @@ // // SPDX-License-Identifier: MIT -import { DS4SpellDataSourceData } from "../item/item-data-source"; -import logger from "../logger"; +import { + getActorUpdateDataGetter, + getCompendiumMigrator, + getSceneUpdateDataGetter, + migrateActors, + migrateCompendiums, + migrateItems, + migrateScenes, +} from "./migrationHelpers"; -// export async function migrate(): Promise { -// await migrateItems(); -// await migrateActors(); -// await migrateScenes(); -// await migrateCompendiums(); -// } +export async function migrate(): Promise { + await migrateItems(getItemUpdateData); + await migrateActors(getActorUpdateData); + await migrateScenes(getSceneUpdateData); + await migrateCompendiums(migrateCompendium); +} -// async function migrateItems() { -// for (const item of getGame().items?.entities ?? []) { -// try { -// const updateData = getItemUpdateData(item._data); -// if (updateData) { -// logger.info(`Migrating Item entity ${item.name} (${item.id})`); -// await item.update(updateData), { enforceTypes: false }; -// } -// } catch (err) { -// err.message = `Error during migration of Item entity ${item.name} (${item.id}), continuing anyways.`; -// logger.error(err); -// } -// } -// } +function getItemUpdateData(itemData: Partial) { + if (itemData.type !== "spell") return; + const updateData: Record = { + "-=data.scrollPrice": null, + "data.minimumLevels": { healer: null, wizard: null, sorcerer: null }, + }; -// function getItemUpdateData(itemData: DeepPartial) { -// if (!["spell"].includes(itemData.type ?? "")) return undefined; -// const updateData: Record = { -// "-=data.scrollPrice": null, -// "data.minimumLevels": { healer: null, wizard: null, sorcerer: null }, -// }; -// if (((itemData.data as DS4SpellDataSourceData).cooldownDuration.unit as string) === "custom") { -// updateData["data.cooldownDuration.unit"] = "rounds"; -// } -// return updateData; -// } + const cooldownDurationUnit: string | undefined = itemData.data?.cooldownDuration.unit; -// async function migrateActors() { -// for (const actor of getGame().actors?.entities ?? []) { -// try { -// const updateData = getActorUpdateData(actor._data); -// if (updateData) { -// logger.info(`Migrating Actor entity ${actor.name} (${actor.id})`); -// await actor.update(updateData, { enforceTypes: false }); -// } -// } catch (err) { -// err.message = `Error during migration of Actor entity ${actor.name} (${actor.id}), continuing anyways.`; -// logger.error(err); -// } -// } -// } + if (cooldownDurationUnit === "custom") { + updateData["data.cooldownDuration.unit"] = "rounds"; + } + return updateData; +} -// function getActorUpdateData(actorData: DeepPartial) { -// let hasItemUpdates = false; -// const items = actorData.items?.map((itemData) => { -// const update = itemData ? getItemUpdateData(itemData) : undefined; -// if (update) { -// hasItemUpdates = true; -// return mergeObject(itemData, update, { enforceTypes: false, inplace: false }); -// } else { -// return itemData; -// } -// }); -// const updateData: Record = {}; -// if (actorData.type === "character") { -// updateData["data.slayerPoints"] = { value: 0 }; -// } -// if (hasItemUpdates) { -// updateData["items"] = items; -// } -// return updateData; -// } - -// async function migrateScenes() { -// for (const scene of getGame().scenes?.entities ?? []) { -// try { -// const updateData = getSceneUpdateData(scene._data); -// if (updateData) { -// logger.info(`Migrating Scene entity ${scene.name} (${scene.id})`); -// await scene.update(updateData, { enforceTypes: false }); -// } -// } catch (err) { -// err.message = `Error during migration of Scene entity ${scene.name} (${scene.id}), continuing anyways.`; -// logger.error(err); -// } -// } -// } - -// function getSceneUpdateData(sceneData: Scene.Data) { -// let hasTokenUpdates = false; -// const tokens = sceneData.tokens.map((tokenData) => { -// if (!tokenData.actorId || tokenData.actorLink || tokenData.actorData.data) { -// tokenData.actorData = {}; -// hasTokenUpdates = true; -// return tokenData; -// } -// const token = new Token(tokenData); -// if (!token.actor) { -// tokenData.actorId = null as unknown as string; -// tokenData.actorData = {}; -// hasTokenUpdates = true; -// } else if (!tokenData.actorLink) { -// const actorUpdateData = getActorUpdateData(token.data.actorData); -// tokenData.actorData = mergeObject(token.data.actorData, actorUpdateData); -// hasTokenUpdates = true; -// } -// return tokenData; -// }); -// if (!hasTokenUpdates) return undefined; -// return hasTokenUpdates ? { tokens } : undefined; -// } - -// async function migrateCompendiums() { -// for (const compendium of getGame().packs ?? []) { -// if (compendium.metadata.package !== "world") continue; -// if (!["Actor", "Item", "Scene"].includes(compendium.metadata.entity)) continue; -// await migrateCompendium(compendium); -// } -// } - -// async function migrateCompendium(compendium: Compendium) { -// const entityName = compendium.metadata.entity; -// if (!["Actor", "Item", "Scene"].includes(entityName)) return; -// const wasLocked = compendium.locked; -// await compendium.configure({ locked: false }); -// await compendium.migrate({}); - -// const content = await compendium.getContent(); - -// for (const entity of content) { -// try { -// const getUpdateData = (entity: Entity) => { -// switch (entityName) { -// case "Item": -// return getItemUpdateData(entity._data); -// case "Actor": -// return getActorUpdateData(entity._data); -// case "Scene": -// return getSceneUpdateData(entity._data as Scene.Data); -// } -// }; -// const updateData = getUpdateData(entity); -// if (updateData) { -// logger.info(`Migrating entity ${entity.name} (${entity.id}) in compendium ${compendium.collection}`); -// await compendium.updateEntity({ ...updateData, _id: entity._id }); -// } -// } catch (err) { -// err.message = `Error during migration of entity ${entity.name} (${entity.id}) in compendium ${compendium.collection}, continuing anyways.`; -// logger.error(err); -// } -// } - -// await compendium.configure({ locked: wasLocked }); -// } +const getActorUpdateData = getActorUpdateDataGetter(getItemUpdateData); +const getSceneUpdateData = getSceneUpdateDataGetter(getActorUpdateData); +const migrateCompendium = getCompendiumMigrator({ getItemUpdateData, getActorUpdateData, getSceneUpdateData }); diff --git a/src/module/migrations/migrationHelpers.ts b/src/module/migrations/migrationHelpers.ts new file mode 100644 index 0000000..16f3b53 --- /dev/null +++ b/src/module/migrations/migrationHelpers.ts @@ -0,0 +1,174 @@ +import { DS4Actor } from "../actor/actor"; +import { getGame } from "../helpers"; +import { DS4Item } from "../item/item"; +import logger from "../logger"; + +type ItemUpdateDataGetter = ( + itemData: Partial, +) => DeepPartial | Record | undefined; + +export async function migrateItems(getItemUpdateData: ItemUpdateDataGetter): Promise { + for (const item of getGame().items ?? []) { + try { + const updateData = getItemUpdateData(item.toObject()); + if (updateData) { + logger.info(`Migrating Item document ${item.name} (${item.id})`); + await item.update(updateData), { enforceTypes: false }; + } + } catch (err) { + err.message = `Error during migration of Item document ${item.name} (${item.id}), continuing anyways.`; + logger.error(err); + } + } +} + +type ActorUpdateDataGetter = ( + itemData: Partial, +) => DeepPartial | undefined; + +export async function migrateActors(getActorUpdateData: ActorUpdateDataGetter): Promise { + for (const actor of getGame().actors ?? []) { + try { + const updateData = getActorUpdateData(actor.toObject()); + if (updateData) { + logger.info(`Migrating Actor entity ${actor.name} (${actor.id})`); + await actor.update(updateData); + } + } catch (err) { + err.message = `Error during migration of Actor entity ${actor.name} (${actor.id}), continuing anyways.`; + logger.error(err); + } + } +} + +type SceneUpdateDataGetter = ( + sceneData: foundry.documents.BaseScene["data"], +) => DeepPartial; + +export async function migrateScenes(getSceneUpdateData: SceneUpdateDataGetter): Promise { + for (const scene of getGame().scenes ?? []) { + try { + const updateData = getSceneUpdateData(scene.data); + if (updateData) { + logger.info(`Migrating Scene document ${scene.name} (${scene.id})`); + await scene.update(updateData); + } + } catch (err) { + err.message = `Error during migration of Scene document ${scene.name} (${scene.id}), continuing anyways.`; + logger.error(err); + } + } +} + +type CompendiumMigrator = (compendium: CompendiumCollection) => Promise; + +export async function migrateCompendiums(migrateCompendium: CompendiumMigrator): Promise { + for (const compendium of getGame().packs ?? []) { + if (compendium.metadata.package !== "world") continue; + if (!["Actor", "Item", "Scene"].includes(compendium.metadata.entity)) continue; + await migrateCompendium(compendium); + } +} + +export function getActorUpdateDataGetter(getItemUpdateData: ItemUpdateDataGetter): ActorUpdateDataGetter { + return ( + actorData: Partial, + ): DeepPartial | undefined => { + let hasItemUpdates = false; + const items = actorData.items?.map((itemData) => { + const update = getItemUpdateData(itemData); + if (update) { + hasItemUpdates = true; + return { ...itemData, ...update }; + } else { + return itemData; + } + }); + return hasItemUpdates ? { items } : undefined; + }; +} + +export function getSceneUpdateDataGetter(getActorUpdateData: ActorUpdateDataGetter): SceneUpdateDataGetter { + return (sceneData: foundry.documents.BaseScene["data"]) => { + const tokens = (sceneData.tokens as Collection).map((token: TokenDocument) => { + const t = token.toObject(); + if (!t.actorId || t.actorLink) { + t.actorData = {}; + } else if (!getGame().actors?.has(t.actorId)) { + t.actorId = null; + t.actorData = {}; + } else if (!t.actorLink) { + const actorData = foundry.utils.deepClone(t.actorData); + actorData.type = token.actor?.type; + const update = getActorUpdateData(actorData); + if (update !== undefined) { + ["items" as const, "effects" as const].forEach((embeddedName) => { + const embeddedUpdates = update[embeddedName]; + if (embeddedUpdates === undefined || update[embeddedName]?.length !== 0) return; + const updates = new Map(embeddedUpdates.flatMap((u) => (u && u._id ? [[u._id, u]] : []))); + const originals = t.actorData[embeddedName]; + if (!originals) return; + originals.forEach((original) => { + if (!original._id) return; + const update = updates.get(original._id); + if (update) mergeObject(original, update); + }); + delete update[embeddedName]; + }); + mergeObject(t.actorData, update); + } + } + return t; + }); + return { tokens }; + }; +} + +export function getCompendiumMigrator( + { + getItemUpdateData, + getActorUpdateData, + getSceneUpdateData, + }: { + getItemUpdateData?: ItemUpdateDataGetter; + getActorUpdateData?: ActorUpdateDataGetter; + getSceneUpdateData?: SceneUpdateDataGetter; + } = {}, + { migrateToTemplateEarly = true } = {}, +) { + return async (compendium: CompendiumCollection): Promise => { + const entityName = compendium.metadata.entity; + if (!["Actor", "Item", "Scene"].includes(entityName)) return; + const wasLocked = compendium.locked; + await compendium.configure({ locked: false }); + if (migrateToTemplateEarly) { + await compendium.migrate(); + } + + const documents = await compendium.getDocuments(); + + for (const doc of documents) { + try { + logger.info(`Migrating document ${doc.name} (${doc.id}) in compendium ${compendium.collection}`); + if (doc instanceof DS4Item && getItemUpdateData) { + const updateData = getItemUpdateData(doc.toObject()); + updateData && (await doc.update(updateData)); + } else if (doc instanceof DS4Actor && getActorUpdateData) { + const updateData = getActorUpdateData(doc.toObject()); + updateData && (await doc.update(updateData)); + } else if (doc instanceof Scene && getSceneUpdateData) { + const updateData = getSceneUpdateData(doc.data); + updateData && (await doc.update(updateData)); + } + } catch (err) { + err.message = `Error during migration of document ${doc.name} (${doc.id}) in compendium ${compendium.collection}, continuing anyways.`; + logger.error(err); + } + } + + if (!migrateToTemplateEarly) { + await compendium.migrate(); + } + await compendium.configure({ locked: wasLocked }); + }; +} diff --git a/yarn.lock b/yarn.lock index 825e1c3..4793aca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -673,7 +673,7 @@ __metadata: "@league-of-foundry-developers/foundry-vtt-types@https://github.com/League-of-Foundry-Developers/foundry-vtt-types.git#foundry-0.8.x": version: 0.7.9-6 - resolution: "@league-of-foundry-developers/foundry-vtt-types@https://github.com/League-of-Foundry-Developers/foundry-vtt-types.git#commit=22688f7be9f9c2605c3376e365e12b6084d59b85" + resolution: "@league-of-foundry-developers/foundry-vtt-types@https://github.com/League-of-Foundry-Developers/foundry-vtt-types.git#commit=b111674a20165ac827a0b5db48791d8502d82e43" dependencies: "@types/jquery": ~3.5.5 "@types/simple-peer": ~9.11.0 @@ -683,7 +683,7 @@ __metadata: socket.io-client: 4.1.2 tinymce: 5.8.1 typescript: ^4.1.6 - checksum: bba8fb7081e7cd1c43a5af841820232629a9034e7075d733558b261b5d488fd166dcf9501158cd49e29a7a2e9b7b4ebf6f4aef76b1d740a253455a7432308a77 + checksum: 028b563649d22583d57e80fed7323dfad7dbd8e31cfa054cbb9747041729c9f7dea098f5a245dd54f493be42556cc50f7d5ab5799d01662a74a88d65c97f36ac languageName: node linkType: hard