@adeficior/data-modifier-recipes 2.0.0-rc.20260814115618
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -0
- package/.turbo/turbo-check.log +1 -0
- package/.turbo/turbo-lint.log +1 -0
- package/@types/modules.d.ts +15 -0
- package/@types/registry.d.ts +20 -0
- package/CHANGELOG.md +14 -0
- package/dist/emitter.d.ts +48 -0
- package/dist/emitter.js +116 -0
- package/dist/emitter.js.map +1 -0
- package/dist/hooks.d.ts +6 -0
- package/dist/hooks.js +4 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/loader.d.ts +27 -0
- package/dist/loader.js +141 -0
- package/dist/loader.js.map +1 -0
- package/dist/module.d.ts +15 -0
- package/dist/module.js +38 -0
- package/dist/module.js.map +1 -0
- package/dist/rule.d.ts +15 -0
- package/dist/rule.js +31 -0
- package/dist/rule.js.map +1 -0
- package/dist/schema.d.ts +7 -0
- package/dist/schema.js +14 -0
- package/dist/schema.js.map +1 -0
- package/dist/serializer/abstract.d.ts +17 -0
- package/dist/serializer/abstract.js +19 -0
- package/dist/serializer/abstract.js.map +1 -0
- package/dist/serializer/context.d.ts +12 -0
- package/dist/serializer/context.js +4 -0
- package/dist/serializer/context.js.map +1 -0
- package/dist/serializer/forge/conditional.d.ts +25 -0
- package/dist/serializer/forge/conditional.js +45 -0
- package/dist/serializer/forge/conditional.js.map +1 -0
- package/dist/serializer/holder.d.ts +25 -0
- package/dist/serializer/holder.js +52 -0
- package/dist/serializer/holder.js.map +1 -0
- package/dist/serializer/manyToMany.d.ts +21 -0
- package/dist/serializer/manyToMany.js +37 -0
- package/dist/serializer/manyToMany.js.map +1 -0
- package/dist/serializer/manyToOne.d.ts +21 -0
- package/dist/serializer/manyToOne.js +37 -0
- package/dist/serializer/manyToOne.js.map +1 -0
- package/dist/serializer/modifier.d.ts +6 -0
- package/dist/serializer/modifier.js +3 -0
- package/dist/serializer/modifier.js.map +1 -0
- package/dist/serializer/oneToOne.d.ts +21 -0
- package/dist/serializer/oneToOne.js +37 -0
- package/dist/serializer/oneToOne.js.map +1 -0
- package/dist/serializer/vanilla/shaped.d.ts +22 -0
- package/dist/serializer/vanilla/shaped.js +37 -0
- package/dist/serializer/vanilla/shaped.js.map +1 -0
- package/dist/serializer/vanilla/shapeless.d.ts +4 -0
- package/dist/serializer/vanilla/shapeless.js +4 -0
- package/dist/serializer/vanilla/shapeless.js.map +1 -0
- package/dist/serializer/vanilla/smelting.d.ts +22 -0
- package/dist/serializer/vanilla/smelting.js +37 -0
- package/dist/serializer/vanilla/smelting.js.map +1 -0
- package/dist/serializer/vanilla/smithing.d.ts +25 -0
- package/dist/serializer/vanilla/smithing.js +46 -0
- package/dist/serializer/vanilla/smithing.js.map +1 -0
- package/dist/serializer/vanilla/stonecutting.d.ts +22 -0
- package/dist/serializer/vanilla/stonecutting.js +38 -0
- package/dist/serializer/vanilla/stonecutting.js.map +1 -0
- package/eslint.config.js +8 -0
- package/package.json +42 -0
- package/src/emitter.ts +239 -0
- package/src/hooks.ts +10 -0
- package/src/index.ts +18 -0
- package/src/loader.ts +219 -0
- package/src/module.ts +70 -0
- package/src/rule.ts +45 -0
- package/src/schema.ts +30 -0
- package/src/serializer/abstract.ts +41 -0
- package/src/serializer/context.ts +17 -0
- package/src/serializer/forge/conditional.ts +72 -0
- package/src/serializer/holder.ts +63 -0
- package/src/serializer/manyToMany.ts +62 -0
- package/src/serializer/manyToOne.ts +62 -0
- package/src/serializer/modifier.ts +10 -0
- package/src/serializer/oneToOne.ts +60 -0
- package/src/serializer/vanilla/shaped.ts +65 -0
- package/src/serializer/vanilla/shapeless.ts +8 -0
- package/src/serializer/vanilla/smelting.ts +62 -0
- package/src/serializer/vanilla/smithing.ts +74 -0
- package/src/serializer/vanilla/stonecutting.ts +63 -0
- package/src/testing/index.ts +1 -0
- package/src/testing/setup.ts +85 -0
- package/test/1.20.1/__snapshots__/emitter.test.ts.snap +122 -0
- package/test/1.20.1/__snapshots__/loader.test.ts.snap +215 -0
- package/test/1.20.1/emitter.test.ts +222 -0
- package/test/1.20.1/loader.test.ts +17 -0
- package/test/1.21.1/loader.test.ts +17 -0
- package/test/1.21.1/packFormat.test.ts +52 -0
- package/test/resources/1.20.1/default/custom/data/custom/recipes/conditional.json +25 -0
- package/tsconfig.build.json +14 -0
- package/tsconfig.json +34 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smithing.js","sourceRoot":"","sources":["../../../src/serializer/vanilla/smithing.ts"],"names":[],"mappings":"AAAA,OAAO,EAGN,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACnD,OAAO,EAAyB,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAA2B,MAAM,YAAY,CAAC;AACrD,OAAO,EAAuB,MAAM,aAAa,CAAC;AAUlD,MAAM,OAAO,cAAe,SAAQ,MAAM;IAErB;IACA;IACA;IACA;IAJnB,YACmB,IAAgB,EAChB,QAAoB,EACpB,MAA0B,EAC1B,QAAgC;QAEjD,KAAK,EAAE,CAAC;QALS,SAAI,GAAJ,IAAI,CAAY;QAChB,aAAQ,GAAR,QAAQ,CAAY;QACpB,WAAM,GAAN,MAAM,CAAoB;QAC1B,aAAQ,GAAR,QAAQ,CAAwB;IAGnD,CAAC;IAED,cAAc;QACZ,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACnE,CAAC;IAED,UAAU;QACR,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACvC,CAAC;IAEQ,MAAM,CAAC,QAAwB;QACtC,OAAO,IAAI,cAAc,CACvB,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAC9B,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,EAClC,IAAI,CAAC,MAAM,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAC3C,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CACpD,CAAC;IACJ,CAAC;IAEQ,SAAS,CAChB,OAA2B;QAE3B,OAAO;YACL,IAAI,EAAE,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;YAC9C,QAAQ,EAAE,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC;YACtD,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC;YACtD,QAAQ,EAAE,OAAO,CAAC,WAAW,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC;SAC/D,CAAC;IACJ,CAAC;CACF;AAED,MAAM,OAAO,cAAe,SAAQ,YAGnC;IACC,WAAW,CACT,UAAoC,EACpC,OAA2B;QAE3B,MAAM,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAC9D,MAAM,QAAQ,GAAG,OAAO,CAAC,WAAW,CAAC,WAAW,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QACtE,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,mBAAmB,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QACtE,MAAM,QAAQ,GAAG,OAAO,CAAC,WAAW,CAAC,mBAAmB,CACtD,UAAU,CAAC,QAAQ,CACpB,CAAC;QACF,OAAO,IAAI,cAAc,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC9D,CAAC;CACF"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type Ingredient, type Result } from "@adeficior/data-modifier-ingredients";
|
|
2
|
+
import { type RecipeDefinition } from "../../schema";
|
|
3
|
+
import { Recipe, RecipeParser } from "../abstract";
|
|
4
|
+
import { type RecipeParseContext } from "../context";
|
|
5
|
+
import { type RecipeModifier } from "../modifier";
|
|
6
|
+
export type StonecuttingRecipeDefinition = RecipeDefinition & Readonly<{
|
|
7
|
+
ingredient: unknown;
|
|
8
|
+
result: unknown;
|
|
9
|
+
count?: number;
|
|
10
|
+
}>;
|
|
11
|
+
export declare class StonecuttingRecipe extends Recipe {
|
|
12
|
+
private readonly ingredient;
|
|
13
|
+
private readonly result;
|
|
14
|
+
constructor(ingredient: Ingredient, result: Result);
|
|
15
|
+
getIngredients(): Ingredient[];
|
|
16
|
+
getResults(): Result[];
|
|
17
|
+
modify(modifier: RecipeModifier): StonecuttingRecipe;
|
|
18
|
+
serialize(context: RecipeParseContext): Partial<StonecuttingRecipeDefinition>;
|
|
19
|
+
}
|
|
20
|
+
export declare class StonecuttingParser extends RecipeParser<StonecuttingRecipeDefinition, StonecuttingRecipe> {
|
|
21
|
+
deserialize(definition: StonecuttingRecipeDefinition, context: RecipeParseContext): StonecuttingRecipe;
|
|
22
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {} from "@adeficior/data-modifier-ingredients";
|
|
2
|
+
import {} from "../../schema";
|
|
3
|
+
import { Recipe, RecipeParser } from "../abstract";
|
|
4
|
+
import {} from "../context";
|
|
5
|
+
import {} from "../modifier";
|
|
6
|
+
// TODO could also be SmeltingRecipe?
|
|
7
|
+
export class StonecuttingRecipe extends Recipe {
|
|
8
|
+
ingredient;
|
|
9
|
+
result;
|
|
10
|
+
constructor(ingredient, result) {
|
|
11
|
+
super();
|
|
12
|
+
this.ingredient = ingredient;
|
|
13
|
+
this.result = result;
|
|
14
|
+
}
|
|
15
|
+
getIngredients() {
|
|
16
|
+
return [this.ingredient];
|
|
17
|
+
}
|
|
18
|
+
getResults() {
|
|
19
|
+
return [this.result];
|
|
20
|
+
}
|
|
21
|
+
modify(modifier) {
|
|
22
|
+
return new StonecuttingRecipe(modifier.ingredient(this.ingredient), modifier.result(this.result));
|
|
23
|
+
}
|
|
24
|
+
serialize(context) {
|
|
25
|
+
return {
|
|
26
|
+
ingredient: context.ingredients.serialize(this.ingredient),
|
|
27
|
+
result: context.results.serialize(this.result),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export class StonecuttingParser extends RecipeParser {
|
|
32
|
+
deserialize(definition, context) {
|
|
33
|
+
const ingredient = context.ingredients.deserialize(definition.ingredient);
|
|
34
|
+
const result = context.results.deserialize(definition.result);
|
|
35
|
+
return new StonecuttingRecipe(ingredient, result);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=stonecutting.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stonecutting.js","sourceRoot":"","sources":["../../../src/serializer/vanilla/stonecutting.ts"],"names":[],"mappings":"AAAA,OAAO,EAGN,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAyB,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAA2B,MAAM,YAAY,CAAC;AACrD,OAAO,EAAuB,MAAM,aAAa,CAAC;AASlD,qCAAqC;AACrC,MAAM,OAAO,kBAAmB,SAAQ,MAAM;IAEzB;IACA;IAFnB,YACmB,UAAsB,EACtB,MAAc;QAE/B,KAAK,EAAE,CAAC;QAHS,eAAU,GAAV,UAAU,CAAY;QACtB,WAAM,GAAN,MAAM,CAAQ;IAGjC,CAAC;IAED,cAAc;QACZ,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC3B,CAAC;IAED,UAAU;QACR,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACvB,CAAC;IAEQ,MAAM,CAAC,QAAwB;QACtC,OAAO,IAAI,kBAAkB,CAC3B,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,EACpC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAC7B,CAAC;IACJ,CAAC;IAEQ,SAAS,CAChB,OAA2B;QAE3B,OAAO;YACL,UAAU,EAAE,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC;YAC1D,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC;SAC/C,CAAC;IACJ,CAAC;CACF;AAED,MAAM,OAAO,kBAAmB,SAAQ,YAGvC;IACC,WAAW,CACT,UAAwC,EACxC,OAA2B;QAE3B,MAAM,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,WAAW,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;QAC1E,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QAC9D,OAAO,IAAI,kBAAkB,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IACpD,CAAC;CACF"}
|
package/eslint.config.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@adeficior/data-modifier-recipes",
|
|
3
|
+
"version": "2.0.0-rc.20260814115618",
|
|
4
|
+
"exports": {
|
|
5
|
+
".": "./dist/index.js",
|
|
6
|
+
"./testing": "./dist/testing/index.js"
|
|
7
|
+
},
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"typesVersions": {
|
|
10
|
+
"*": {
|
|
11
|
+
"testing": [
|
|
12
|
+
"./dist/testing/index.d.ts"
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"type": "module",
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc --project tsconfig.build.json",
|
|
19
|
+
"check": "tsc --noEmit",
|
|
20
|
+
"dev": "bun run build --watch",
|
|
21
|
+
"lint": "eslint {src,test}",
|
|
22
|
+
"test": "bun test --timeout 10000",
|
|
23
|
+
"prune": "bun run datamod-dev --prune",
|
|
24
|
+
"prepare": "datamod-dev --configs && datamod-dev --types",
|
|
25
|
+
"test:ci": "bun run lint && bun run check && bun run test --coverage"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@adeficior/configs": "workspace:*",
|
|
29
|
+
"@adeficior/testing": "workspace:*",
|
|
30
|
+
"@types/lodash-es": "catalog:",
|
|
31
|
+
"@adeficior/data-modifier-ingredients": "workspace:*"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"@adeficior/pack-resolver": "catalog:",
|
|
35
|
+
"typescript": "catalog:",
|
|
36
|
+
"minimatch": "catalog:",
|
|
37
|
+
"lodash-es": "catalog:",
|
|
38
|
+
"@adeficior/data-modifier-core": "workspace:*",
|
|
39
|
+
"@adeficior/data-modifier-ingredients": "workspace:*",
|
|
40
|
+
"@adeficior/data-modifier-tags": "workspace:*"
|
|
41
|
+
}
|
|
42
|
+
}
|
package/src/emitter.ts
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CustomEmitter,
|
|
3
|
+
encodeId,
|
|
4
|
+
RuledEmitter,
|
|
5
|
+
withDisabledConditions,
|
|
6
|
+
type ClearableEmitter,
|
|
7
|
+
type Id,
|
|
8
|
+
type IdInput,
|
|
9
|
+
type LoaderContext,
|
|
10
|
+
type Modifier,
|
|
11
|
+
type NormalizedId,
|
|
12
|
+
type RegistryProvider,
|
|
13
|
+
type SemVerInput,
|
|
14
|
+
} from "@adeficior/data-modifier-core";
|
|
15
|
+
import {
|
|
16
|
+
createReplacer,
|
|
17
|
+
resolveIdTest,
|
|
18
|
+
type CommonFilter,
|
|
19
|
+
type Predicate,
|
|
20
|
+
} from "@adeficior/data-modifier-core/serializer";
|
|
21
|
+
import {
|
|
22
|
+
type Ingredient,
|
|
23
|
+
type IngredientFilter,
|
|
24
|
+
type IngredientInput,
|
|
25
|
+
type IngredientSerializer,
|
|
26
|
+
type Predicates,
|
|
27
|
+
type Result,
|
|
28
|
+
type ResultInput,
|
|
29
|
+
type ResultSerializer,
|
|
30
|
+
} from "@adeficior/data-modifier-ingredients";
|
|
31
|
+
import { type RecipeSerializerId } from "@adeficior/data-modifier/generated";
|
|
32
|
+
import {
|
|
33
|
+
combineResolvers,
|
|
34
|
+
notNull,
|
|
35
|
+
type ContextLike,
|
|
36
|
+
type Logger,
|
|
37
|
+
} from "@adeficior/pack-resolver";
|
|
38
|
+
import { RecipeRule } from "./rule";
|
|
39
|
+
import { recipePath, type RecipeDefinition } from "./schema";
|
|
40
|
+
import { type Recipe } from "./serializer/abstract";
|
|
41
|
+
import { type RecipeSerializer } from "./serializer/context";
|
|
42
|
+
import { RecipeHolder } from "./serializer/holder";
|
|
43
|
+
|
|
44
|
+
export type RecipeTest = Readonly<{
|
|
45
|
+
id?: CommonFilter<NormalizedId>;
|
|
46
|
+
type?: CommonFilter<NormalizedId<RecipeSerializerId>>;
|
|
47
|
+
namespace?: string;
|
|
48
|
+
output?: IngredientFilter;
|
|
49
|
+
input?: IngredientFilter;
|
|
50
|
+
// TODO not sure if I want to even keep this?
|
|
51
|
+
optional?: boolean;
|
|
52
|
+
}>;
|
|
53
|
+
|
|
54
|
+
export interface RecipeRules {
|
|
55
|
+
replaceResult(
|
|
56
|
+
test: IngredientFilter,
|
|
57
|
+
value: ResultInput,
|
|
58
|
+
additionalTests?: RecipeTest,
|
|
59
|
+
): void;
|
|
60
|
+
|
|
61
|
+
replaceIngredient(
|
|
62
|
+
test: IngredientFilter,
|
|
63
|
+
value: IngredientInput,
|
|
64
|
+
additionalTests?: RecipeTest,
|
|
65
|
+
): void;
|
|
66
|
+
|
|
67
|
+
add(id: IdInput, value: RecipeDefinition): void;
|
|
68
|
+
add(id: IdInput, value: RecipeHolder): void;
|
|
69
|
+
add(id: IdInput, type: NormalizedId<RecipeSerializerId>, value: Recipe): void;
|
|
70
|
+
|
|
71
|
+
remove(test: RecipeTest): void;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export const EMPTY_RECIPE: RecipeDefinition = withDisabledConditions({
|
|
75
|
+
type: "minecraft:disabled",
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
export class RecipeEmitter implements RecipeRules, ClearableEmitter {
|
|
79
|
+
private readonly custom = new CustomEmitter<RecipeDefinition>((it) =>
|
|
80
|
+
recipePath(this.packFormat, it),
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
private readonly ruled: RuledEmitter<RecipeHolder, RecipeRule>;
|
|
84
|
+
|
|
85
|
+
constructor(
|
|
86
|
+
// TODO inject
|
|
87
|
+
private readonly logger: Logger,
|
|
88
|
+
private readonly packFormat: SemVerInput,
|
|
89
|
+
private readonly registry: RegistryProvider<RecipeHolder>,
|
|
90
|
+
private readonly resultSerializer: ResultSerializer,
|
|
91
|
+
private readonly ingredientSerializer: IngredientSerializer,
|
|
92
|
+
private readonly predicates: Predicates,
|
|
93
|
+
private readonly serializer: RecipeSerializer,
|
|
94
|
+
) {
|
|
95
|
+
this.ruled = new RuledEmitter<RecipeHolder, RecipeRule>(
|
|
96
|
+
this.registry,
|
|
97
|
+
(id) => recipePath(packFormat, id),
|
|
98
|
+
EMPTY_RECIPE,
|
|
99
|
+
(it) => this.serializer.serialize(it),
|
|
100
|
+
(id) => this.custom.has(id),
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
resolver(context: LoaderContext) {
|
|
105
|
+
return combineResolvers(
|
|
106
|
+
[this.ruled.resolver(context), this.custom.resolver(context)],
|
|
107
|
+
{ async: true },
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private createIngredientPredicate(filter?: IngredientFilter) {
|
|
112
|
+
if (!filter) return () => true;
|
|
113
|
+
return this.predicates.ingredient(filter);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private createResultPredicate(filter?: IngredientFilter) {
|
|
117
|
+
if (!filter) return () => true;
|
|
118
|
+
return this.predicates.result(filter);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
private resolveRecipeTest(test: RecipeTest) {
|
|
122
|
+
const id: Predicate<Id>[] = [];
|
|
123
|
+
const type: Predicate<Id>[] = [];
|
|
124
|
+
const ingredient: Predicate<Ingredient>[] = [];
|
|
125
|
+
const result: Predicate<Result>[] = [];
|
|
126
|
+
|
|
127
|
+
if (test.id) id.push(resolveIdTest(test.id));
|
|
128
|
+
if (test.type) type.push(resolveIdTest(test.type));
|
|
129
|
+
if (test.namespace) id.push((id) => id.namespace === test.namespace);
|
|
130
|
+
if (test.output) result.push(this.createResultPredicate(test.output));
|
|
131
|
+
if (test.input) ingredient.push(this.createIngredientPredicate(test.input));
|
|
132
|
+
|
|
133
|
+
return { id, type, ingredient, result };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
add(
|
|
137
|
+
id: IdInput,
|
|
138
|
+
arg: RecipeDefinition | RecipeHolder | NormalizedId<RecipeSerializerId>,
|
|
139
|
+
arg2?: Recipe,
|
|
140
|
+
) {
|
|
141
|
+
if (typeof arg === "string") {
|
|
142
|
+
const type = arg;
|
|
143
|
+
const recipe = arg2!;
|
|
144
|
+
this.add(id, new RecipeHolder({ type }, recipe));
|
|
145
|
+
} else {
|
|
146
|
+
const value = arg;
|
|
147
|
+
|
|
148
|
+
if (this.custom.has(id))
|
|
149
|
+
this.logger.error(`Overwriting custom recipe with ID ${encodeId(id)}`);
|
|
150
|
+
|
|
151
|
+
if (value instanceof RecipeHolder)
|
|
152
|
+
this.custom.add(id, this.serializer.serialize(value));
|
|
153
|
+
else this.custom.add(id, value);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private addRule(
|
|
158
|
+
context: ContextLike,
|
|
159
|
+
modifier: Modifier<RecipeHolder>,
|
|
160
|
+
recipeTest: RecipeTest = {},
|
|
161
|
+
ingredientTests: {
|
|
162
|
+
ingredient?: Predicate<Ingredient>;
|
|
163
|
+
result?: Predicate<Result>;
|
|
164
|
+
} = {},
|
|
165
|
+
) {
|
|
166
|
+
const recipePredicates = this.resolveRecipeTest(recipeTest ?? {});
|
|
167
|
+
|
|
168
|
+
this.ruled.addRule(
|
|
169
|
+
new RecipeRule(
|
|
170
|
+
context,
|
|
171
|
+
recipePredicates.id,
|
|
172
|
+
recipePredicates.type,
|
|
173
|
+
[ingredientTests.ingredient, ...recipePredicates.ingredient].filter(
|
|
174
|
+
notNull,
|
|
175
|
+
),
|
|
176
|
+
[ingredientTests.result, ...recipePredicates.result].filter(notNull),
|
|
177
|
+
modifier,
|
|
178
|
+
),
|
|
179
|
+
recipeTest.optional !== true,
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
remove(test: RecipeTest) {
|
|
184
|
+
this.addRule({ operation: "remove", test }, () => null, test);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
replaceResult(
|
|
188
|
+
test: IngredientFilter,
|
|
189
|
+
input: ResultInput,
|
|
190
|
+
additionalTest?: RecipeTest,
|
|
191
|
+
) {
|
|
192
|
+
const predicate = this.createResultPredicate(test);
|
|
193
|
+
|
|
194
|
+
const value = this.resultSerializer.deserialize(input);
|
|
195
|
+
|
|
196
|
+
const replace = createReplacer(predicate, value);
|
|
197
|
+
|
|
198
|
+
this.addRule(
|
|
199
|
+
{
|
|
200
|
+
operation: "replace result",
|
|
201
|
+
from: test,
|
|
202
|
+
to: value,
|
|
203
|
+
test: additionalTest,
|
|
204
|
+
},
|
|
205
|
+
(recipe) => recipe.replaceResult(replace),
|
|
206
|
+
additionalTest,
|
|
207
|
+
{ result: predicate },
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
replaceIngredient(
|
|
212
|
+
test: IngredientFilter,
|
|
213
|
+
input: IngredientInput,
|
|
214
|
+
additionalTest?: RecipeTest,
|
|
215
|
+
) {
|
|
216
|
+
const predicate = this.createIngredientPredicate(test);
|
|
217
|
+
|
|
218
|
+
const value = this.ingredientSerializer.deserialize(input);
|
|
219
|
+
|
|
220
|
+
const replace = createReplacer(predicate, value);
|
|
221
|
+
|
|
222
|
+
this.addRule(
|
|
223
|
+
{
|
|
224
|
+
operation: "replace ingredient",
|
|
225
|
+
from: test,
|
|
226
|
+
to: value,
|
|
227
|
+
test: additionalTest,
|
|
228
|
+
},
|
|
229
|
+
(recipe) => recipe.replaceIngredient(replace),
|
|
230
|
+
additionalTest,
|
|
231
|
+
{ ingredient: predicate },
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
clear() {
|
|
236
|
+
this.custom.clear();
|
|
237
|
+
this.ruled.clear();
|
|
238
|
+
}
|
|
239
|
+
}
|
package/src/hooks.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type RecipeSerializerId } from "@adeficior/data-modifier/generated";
|
|
2
|
+
import { type RecipeDefinition } from "./schema";
|
|
3
|
+
import { type Recipe, type RecipeParser } from "./serializer/abstract";
|
|
4
|
+
|
|
5
|
+
export type RegisterRecipeParser = {
|
|
6
|
+
register(
|
|
7
|
+
recipeType: RecipeSerializerId,
|
|
8
|
+
parser: RecipeParser<RecipeDefinition, Recipe>,
|
|
9
|
+
): void;
|
|
10
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export { EMPTY_RECIPE, type RecipeRules, type RecipeTest } from "./emitter";
|
|
2
|
+
export type * from "./hooks";
|
|
3
|
+
export type { RecipeLoaderAccessor } from "./loader";
|
|
4
|
+
export { default } from "./module";
|
|
5
|
+
export * from "./schema";
|
|
6
|
+
export * from "./serializer/abstract";
|
|
7
|
+
export type * from "./serializer/context";
|
|
8
|
+
export * from "./serializer/forge/conditional";
|
|
9
|
+
export type * from "./serializer/holder";
|
|
10
|
+
export * from "./serializer/manyToMany";
|
|
11
|
+
export * from "./serializer/manyToOne";
|
|
12
|
+
export type * from "./serializer/modifier";
|
|
13
|
+
export * from "./serializer/oneToOne";
|
|
14
|
+
export * from "./serializer/vanilla/shaped";
|
|
15
|
+
export * from "./serializer/vanilla/shapeless";
|
|
16
|
+
export * from "./serializer/vanilla/smelting";
|
|
17
|
+
export * from "./serializer/vanilla/smithing";
|
|
18
|
+
export * from "./serializer/vanilla/stonecutting";
|
package/src/loader.ts
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import {
|
|
2
|
+
encodeId,
|
|
3
|
+
IllegalShapeError,
|
|
4
|
+
JsonLoader,
|
|
5
|
+
type ConditionContext,
|
|
6
|
+
type RegistryProvider,
|
|
7
|
+
} from "@adeficior/data-modifier-core";
|
|
8
|
+
import {
|
|
9
|
+
type IngredientSerializer,
|
|
10
|
+
type ResultSerializer,
|
|
11
|
+
type WithSerializerModules,
|
|
12
|
+
} from "@adeficior/data-modifier-ingredients";
|
|
13
|
+
import { omit } from "lodash-es";
|
|
14
|
+
import { minimatch } from "minimatch";
|
|
15
|
+
import { type RecipeDefinition } from "./schema";
|
|
16
|
+
import { type Recipe, type RecipeParser } from "./serializer/abstract";
|
|
17
|
+
import {
|
|
18
|
+
type RecipeParseContext,
|
|
19
|
+
type RecipeSerializer,
|
|
20
|
+
} from "./serializer/context";
|
|
21
|
+
import { ForgeConditionalRecipeParser } from "./serializer/forge/conditional";
|
|
22
|
+
import { RecipeHolder } from "./serializer/holder";
|
|
23
|
+
import { ShapedParser } from "./serializer/vanilla/shaped";
|
|
24
|
+
import { ShapelessParser } from "./serializer/vanilla/shapeless";
|
|
25
|
+
import { SmeltingParser } from "./serializer/vanilla/smelting";
|
|
26
|
+
import { SmithingParser } from "./serializer/vanilla/smithing";
|
|
27
|
+
import { StonecuttingParser } from "./serializer/vanilla/stonecutting";
|
|
28
|
+
|
|
29
|
+
export interface RecipeLoaderAccessor extends RegistryProvider<RecipeHolder> {
|
|
30
|
+
unknownRecipeTypes(): RecipeDefinition[];
|
|
31
|
+
registerParser(
|
|
32
|
+
recipeType: string,
|
|
33
|
+
parser: RecipeParser<RecipeDefinition, Recipe>,
|
|
34
|
+
): void;
|
|
35
|
+
ignoreType(recipeType: string): void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Split serializer into seperate class
|
|
39
|
+
export class RecipeLoader
|
|
40
|
+
extends JsonLoader<RecipeHolder>
|
|
41
|
+
implements RecipeLoaderAccessor, RecipeSerializer
|
|
42
|
+
{
|
|
43
|
+
private readonly recipeParsers = new Map<string, RecipeParser>();
|
|
44
|
+
|
|
45
|
+
private readonly ignoredRecipeTypePatterns: string[] = [];
|
|
46
|
+
private readonly _unknownRecipeTypes = new Map<string, RecipeDefinition>();
|
|
47
|
+
|
|
48
|
+
constructor(
|
|
49
|
+
private readonly resultSerializer: ResultSerializer,
|
|
50
|
+
private readonly ingredientSerializer: IngredientSerializer,
|
|
51
|
+
context?: ConditionContext,
|
|
52
|
+
) {
|
|
53
|
+
super(context);
|
|
54
|
+
|
|
55
|
+
this.registerParser("minecraft:crafting_shaped", new ShapedParser());
|
|
56
|
+
this.registerParser("minecraft:crafting_shapeless", new ShapelessParser());
|
|
57
|
+
this.registerParser("minecraft:smelting", new SmeltingParser());
|
|
58
|
+
this.registerParser("minecraft:smoking", new SmeltingParser());
|
|
59
|
+
this.registerParser("minecraft:blasting", new SmeltingParser());
|
|
60
|
+
this.registerParser("minecraft:campfire_cooking", new SmeltingParser());
|
|
61
|
+
this.registerParser("minecraft:smithing", new SmithingParser());
|
|
62
|
+
this.registerParser("minecraft:smithing_trim", new SmithingParser());
|
|
63
|
+
this.registerParser("minecraft:smithing_transform", new SmithingParser());
|
|
64
|
+
this.registerParser("minecraft:stonecutting", new StonecuttingParser());
|
|
65
|
+
|
|
66
|
+
this.registerParser("theoneprobe:probe_helmet", new ShapedParser());
|
|
67
|
+
|
|
68
|
+
this.registerParser(
|
|
69
|
+
"forge:conditional",
|
|
70
|
+
new ForgeConditionalRecipeParser(),
|
|
71
|
+
);
|
|
72
|
+
/*
|
|
73
|
+
TODO move to seperate modules
|
|
74
|
+
|
|
75
|
+
this.registerParser(
|
|
76
|
+
"sullysmod:grindstone_polishing",
|
|
77
|
+
new GrindstonePolishingParser(),
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
this.registerParser("ad_astra:hammering", new HammeringRecipeParser());
|
|
81
|
+
this.registerParser(
|
|
82
|
+
"ad_astra:cryo_fuel_conversion",
|
|
83
|
+
new FluidConversionRecipeParser(),
|
|
84
|
+
);
|
|
85
|
+
this.registerParser(
|
|
86
|
+
"ad_astra:fuel_conversion",
|
|
87
|
+
new FluidConversionRecipeParser(),
|
|
88
|
+
);
|
|
89
|
+
this.registerParser(
|
|
90
|
+
"ad_astra:oxygen_conversion",
|
|
91
|
+
new FluidConversionRecipeParser(),
|
|
92
|
+
);
|
|
93
|
+
this.registerParser("ad_astra:compressing", new InputOutputRecipeParser());
|
|
94
|
+
this.registerParser(
|
|
95
|
+
"ad_astra:crafting_shaped_space_suit",
|
|
96
|
+
new ShapedParser(),
|
|
97
|
+
);
|
|
98
|
+
this.registerParser(
|
|
99
|
+
"ad_astra:nasa_workbench",
|
|
100
|
+
new NasaWorkbenchRecipeParser(),
|
|
101
|
+
);
|
|
102
|
+
this.registerParser(
|
|
103
|
+
"ad_astra:space_station",
|
|
104
|
+
new SpaceStationRecipeParser(),
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
this.registerParser(
|
|
108
|
+
"patchouli:shapeless_book_recipe",
|
|
109
|
+
new ShapelessParser(),
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
this.registerParser("cofh_core:crafting_shaped_potion", new ShapedParser());
|
|
113
|
+
|
|
114
|
+
this.registerParser(
|
|
115
|
+
"rootsclassic:component",
|
|
116
|
+
new RootComponentRecipeParser(),
|
|
117
|
+
);
|
|
118
|
+
this.registerParser("rootsclassic:ritual", new RootRitualRecipeParser());
|
|
119
|
+
|
|
120
|
+
this.ignoreType("jeed:*");
|
|
121
|
+
this.ignoreType("immersiveengineering:*");
|
|
122
|
+
*/
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
ignoreType(pattern: string) {
|
|
126
|
+
this.ignoredRecipeTypePatterns.push(pattern);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
unknownRecipeTypes() {
|
|
130
|
+
return [...this._unknownRecipeTypes.values()];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private recipeParseContext(
|
|
134
|
+
parser: WithSerializerModules,
|
|
135
|
+
): RecipeParseContext {
|
|
136
|
+
return {
|
|
137
|
+
recipes: this,
|
|
138
|
+
ingredients: this.ingredientSerializer.selectModule(
|
|
139
|
+
parser.ingredientModules(),
|
|
140
|
+
),
|
|
141
|
+
results: this.resultSerializer.selectModule(parser.resultModules()),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
serialize(recipe: RecipeHolder): RecipeDefinition {
|
|
146
|
+
const parser = this.recipeParsers.get(recipe.serializerType);
|
|
147
|
+
|
|
148
|
+
if (!parser)
|
|
149
|
+
throw new Error(
|
|
150
|
+
`Unable to find parser for type '${recipe.serializerType}'`,
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
const context = this.recipeParseContext(parser);
|
|
154
|
+
return recipe.serialize(context);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
deserialize(definition: RecipeDefinition): RecipeHolder {
|
|
158
|
+
if (!definition.type)
|
|
159
|
+
throw new IllegalShapeError(`no recipe type set`, definition);
|
|
160
|
+
|
|
161
|
+
const parser = this.recipeParsers.get(encodeId(definition.type));
|
|
162
|
+
|
|
163
|
+
if (!("type" in definition))
|
|
164
|
+
throw new IllegalShapeError("recipe type missing");
|
|
165
|
+
|
|
166
|
+
if (!parser) {
|
|
167
|
+
if (!this._unknownRecipeTypes.has(definition.type)) {
|
|
168
|
+
this._unknownRecipeTypes.set(definition.type, definition);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
throw new IllegalShapeError(
|
|
172
|
+
`unknown recipe type: '${definition.type}'`,
|
|
173
|
+
definition,
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const parsed = parser.deserialize(
|
|
178
|
+
definition,
|
|
179
|
+
this.recipeParseContext(parser),
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
return new RecipeHolder(definition, parsed);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
override parse(definition: RecipeDefinition): RecipeHolder | null {
|
|
186
|
+
if (
|
|
187
|
+
this.ignoredRecipeTypePatterns.some((it) =>
|
|
188
|
+
minimatch(definition.type, it),
|
|
189
|
+
)
|
|
190
|
+
)
|
|
191
|
+
return null;
|
|
192
|
+
|
|
193
|
+
// TODO only print unknown recipe types once in the end
|
|
194
|
+
|
|
195
|
+
const importantData = omit(
|
|
196
|
+
definition,
|
|
197
|
+
"type",
|
|
198
|
+
"category",
|
|
199
|
+
"conditions",
|
|
200
|
+
"fabric:load_conditions",
|
|
201
|
+
"neoforge:conditions",
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
if (Object.keys(importantData).length === 0) return null;
|
|
205
|
+
|
|
206
|
+
return this.deserialize(definition);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
registerParser(
|
|
210
|
+
recipeType: string,
|
|
211
|
+
parser: RecipeParser<RecipeDefinition, Recipe>,
|
|
212
|
+
) {
|
|
213
|
+
this.recipeParsers.set(recipeType, parser);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
clear() {
|
|
217
|
+
this._unknownRecipeTypes.clear();
|
|
218
|
+
}
|
|
219
|
+
}
|
package/src/module.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { defineModule } from "@adeficior/data-modifier-core";
|
|
2
|
+
import { name } from "../package.json";
|
|
3
|
+
import { RecipeEmitter, type RecipeRules } from "./emitter";
|
|
4
|
+
import { type RegisterRecipeParser } from "./hooks";
|
|
5
|
+
import { RecipeLoader, type RecipeLoaderAccessor } from "./loader";
|
|
6
|
+
import { recipePattern } from "./schema";
|
|
7
|
+
|
|
8
|
+
export default defineModule<{
|
|
9
|
+
hooks: {
|
|
10
|
+
"recipes:register-parser": RegisterRecipeParser;
|
|
11
|
+
};
|
|
12
|
+
emitters: {
|
|
13
|
+
recipes: RecipeRules;
|
|
14
|
+
};
|
|
15
|
+
loaders: {
|
|
16
|
+
recipes: RecipeLoaderAccessor;
|
|
17
|
+
};
|
|
18
|
+
}>({
|
|
19
|
+
importModule: name,
|
|
20
|
+
dependencies: {
|
|
21
|
+
"@adeficior/data-modifier-ingredients": "required",
|
|
22
|
+
// TODO make optional?
|
|
23
|
+
"@adeficior/data-modifier-tags": "required",
|
|
24
|
+
},
|
|
25
|
+
types: {
|
|
26
|
+
hooks: {
|
|
27
|
+
"recipes:register-parser": { name: "RegisterRecipeParser" },
|
|
28
|
+
},
|
|
29
|
+
loaders: {
|
|
30
|
+
recipes: "RecipeLoaderAccessor",
|
|
31
|
+
},
|
|
32
|
+
emitters: {
|
|
33
|
+
recipes: "RecipeRules",
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
setup: (pack) => {
|
|
37
|
+
const loader = pack.loader(
|
|
38
|
+
"recipes",
|
|
39
|
+
(container) =>
|
|
40
|
+
new RecipeLoader(
|
|
41
|
+
container.get("serializer:results"),
|
|
42
|
+
container.get("serializer:ingredients"),
|
|
43
|
+
),
|
|
44
|
+
recipePattern(pack.options.packFormat),
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
pack.emitter(
|
|
48
|
+
"recipes",
|
|
49
|
+
(container) =>
|
|
50
|
+
new RecipeEmitter(
|
|
51
|
+
container.get("logger"),
|
|
52
|
+
pack.options.packFormat,
|
|
53
|
+
loader(),
|
|
54
|
+
container.get("serializer:results"),
|
|
55
|
+
container.get("serializer:ingredients"),
|
|
56
|
+
container.get("predicates"),
|
|
57
|
+
loader(),
|
|
58
|
+
),
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
pack.hook("setup:after", ({ callHook }) => {
|
|
62
|
+
const serializer = loader();
|
|
63
|
+
callHook("recipes:register-parser", {
|
|
64
|
+
register: (type, parser) => {
|
|
65
|
+
serializer.registerParser(type, parser);
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
},
|
|
70
|
+
});
|