175 lines
7.2 KiB
TypeScript
175 lines
7.2 KiB
TypeScript
|
import { DS4Actor } from "../actor/actor";
|
||
|
import { getGame } from "../helpers";
|
||
|
import { DS4Item } from "../item/item";
|
||
|
import logger from "../logger";
|
||
|
|
||
|
type ItemUpdateDataGetter = (
|
||
|
itemData: Partial<foundry.data.ItemData["_source"]>,
|
||
|
) => DeepPartial<foundry.data.ItemData["_source"]> | Record<string, unknown> | undefined;
|
||
|
|
||
|
export async function migrateItems(getItemUpdateData: ItemUpdateDataGetter): Promise<void> {
|
||
|
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<foundry.data.ActorData["_source"]>,
|
||
|
) => DeepPartial<foundry.data.ActorData["_source"]> | undefined;
|
||
|
|
||
|
export async function migrateActors(getActorUpdateData: ActorUpdateDataGetter): Promise<void> {
|
||
|
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<foundry.documents.BaseScene["data"]["_source"]>;
|
||
|
|
||
|
export async function migrateScenes(getSceneUpdateData: SceneUpdateDataGetter): Promise<void> {
|
||
|
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<CompendiumCollection.Metadata>) => Promise<void>;
|
||
|
|
||
|
export async function migrateCompendiums(migrateCompendium: CompendiumMigrator): Promise<void> {
|
||
|
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<foundry.data.ActorData["_source"]>,
|
||
|
): DeepPartial<foundry.data.ActorData["_source"]> | 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<TokenDocument>).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<CompendiumCollection.Metadata>): Promise<void> => {
|
||
|
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 });
|
||
|
};
|
||
|
}
|