From 5235be26260999ae25de0e6d73b5d33735e0e87f Mon Sep 17 00:00:00 2001 From: Matt Potts Date: Wed, 22 Mar 2023 10:30:20 +0000 Subject: [PATCH] docs: Latest release (#109) --- docs/api.md | 122 ++++++++++++++++-- .../componentManager/ComponentsBrowser.svelte | 4 +- src/scripts/foundry/DocumentManager.ts | 3 +- src/scripts/system/ComponentDictionary.ts | 24 +++- src/scripts/system/CraftingSystem.ts | 4 + src/scripts/system/PartDictionary.ts | 15 ++- test/PartDictionary.test.ts | 3 +- 7 files changed, 160 insertions(+), 15 deletions(-) diff --git a/docs/api.md b/docs/api.md index dcb7c9f9..f433fdb9 100644 --- a/docs/api.md +++ b/docs/api.md @@ -254,6 +254,11 @@ type SalvageOptionJson = Record; To create one, get the crafting system you want to add it to, and call `CraftingSystem#createComponent`. Then, save the crafting system. +{: .highlight } +> If your crafting system has components and recipes already defined, make sure that you **load the part dictionary** +> before you save the crafting system. Attempts to save a crafting system that has not had its part dictionary loaded +> will produce an error. This area of the API is _likely to change_ in future. +
Add a component to a crafting system @@ -271,6 +276,7 @@ const myComponent = { const myCraftingSystemId = "8Bn1VBc6noSUeKlG"; // replace this with your own crafting system ID const craftingSystem = await game.fabricate.SystemRegistry.getCraftingSystemById(myCraftingSystemId); +await craftingSystem.loadPartDictionary() // <-- Do this before modifying systems that already have components const createdComponent = await craftingSystem.createComponent(myComponent); await game.fabricate.SystemRegistry.saveCraftingSystem(craftingSystem); console.log(`Created component with ID "${createdComponent.id}"`); // <-- You'll need this to edit the component @@ -278,16 +284,116 @@ console.log(`Created component with ID "${createdComponent.id}"`); // <-- You'll
+## Accessing a component + +To access a component, you will need to: + +1. Get the crafting system it belongs to +2. Load the part dictionary for that system +3. Get the component by ID from the crafting system + +
+ +Accessing a component + + +```js +const myCraftingSystemId = "8Bn1VBc6noSUeKlG"; // replace this with your own crafting system ID +const craftingSystem = await game.fabricate.SystemRegistry.getCraftingSystemById(myCraftingSystemId); +// Fabricate currently requires you to load essences, components and recipes into memory before you can act on them +// **This area of the API is likely to change** in future as I move towards loading individual parts on demand +await craftingSystem.loadPartDictionary(); +const myComponentId = "T3zD0zrcd0otXXwB"; // replace this with the ID of the component you want to get +const myComponent = craftingSystem.getComponentById(myComponentId); +``` + +
+ ## Editing a component -{: .highlight } -> The docs haven't caught up with the code here just yet. -> The API exists, if you want to [dig around in the sources](http://github.com/misterpotts/fabricate) and experiment. -> Alternatively, you can wait for these docs to be updated. +To edit a component, you will need to: + +1. [Access the component](#accessing-a-component) +2. Serialize it +3. Modify the serialized data +4. Call `CraftingSystem#mutateComponent` with the component ID and the modified data +5. Save the crafting system + +
+ +CraftingSystem#mutateComponent + + +```typescript + /** + * Modifies an existing component by applying the mutations defined in the supplied `CraftingComponentJson` + * + * @param id The ID of the component to modify + * @param the complete target state of the component to apply. Anything you omit is deleted + * @return a Promise that resolves to the updated crafting component + * + * @throws an Error if the ID is not for an existing component + * @throws an Error if the the component dictionary has not been loaded + * @throws an Error if the mutation contains an invalid item UUID + * @throws an Error if the mutation references essences orcomponents that do not exist + * */ + mutateComponent(id: string, mutation: CraftingComponentJson): Promise; +``` + +
+ +
+ +Modifying a component + + +```js +// Access the component as described above +const myComponent = craftingSystem.getComponentById(myComponentId); +console.log(`Before modification: ${myComponent}`); +const componentData = myComponent.toJson(); +componentData.salvageOptions["My new Salvage Option"] = { "rLP3cTCTnQsxddDt": 1 } // Adds a new salvage option +// perform any other modifications you want here. You can alter any of the fields in the `CraftingComponentJson`, +// including the item UUID. The new values meet the requirements specified in the interface. +const updatedComponent = await craftingSystem.mutateComponent(myComponent.id, componentData); +await game.fabricate.SystemRegistry.saveCraftingSystem(craftingSystem); +console.log(`After modification: ${updatedComponent}`); +``` + +
## Deleting a component -{: .highlight } -> The docs haven't caught up with the code here just yet. -> The API exists, if you want to [dig around in the sources](http://github.com/misterpotts/fabricate) and experiment. -> Alternatively, you can wait for these docs to be updated. \ No newline at end of file +Deleting a component requires only that you load the crafting system, delete the component using its ID by calling +`CraftingSystem#deleteComponentById` then save the crafting system. + +
+ +CraftingSystem#deleteComponentById + + +```typescript + /** + * Deletes a crafting component by ID + * + * @param the ID of the component to delete + * */ + deleteComponentById(id: string): void; +``` + +
+ +
+ +Modifying a component + + +```js +// Obtain the component ID and load the crafting system part dictionary beforehand +console.log(`Before deletion, CraftingSystem#hasComponent is ${craftingSystem.hasComponent(myComponentId)}`); +craftingSystem.deleteComponentById(myComponentId); +await game.fabricate.SystemRegistry.saveCraftingSystem(craftingSystem); +console.log(`After deletion, CraftingSystem#hasComponent is ${craftingSystem.hasComponent(myComponentId)}`); +``` + +
\ No newline at end of file diff --git a/src/applications/craftingSystemManagerApp/componentManager/ComponentsBrowser.svelte b/src/applications/craftingSystemManagerApp/componentManager/ComponentsBrowser.svelte index 3c09cb65..2b76493c 100644 --- a/src/applications/craftingSystemManagerApp/componentManager/ComponentsBrowser.svelte +++ b/src/applications/craftingSystemManagerApp/componentManager/ComponentsBrowser.svelte @@ -42,8 +42,8 @@ component.isDisabled = true; $loading = true; await craftingComponentEditor.saveComponent(component, $selectedCraftingSystem); - const message = this._localizationService.format( - `${this._localizationPath}.component.disabled`, + const message = localization.format( + `${localizationPath}.component.disabled`, { componentName: component.name } diff --git a/src/scripts/foundry/DocumentManager.ts b/src/scripts/foundry/DocumentManager.ts index 8e4e25b2..30cc9d13 100644 --- a/src/scripts/foundry/DocumentManager.ts +++ b/src/scripts/foundry/DocumentManager.ts @@ -157,7 +157,8 @@ class PendingFabricateItemData implements FabricateItemData { } toJson(): FabricateItemDataJson { - throw new Error("Pending item data should never be serialised. "); + console.warn("Serialising pending item data. The item UUID has not been checked and may not be valid. "); + return { uuid: this._itemUuid }; } } diff --git a/src/scripts/system/ComponentDictionary.ts b/src/scripts/system/ComponentDictionary.ts index a66eb2b0..59b03cca 100644 --- a/src/scripts/system/ComponentDictionary.ts +++ b/src/scripts/system/ComponentDictionary.ts @@ -1,6 +1,10 @@ import {Dictionary} from "./Dictionary"; import {CraftingComponent, CraftingComponentJson, SalvageOption, SalvageOptionJson} from "../common/CraftingComponent"; -import {DocumentManager, FabricateItemData, NoFabricateItemData} from "../foundry/DocumentManager"; +import { + DocumentManager, + FabricateItemData, + NoFabricateItemData +} from "../foundry/DocumentManager"; import {EssenceDictionary} from "./EssenceDictionary"; import {combinationFromRecord} from "./DictionaryUtils"; import {SelectableOptions} from "../common/SelectableOptions"; @@ -245,4 +249,22 @@ export class ComponentDictionary implements Dictionary { + if (!this._loaded) { + throw new Error("Fabricate doesn't currently support modifying components before the component dictionary has been loaded. "); + } + if (!this._entriesById.has(id)) { + throw new Error(`Unable to mutate component with ID ${id}. It doesn't exist.`); + } + const target = this._entriesById.get(id); + const itemData = target.itemUuid === mutation.itemUuid ? target.itemData : await this._documentManager.getDocumentByUuid(mutation.itemUuid); + if (itemData.hasErrors) { + throw new Error(`Could not load document with UUID "${mutation.itemUuid}". Errors ${itemData.errors.join(", ")} `); + } + const result = this.buildComponent(target.id, mutation, itemData); + this.insert(result); + return result; + } + } \ No newline at end of file diff --git a/src/scripts/system/CraftingSystem.ts b/src/scripts/system/CraftingSystem.ts index 408240f6..3f09ef4c 100644 --- a/src/scripts/system/CraftingSystem.ts +++ b/src/scripts/system/CraftingSystem.ts @@ -128,6 +128,10 @@ class CraftingSystem { return this._partDictionary.insertComponent(component); } + public async mutateComponent(id: string, mutation: CraftingComponentJson): Promise { + return await this._partDictionary.mutateComponent(id, mutation); + } + public getRecipes(): Recipe[] { return this._partDictionary.getRecipes(); } diff --git a/src/scripts/system/PartDictionary.ts b/src/scripts/system/PartDictionary.ts index 5640b9ee..34a8a0ad 100644 --- a/src/scripts/system/PartDictionary.ts +++ b/src/scripts/system/PartDictionary.ts @@ -90,6 +90,10 @@ class PartDictionary { this._componentDictionary.insert(craftingComponent); } + async mutateComponent(id: string, mutation: CraftingComponentJson): Promise { + return this._componentDictionary.mutate(id, mutation); + } + public insertRecipe(recipe: Recipe): void { this._recipeDictionary.insert(recipe); } @@ -125,7 +129,7 @@ class PartDictionary { await this._componentDictionary.loadAll(); await this._recipeDictionary.loadAll(); } - + async loadEssences(updatedSource?: Record): Promise { if (updatedSource) { this._essenceDictionary.sourceData = updatedSource; @@ -148,6 +152,9 @@ class PartDictionary { } public toJson(): PartDictionaryJson { + if (!this.isLoaded) { + throw new Error("Fabricate currently requires that a part dictionary is loaded before it is serialized and saved. "); + } const essences = this._essenceDictionary.toJson(); const components = this._componentDictionary.toJson(); const recipes = this._recipeDictionary.toJson(); @@ -193,6 +200,7 @@ class PartDictionary { async createEssence(essenceJson: EssenceJson): Promise { return this._essenceDictionary.create(essenceJson); } + } class PartDictionaryFactory { @@ -208,7 +216,10 @@ class PartDictionaryFactory { make(sourceData: PartDictionaryJson): PartDictionary { const documentManager = this._documentManager; - const essenceDictionary = new EssenceDictionary({sourceData: sourceData.essences, documentManager}); + const essenceDictionary = new EssenceDictionary({ + sourceData: sourceData.essences, + documentManager + }); const componentDictionary = new ComponentDictionary({ sourceData: sourceData.components, documentManager, diff --git a/test/PartDictionary.test.ts b/test/PartDictionary.test.ts index fff88916..edd5e765 100644 --- a/test/PartDictionary.test.ts +++ b/test/PartDictionary.test.ts @@ -26,7 +26,7 @@ import {LoadedFabricateItemData} from "../src/scripts/foundry/DocumentManager"; describe("Create", () => { - test("Should construct empty Part Dictionary", () => { + test("Should construct empty Part Dictionary", async () => { const underTest: PartDictionary = new PartDictionaryFactory({}) .make({ essences: {}, @@ -41,6 +41,7 @@ describe("Create", () => { expect(underTest.hasComponent(id)).toBe(false); expect(underTest.hasRecipe(id)).toBe(false); + await underTest.loadAll(); const asJson = underTest.toJson(); expect(Object.keys(asJson.essences).length).toEqual(0); expect(Object.keys(asJson.recipes).length).toEqual(0);