2021-07-08 07:17:59 +02:00
|
|
|
// SPDX-FileCopyrightText: 2021 Johannes Loher
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
|
2022-11-04 21:47:18 +01:00
|
|
|
import { DS4Actor } from "../documents/actor/actor";
|
|
|
|
import { DS4Item } from "../documents/item/item";
|
|
|
|
import { logger } from "../utils/logger";
|
|
|
|
import { getGame } from "../utils/utils";
|
2021-07-08 02:32:25 +02:00
|
|
|
|
2022-11-17 00:12:29 +01:00
|
|
|
/** @typedef {(itemData: Partial<foundry.data.ItemData["_source"]>) => DeepPartial<foundry.data.ItemData["_source"]> | Record<string, unknown> | undefined} ItemUpdateDataGetter */
|
2021-07-08 02:32:25 +02:00
|
|
|
|
2022-11-17 00:12:29 +01:00
|
|
|
/**
|
|
|
|
* Migrate world items.
|
|
|
|
* @param {ItemUpdateDataGetter} getItemUpdateData A function for getting the update data for a given item data object
|
|
|
|
* @returns {Promise<void>} A promise that resolves once the migration is complete
|
|
|
|
*/
|
|
|
|
export async function migrateItems(getItemUpdateData) {
|
2021-07-08 02:32:25 +02:00
|
|
|
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) {
|
2021-09-12 17:48:14 +02:00
|
|
|
logger.error(`Error during migration of Item document ${item.name} (${item.id}), continuing anyways.`, err);
|
2021-07-08 02:32:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-17 00:12:29 +01:00
|
|
|
/** @typedef {(actorData: Partial<foundry.data.ActorData["_source"]>) => DeepPartial<foundry.data.ActorData["_source"]> | undefined} ActorUpdateDataGetter */
|
2021-07-08 02:32:25 +02:00
|
|
|
|
2022-11-17 00:12:29 +01:00
|
|
|
/**
|
|
|
|
* Migrate world actors.
|
|
|
|
* @param {ActorUpdateDataGetter} getActorUpdateData A function for getting the update data for a given actor data object
|
|
|
|
* @returns {Promise<void>} A promise that resolves once the migration is complete
|
|
|
|
*/
|
|
|
|
export async function migrateActors(getActorUpdateData) {
|
2021-07-08 02:32:25 +02:00
|
|
|
for (const actor of getGame().actors ?? []) {
|
|
|
|
try {
|
|
|
|
const updateData = getActorUpdateData(actor.toObject());
|
|
|
|
if (updateData) {
|
2021-07-08 08:00:58 +02:00
|
|
|
logger.info(`Migrating Actor document ${actor.name} (${actor.id})`);
|
2021-07-08 02:32:25 +02:00
|
|
|
await actor.update(updateData);
|
|
|
|
}
|
|
|
|
} catch (err) {
|
2021-09-12 17:48:14 +02:00
|
|
|
logger.error(
|
|
|
|
`Error during migration of Actor document ${actor.name} (${actor.id}), continuing anyways.`,
|
|
|
|
err,
|
|
|
|
);
|
2021-07-08 02:32:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-17 00:12:29 +01:00
|
|
|
/** @typedef {(aceneData: foundry.data.SceneData) => DeepPartial<foundry.data.SceneData["_source"]> | undefined} SceneUpdateDataGetter */
|
2021-07-08 02:32:25 +02:00
|
|
|
|
2022-11-17 00:12:29 +01:00
|
|
|
/**
|
|
|
|
* Migrate world scenes.
|
|
|
|
* @param {SceneUpdateDataGetter} getSceneUpdateData A function for getting the update data for a given scene data object
|
|
|
|
* @returns {Promise<void>} A promise that resolves once the migration is complete
|
|
|
|
*/
|
|
|
|
export async function migrateScenes(getSceneUpdateData) {
|
2021-07-08 02:32:25 +02:00
|
|
|
for (const scene of getGame().scenes ?? []) {
|
|
|
|
try {
|
|
|
|
const updateData = getSceneUpdateData(scene.data);
|
|
|
|
if (updateData) {
|
|
|
|
logger.info(`Migrating Scene document ${scene.name} (${scene.id})`);
|
2022-11-17 00:12:29 +01:00
|
|
|
await scene.update(updateData);
|
2021-07-08 02:32:25 +02:00
|
|
|
}
|
|
|
|
} catch (err) {
|
2021-09-12 17:48:14 +02:00
|
|
|
logger.error(
|
|
|
|
`Error during migration of Scene document ${scene.name} (${scene.id}), continuing anyways.`,
|
|
|
|
err,
|
|
|
|
);
|
2021-07-08 02:32:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-17 00:12:29 +01:00
|
|
|
/** @typedef {(pack: CompendiumCollection) => Promise<void>} CompendiumMigrator*/
|
2021-07-08 02:32:25 +02:00
|
|
|
|
2022-11-17 00:12:29 +01:00
|
|
|
/**
|
|
|
|
* Migrate world compendium packs.
|
|
|
|
* @param {CompendiumMigrator} migrateCompendium A function for migrating a single compendium pack
|
|
|
|
* @returns {Promise<void>} A promise that resolves once the migration is complete
|
|
|
|
*/
|
|
|
|
export async function migrateCompendiums(migrateCompendium) {
|
2021-07-08 02:32:25 +02:00
|
|
|
for (const compendium of getGame().packs ?? []) {
|
|
|
|
if (compendium.metadata.package !== "world") continue;
|
2022-11-04 21:08:23 +01:00
|
|
|
if (!["Actor", "Item", "Scene"].includes(compendium.metadata.type)) continue;
|
2021-07-08 02:32:25 +02:00
|
|
|
await migrateCompendium(compendium);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-17 00:12:29 +01:00
|
|
|
/**
|
|
|
|
* Get a function to create actor update data that adjusts the owned items of the actor according to the given function.
|
|
|
|
* @param {ItemUpdateDataGetter} getItemUpdateData The function to generate item update data
|
|
|
|
* @returns {ActorUpdateDataGetter} A function to get actor update data
|
|
|
|
*/
|
|
|
|
export function getActorUpdateDataGetter(getItemUpdateData) {
|
|
|
|
return (actorData) => {
|
2021-07-08 02:32:25 +02:00
|
|
|
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;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-11-17 00:12:29 +01:00
|
|
|
/**
|
|
|
|
* Get a function to create scene update data that adjusts the actors of the tokens of the scene according to the given function.
|
|
|
|
* @param {ActorUpdateDataGetter} getItemUpdateData The function to generate actor update data
|
|
|
|
* @returns {SceneUpdateDataGetter} A function to get scene update data
|
|
|
|
*/
|
|
|
|
export function getSceneUpdateDataGetter(getActorUpdateData) {
|
|
|
|
return (sceneData) => {
|
|
|
|
const tokens = sceneData.tokens.map((token) => {
|
2021-07-08 02:32:25 +02:00
|
|
|
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) {
|
2022-11-17 00:12:29 +01:00
|
|
|
["items", "effects"].forEach((embeddedName) => {
|
2021-07-08 02:32:25 +02:00
|
|
|
const embeddedUpdates = update[embeddedName];
|
2021-07-08 07:10:34 +02:00
|
|
|
if (embeddedUpdates === undefined || !embeddedUpdates.length) return;
|
2021-07-08 02:32:25 +02:00
|
|
|
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);
|
2021-07-08 07:10:34 +02:00
|
|
|
if (update) foundry.utils.mergeObject(original, update);
|
2021-07-08 02:32:25 +02:00
|
|
|
});
|
|
|
|
delete update[embeddedName];
|
|
|
|
});
|
2021-07-08 07:10:34 +02:00
|
|
|
foundry.utils.mergeObject(t.actorData, update);
|
2021-07-08 02:32:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return t;
|
|
|
|
});
|
|
|
|
return { tokens };
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-11-17 00:12:29 +01:00
|
|
|
/**
|
|
|
|
* @typedef {object} UpdateDataGetters
|
|
|
|
* @property {ItemUpdateDataGetter} [getItemUpdateData]
|
|
|
|
* @property {ActorUpdateDataGetter} [getActorUpdateData]
|
|
|
|
* @property {SceneUpdateDataGetter} [getSceneUpdateData]
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a compendium migrator for the given update data getters.
|
|
|
|
* @param {UpdateDataGetters} [updateDataGetters={}] The functions to use for getting update data
|
|
|
|
* @param {{migrateToTemplateEarly?: boolean}} [options={}] Additional options for the compendium migrator
|
|
|
|
* @returns {CompendiumMigrator} The resulting compendium migrator
|
|
|
|
*/
|
2021-07-08 02:32:25 +02:00
|
|
|
export function getCompendiumMigrator(
|
2022-11-17 00:12:29 +01:00
|
|
|
{ getItemUpdateData, getActorUpdateData, getSceneUpdateData } = {},
|
2021-07-08 02:32:25 +02:00
|
|
|
{ migrateToTemplateEarly = true } = {},
|
|
|
|
) {
|
2022-11-17 00:12:29 +01:00
|
|
|
return async (pack) => {
|
|
|
|
const type = pack.metadata.type;
|
2022-11-04 21:08:23 +01:00
|
|
|
if (!["Actor", "Item", "Scene"].includes(type)) return;
|
2022-11-17 00:12:29 +01:00
|
|
|
const wasLocked = pack.locked;
|
|
|
|
await pack.configure({ locked: false });
|
2021-07-08 02:32:25 +02:00
|
|
|
if (migrateToTemplateEarly) {
|
2022-11-17 00:12:29 +01:00
|
|
|
await pack.migrate();
|
2021-07-08 02:32:25 +02:00
|
|
|
}
|
|
|
|
|
2022-11-17 00:12:29 +01:00
|
|
|
const documents = await pack.getDocuments();
|
2021-07-08 02:32:25 +02:00
|
|
|
|
|
|
|
for (const doc of documents) {
|
|
|
|
try {
|
2022-11-17 00:12:29 +01:00
|
|
|
logger.info(`Migrating document ${doc.name} (${doc.id}) in compendium ${pack.collection}`);
|
2021-07-08 02:32:25 +02:00
|
|
|
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) {
|
2022-11-17 00:12:29 +01:00
|
|
|
const updateData = getSceneUpdateData(doc.data);
|
|
|
|
updateData && (await doc.update(updateData));
|
2021-07-08 02:32:25 +02:00
|
|
|
}
|
|
|
|
} catch (err) {
|
2021-09-12 17:48:14 +02:00
|
|
|
logger.error(
|
2022-11-17 00:12:29 +01:00
|
|
|
`Error during migration of document ${doc.name} (${doc.id}) in compendium ${pack.collection}, continuing anyways.`,
|
2021-09-12 17:48:14 +02:00
|
|
|
err,
|
|
|
|
);
|
2021-07-08 02:32:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!migrateToTemplateEarly) {
|
2022-11-17 00:12:29 +01:00
|
|
|
await pack.migrate();
|
2021-07-08 02:32:25 +02:00
|
|
|
}
|
2022-11-17 00:12:29 +01:00
|
|
|
await pack.configure({ locked: wasLocked });
|
2021-07-08 02:32:25 +02:00
|
|
|
};
|
|
|
|
}
|