@block_factory/lib 0.0.5 → 0.0.8

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.
Files changed (69) hide show
  1. package/_module/BlockFactory.ts +10 -20
  2. package/_module/util/Command.ts +1 -9
  3. package/_module/util/Forms/Form.ts +5 -31
  4. package/_module/util/Forms/FormAction.ts +2 -1
  5. package/_module/util/Forms/FormMessage.ts +2 -1
  6. package/_module/util/Forms/FormModal.ts +9 -10
  7. package/_module/util/Forms/FormRegistry.ts +20 -23
  8. package/_module/util/Forms/IForm.ts +31 -0
  9. package/_module/util/Globals.ts +26 -0
  10. package/_module/util/ISystem.ts +58 -0
  11. package/_module/util/IVector.ts +420 -0
  12. package/_module/util/Math.ts +2 -0
  13. package/_module/util/Navigation.ts +130 -0
  14. package/_module/util/Signal.ts +71 -7
  15. package/_module/util/TempLeaker.ts +137 -0
  16. package/_module/util/Wrapper/IEntity.ts +93 -25
  17. package/_module/util/Wrapper/IItemStack.ts +63 -0
  18. package/_module/util/Wrapper/IPlayer.ts +73 -29
  19. package/_module/util/Wrapper/_Common.ts +130 -0
  20. package/_module/util/Wrapper/{Container.ts → _Container.ts} +5 -5
  21. package/index.js +3911 -521
  22. package/package.json +10 -4
  23. package/typedoc.json +6 -0
  24. package/_module/Framework/EntityTasks.ts +0 -203
  25. package/_module/Framework/Threads.ts +0 -72
  26. package/_module/Framework/_INIT.ts +0 -39
  27. package/_module/Types.ts +0 -10
  28. package/_module/util/System.ts +0 -21
  29. package/_module/util/Vector.ts +0 -388
  30. package/_types/_module/BlockFactory.d.ts +0 -19
  31. package/_types/_module/BlockFactory.d.ts.map +0 -1
  32. package/_types/_module/Framework/EntityTasks.d.ts +0 -40
  33. package/_types/_module/Framework/EntityTasks.d.ts.map +0 -1
  34. package/_types/_module/Framework/Threads.d.ts +0 -22
  35. package/_types/_module/Framework/Threads.d.ts.map +0 -1
  36. package/_types/_module/Framework/_INIT.d.ts +0 -19
  37. package/_types/_module/Framework/_INIT.d.ts.map +0 -1
  38. package/_types/_module/Types.d.ts +0 -10
  39. package/_types/_module/Types.d.ts.map +0 -1
  40. package/_types/_module/util/Command.d.ts +0 -92
  41. package/_types/_module/util/Command.d.ts.map +0 -1
  42. package/_types/_module/util/Forms/Form.d.ts +0 -12
  43. package/_types/_module/util/Forms/Form.d.ts.map +0 -1
  44. package/_types/_module/util/Forms/FormAction.d.ts +0 -73
  45. package/_types/_module/util/Forms/FormAction.d.ts.map +0 -1
  46. package/_types/_module/util/Forms/FormMessage.d.ts +0 -24
  47. package/_types/_module/util/Forms/FormMessage.d.ts.map +0 -1
  48. package/_types/_module/util/Forms/FormModal.d.ts +0 -66
  49. package/_types/_module/util/Forms/FormModal.d.ts.map +0 -1
  50. package/_types/_module/util/Forms/FormRegistry.d.ts +0 -12
  51. package/_types/_module/util/Forms/FormRegistry.d.ts.map +0 -1
  52. package/_types/_module/util/Math.d.ts +0 -20
  53. package/_types/_module/util/Math.d.ts.map +0 -1
  54. package/_types/_module/util/RawText.d.ts +0 -60
  55. package/_types/_module/util/RawText.d.ts.map +0 -1
  56. package/_types/_module/util/Signal.d.ts +0 -11
  57. package/_types/_module/util/Signal.d.ts.map +0 -1
  58. package/_types/_module/util/System.d.ts +0 -4
  59. package/_types/_module/util/System.d.ts.map +0 -1
  60. package/_types/_module/util/Vector.d.ts +0 -212
  61. package/_types/_module/util/Vector.d.ts.map +0 -1
  62. package/_types/_module/util/Wrapper/Container.d.ts +0 -9
  63. package/_types/_module/util/Wrapper/Container.d.ts.map +0 -1
  64. package/_types/_module/util/Wrapper/IEntity.d.ts +0 -12
  65. package/_types/_module/util/Wrapper/IEntity.d.ts.map +0 -1
  66. package/_types/_module/util/Wrapper/IPlayer.d.ts +0 -13
  67. package/_types/_module/util/Wrapper/IPlayer.d.ts.map +0 -1
  68. package/_types/index.d.ts +0 -3
  69. package/_types/index.d.ts.map +0 -1
@@ -0,0 +1,137 @@
1
+ import { Entity, Player, system } from "@minecraft/server";
2
+
3
+ /**
4
+ * Relay utility that abuses `playAnimation(... stopExpression ...)`
5
+ * to write values into Molang temps (`t.*`) on the client.
6
+ *
7
+ * - Always includes `t.source_name` and `t.__nonce__`
8
+ * - Values are serialized into a single `stopExpression` string
9
+ * - Intended for lightweight HUD / animation-controller syncing
10
+ *
11
+ * @external @example
12
+ *
13
+ * Client json relay example
14
+ *
15
+ * "scripts": {
16
+ * "initialize": [
17
+ * "v._nonce_swing = -1;",
18
+ * "v.swing = 0;"
19
+ * ],
20
+ * "pre_animation": [
21
+ * "q.is_name_any(t.source_name??'') && (t.__nonce__??-1) != v._nonce_swing ? { v._nonce_swing = t.__nonce__??-1; v.swing = (t.swing??0); };"
22
+ * ]
23
+ * }
24
+ */
25
+ export class TempLeak {
26
+ private static readonly ANIM = "5bb1702b_252a_4133_8aff_8ed11f168e4e";
27
+ private static readonly CTRL = "2ca2eabb_c7d9_4777_8497_57444ea2cd50";
28
+
29
+ private static nonceBySource = new Map<string, number>();
30
+
31
+ private static nextNonce(source: Player | Entity): number {
32
+ const n = (this.nonceBySource.get(source.id) ?? 0) + 1;
33
+ this.nonceBySource.set(source.id, n);
34
+ return n;
35
+ }
36
+
37
+ private static esc(str: string): string {
38
+ return str.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
39
+ }
40
+
41
+ private static fmt(value: unknown): string {
42
+ if (typeof value === "number") return Number.isFinite(value) ? String(value) : "0";
43
+ if (typeof value === "boolean") return value ? "1" : "0";
44
+ if (typeof value === "string") return `'${this.esc(value)}'`;
45
+ return `'${this.esc(String(value))}'`;
46
+ }
47
+
48
+ private static isPlayer(e: Player | Entity): e is Player {
49
+ return e instanceof Player;
50
+ }
51
+
52
+ private static buildStopExp(
53
+ source: Player | Entity,
54
+ nonce: number,
55
+ fields: Record<string, unknown>
56
+ ): string {
57
+ const sourceName = this.isPlayer(source) ? source.name : source.typeId;
58
+
59
+ let exp =
60
+ `t.source_name='${this.esc(sourceName)}';` +
61
+ `t.__nonce__=${nonce};`;
62
+
63
+ for (const [k, v] of Object.entries(fields)) {
64
+ const key = k.includes(".") ? k : `t.${k}`;
65
+ exp += `${key}=${this.fmt(v)};`;
66
+ }
67
+
68
+ exp += "return 0;";
69
+ return exp;
70
+ }
71
+
72
+ /**
73
+ * Sends a “no-op” update that only bumps `t.__nonce__` (and sets `t.source_name`).
74
+ *
75
+ *
76
+ * @param source Target source to receive the update.
77
+ */
78
+ public static refresh(source: Player | Entity): void {
79
+ TempLeak.send(source, {});
80
+ }
81
+
82
+ /**
83
+ * Emits a single payload update to the client by playing a hidden animation
84
+ * whose `stopExpression` assigns values into `t.*`.
85
+ *
86
+ * Notes:
87
+ * - A per-source monotonic nonce is included as `t.__nonce__`.
88
+ * - Keys in `fields` are written as `t.<key>` unless you pass a dotted path
89
+ * (e.g. `"t.swing"` or `"t.ui.swing"`).
90
+ * - Numbers are clamped to finite values, booleans become `1/0`, and strings
91
+ * are escaped.
92
+ *
93
+ * @param source Target source to receive the update.
94
+ * @param fields Key/value pairs to assign into `t.*` on the client.
95
+ */
96
+ public static send(source: Player | Entity, fields: Record<string, unknown>): void {
97
+ const nonce = this.nextNonce(source);
98
+ source.playAnimation(this.ANIM, {
99
+ controller: this.CTRL,
100
+ stopExpression: this.buildStopExp(source, nonce, fields),
101
+ });
102
+ }
103
+
104
+ /**
105
+ * Convenience helper for impulse leaks.
106
+ *
107
+ * This sends `setFields` immediately, then (optionally) sends `clearFields`
108
+ * after `clearDelayTicks`.
109
+ *
110
+ * If `clearFields` is provided, it is used as-is.
111
+ *
112
+ * Intended use:
113
+ * - Fire-and-forget booleans like `t.swing = 1` then clear back to `0`
114
+ * - Short pulses for UI triggers where the client watches for nonce changes
115
+ *
116
+ * @param source Target source to receive the update(s).
117
+ * @param setFields Fields to set immediately.
118
+ * @param clearFields Fields to send after the delay (optional).
119
+ * @param clearDelayTicks Tick delay before sending `clearFields` (default: 1).
120
+ */
121
+ public static pulse(source: Player | Entity,setFields: Record<string, unknown>,clearFields?: Record<string, unknown>,clearDelayTicks: number = 1): void {
122
+ this.send(source, setFields);
123
+ if (!clearFields) return;
124
+ system.runTimeout(() => {
125
+ const autoClear: Record<string, unknown> = {};
126
+ if (!clearFields) {
127
+ for (const [k, v] of Object.entries(setFields)) {
128
+ if (typeof v === "string") autoClear[k] = "";
129
+ else autoClear[k] = 0;
130
+ }
131
+ clearFields = autoClear;
132
+ }
133
+
134
+ this.send(source, clearFields);
135
+ }, clearDelayTicks);
136
+ }
137
+ }
@@ -1,35 +1,103 @@
1
- import { Entity, EntityInventoryComponent, PlayAnimationOptions, system, TicksPerSecond } from "@minecraft/server";
2
- import { ContainerWrapper, Inventory } from "./Container";
3
- import { System } from "../System";
4
- import { Animation } from "../../Types";
1
+ import { Entity, EntityRemoveBeforeEvent, PlayAnimationOptions, system, world } from "@minecraft/server";
2
+ import { Inventory } from "./_Container";
3
+ import { iSystem } from "../ISystem";
4
+ import { MinecraftDimensionTypes } from "@minecraft/vanilla-data";
5
+ import { Ctor, PropertyRecord } from "../Globals";
6
+ import { CLASS_I_UUID, WrapperCore } from "./_Common";
5
7
 
6
- export type IEntity = IEntityWrapper & Entity;
8
+ export type IEntity<T extends ICustomEntity> = T & Entity;
7
9
 
8
- export class IEntityWrapper {
9
- public readonly source: Entity;
10
+ export abstract class ICustomEntity {
11
+ protected readonly source: Entity;
12
+ private intervalId: number = -1;
13
+ public isNavigating: boolean = false;
10
14
 
11
- private constructor(source: Entity) {
12
- this.source = source;
13
- return System.ProxyConstructor(this, source);
14
- }
15
+ public constructor(source: Entity) {
16
+ this.source = source;
17
+ }
18
+ public static wrap<T extends ICustomEntity>(source: Entity, Ctor: Ctor<T>): IEntity<T> {
19
+ return EntityWrapper.wrap(source, Ctor);
20
+ }
15
21
 
16
- public static wrap(source: Entity): IEntity {
17
- return new IEntityWrapper(source) as IEntity;
18
- }
22
+ public get inventory(): Inventory | undefined { return WrapperCore.getInventory(this.source); }
19
23
 
20
- //======================== Interal ========================
24
+ public loadData(): void { WrapperCore.loadDynData(this as any); }
25
+ public saveData(): void { WrapperCore.saveDynData(this as any); }
21
26
 
22
- public get inventory(): Inventory | undefined {
23
- const i = this.source.getComponent(
24
- EntityInventoryComponent.componentId
25
- ) as EntityInventoryComponent;
27
+ public async playAnimationAsync(id: string, length: number, options?: PlayAnimationOptions): Promise<void> {
28
+ await WrapperCore.playAnimationAsync(this.source, id, length, options);
29
+ }
26
30
 
27
- return ContainerWrapper.wrap(i.container);
28
- }
31
+ public startInterval(tickInterval?: number): number {
32
+ //TODO
33
+ //add simulation distance range to pause intervals
34
+ this.intervalId = system.runInterval(() => {
35
+ if (!this.source.isValid) { system.clearRun(this.intervalId); return; }
36
+ this.interval?.();
37
+ }, tickInterval);
38
+ return this.intervalId;
39
+ }
29
40
 
30
- public async playAnimationAsync(animation: Animation, options: PlayAnimationOptions): Promise<void> {
31
- this.source.playAnimation(animation.id, options);
32
- await system.waitTicks(animation.length * TicksPerSecond);
33
- }
41
+ public data?(): PropertyRecord;
42
+ public onReady?(): void;
43
+ public onRemove?(): void;
44
+ public interval?(): void;
45
+ }
46
+
47
+ const cache = new Map<string, Map<Function, any>>();
48
+
49
+ class EntityWrapper {
50
+ public static wrap<T extends ICustomEntity>(source: Entity, Ctor: Ctor<T>): IEntity<T> {
51
+ if (iSystem.packId === "undefined") throw Error(`${this.name} - System.PackId is undefined.`);
52
+ return WrapperCore.wrap<Entity, IEntity<T>>(cache, source, Ctor);
53
+ }
34
54
 
55
+ public static repopulateEntity(entity: Entity): void {
56
+ if (!entity.typeId.startsWith(iSystem.packId)) return;
57
+ WrapperCore.repopulate(cache, entity, iSystem.iClassRegistry, ICustomEntity.wrap);
58
+ }
59
+
60
+ public static getAll<T extends ICustomEntity>(Ctor: Ctor<T>): IterableIterator<IEntity<T>> {
61
+ return WrapperCore.iterateByCtor<IEntity<T>>(cache, Ctor);
62
+ }
35
63
  }
64
+
65
+ world.beforeEvents.entityRemove.subscribe((event) => {
66
+ const e: Entity = event.removedEntity;
67
+ const classId = e.getDynamicProperty(CLASS_I_UUID) as string | undefined;
68
+ system.run(() => {
69
+ if (!classId) return;
70
+ const Ctor = iSystem.iClassRegistry.get(classId);
71
+ if (!Ctor) return;
72
+ const wrapped = ICustomEntity.wrap(e, Ctor);
73
+ wrapped.onRemove?.();
74
+ })
75
+ });
76
+
77
+ //Delete from cache on remove
78
+ world.afterEvents.entityRemove.subscribe(({ removedEntityId, typeId }) => {
79
+ if (!typeId.startsWith(iSystem.packId) || !cache.has(removedEntityId)) return;
80
+ cache.delete(removedEntityId);
81
+ })
82
+
83
+ //Save entity data on shutdown or reset
84
+ system.beforeEvents.shutdown.subscribe(() => {
85
+ for (const ent of WrapperCore.iterateAll<IEntity<any>>(cache)) {
86
+ ent.saveData();
87
+ }
88
+ });
89
+
90
+ //Repopulate Entity data on load
91
+ world.afterEvents.entityLoad.subscribe(({ entity }) => {
92
+ EntityWrapper.repopulateEntity(entity);
93
+ });
94
+
95
+ //Repopulate All Entity data on reload
96
+ world.afterEvents.worldLoad.subscribe(() => {
97
+ //TODO
98
+ //Replace dimension check with stored id array to improve init cycle speed
99
+ for (const dimensionId of Object.values(MinecraftDimensionTypes)) {
100
+ const entities = world.getDimension(dimensionId).getEntities();
101
+ entities.forEach((entity: Entity) => EntityWrapper.repopulateEntity(entity));
102
+ }
103
+ });
@@ -0,0 +1,63 @@
1
+ import { ItemStack, system } from "@minecraft/server";
2
+ import { iSystem } from "../ISystem";
3
+ import { Ctor, PropertyRecord } from "../Globals";
4
+ import { WrapperCore, CLASS_I_UUID } from "./_Common";
5
+
6
+ type DynPropsRW = { getDynamicProperty(k: string): any; setDynamicProperty(k: string, v: any): void };
7
+ type DynPropsW = { setDynamicProperty(k: string, v: any): void };
8
+ type WrapperLike = { loadData(): void; onReady?: () => void };
9
+
10
+ export type IItemStack<T extends ICustomItemStack> = T & ItemStack;
11
+
12
+ export abstract class ICustomItemStack {
13
+ protected readonly source: ItemStack;
14
+ private intervalId: number = -1;
15
+
16
+ public constructor(source: ItemStack) {
17
+ this.source = source;
18
+ }
19
+
20
+ public loadData(): void { WrapperCore.loadDynData(this as any); }
21
+ public saveData(): void { WrapperCore.saveDynData(this as any); }
22
+
23
+ public static wrap<T extends ICustomItemStack>(source: ItemStack, Ctor: Ctor<T>): IItemStack<T> {
24
+ return ItemStackWrapper.wrap(source, Ctor);
25
+ }
26
+
27
+ public startInterval(tickInterval?: number): number {
28
+ this.intervalId = system.runInterval(() => {
29
+ if (!this.source) { system.clearRun(this.intervalId); return; }
30
+ this.interval?.();
31
+ }, tickInterval);
32
+ return this.intervalId;
33
+ }
34
+
35
+ public data?(): PropertyRecord;
36
+ public onReady?(): void;
37
+ public interval?(): void;
38
+ }
39
+
40
+ class ItemStackWrapper {
41
+ public static wrap<S extends DynPropsW, W extends WrapperLike>(source: S, Ctor: Ctor<any>): W {
42
+ const inst = new (Ctor as any)(source);
43
+ const proxied = iSystem.ProxyConstructor(inst, source) as W;
44
+
45
+ proxied.loadData();
46
+ proxied.onReady?.();
47
+
48
+ source.setDynamicProperty(CLASS_I_UUID, Ctor.name);
49
+ return proxied;
50
+ }
51
+
52
+ public static repopulateItemStack<S extends DynPropsRW, W extends WrapperLike>(source: S): W | undefined {
53
+ const classId = source.getDynamicProperty(CLASS_I_UUID) as string | undefined;
54
+ if (!classId) return undefined;
55
+
56
+ const Ctor = iSystem.iClassRegistry.get(classId);
57
+ if (!Ctor) return undefined;
58
+
59
+ return ItemStackWrapper.wrap<S, W>(source as any, Ctor);
60
+ }
61
+ }
62
+
63
+
@@ -1,40 +1,84 @@
1
- import { EntityInventoryComponent, PlayAnimationOptions, Player, PlayerSoundOptions, system, TicksPerSecond } from "@minecraft/server";
2
- import { ContainerWrapper, Inventory } from "./Container";
3
- import { System } from "../System";
4
- import { Animation, Sound } from "../../Types";
1
+ import { Player, EntityInventoryComponent, PlayAnimationOptions, system, world, TicksPerSecond } from "@minecraft/server";
2
+ import { IContainer, Inventory } from "./_Container";
3
+ import { iSystem } from "../ISystem";
4
+ import { Ctor, PropertyRecord } from "../Globals";
5
+ import { WrapperCore } from "./_Common";
5
6
 
6
- export type IPlayer = IPlayerWrapper & Player;
7
+ export type IPlayer<T extends ICustomPlayer> = T & Player;
7
8
 
8
- export class IPlayerWrapper {
9
- public readonly source: Player;
9
+ export abstract class ICustomPlayer {
10
+ protected readonly source: Player;
11
+ private intervalId: number = -1;
10
12
 
11
- private constructor(source: Player) {
12
- this.source = source;
13
- return System.ProxyConstructor(this, source);
14
- }
13
+ public constructor(source: Player) { this.source = source; }
15
14
 
16
- public static wrap(player: Player): IPlayer {
17
- return new IPlayerWrapper(player) as IPlayer;
18
- }
15
+ public loadData(): void { WrapperCore.loadDynData(this as any); }
16
+ public saveData(): void { WrapperCore.saveDynData(this as any); }
19
17
 
20
- //======================== Interal ========================
18
+ public static wrap<T extends ICustomPlayer>(source: Player, Ctor: Ctor<T>): IPlayer<T> {
19
+ return PlayerWrapper.wrap(source, Ctor);
20
+ }
21
21
 
22
- public get inventory(): Inventory {
23
- const i = this.source.getComponent(
24
- EntityInventoryComponent.componentId
25
- ) as EntityInventoryComponent;
22
+ public get inventory(): Inventory | undefined {
23
+ const i = this.source.getComponent(EntityInventoryComponent.componentId) as EntityInventoryComponent | undefined;
24
+ if (!i?.container) return undefined;
25
+ return IContainer.wrap(i.container);
26
+ }
26
27
 
27
- return ContainerWrapper.wrap(i.container);
28
- }
28
+ public async playAnimationAsync(id: string, length: number, options?: PlayAnimationOptions): Promise<void> {
29
+ this.source.playAnimation(id, options);
30
+ await system.waitTicks(Math.round(length * TicksPerSecond));
31
+ }
29
32
 
30
- public async playSoundAsync(sound:Sound, options: PlayerSoundOptions): Promise<void>{
31
- this.source.playSound(sound.id, options);
32
- await system.waitTicks(sound.length * TicksPerSecond);
33
- }
33
+ public startInterval(tickInterval?: number): number {
34
+ this.intervalId = system.runInterval(() => {
35
+ if (!this.source.isValid) { system.clearRun(this.intervalId); return; }
36
+ this.interval?.();
37
+ }, tickInterval);
38
+ return this.intervalId;
39
+ }
34
40
 
35
- public async playAnimationAsync(animation: Animation, options: PlayAnimationOptions): Promise<void> {
36
- this.source.playAnimation(animation.id, options);
37
- await system.waitTicks(animation.length * TicksPerSecond);
38
- }
41
+ public data?(): PropertyRecord;
42
+ public onReady?(): void;
43
+ public interval?(): void;
44
+ }
45
+
46
+ const cache = new Map<string, Map<Function, any>>();
47
+
48
+ class PlayerWrapper {
49
+ public static wrap<T extends ICustomPlayer>(source: Player, Ctor: Ctor<T>): IPlayer<T> {
50
+ return WrapperCore.wrap<Player, IPlayer<T>>(cache, source, Ctor);
51
+ }
52
+
53
+ public static repopulatePlayer(player: Player): void {
54
+ WrapperCore.repopulate(cache, player, iSystem.iClassRegistry, ICustomPlayer.wrap);
55
+ }
39
56
 
57
+ public static getAll<T extends ICustomPlayer>(Ctor: Ctor<T>): IterableIterator<IPlayer<T>> {
58
+ return WrapperCore.iterateByCtor<IPlayer<T>>(cache, Ctor);
59
+ }
40
60
  }
61
+
62
+ //Delete from cache on remove
63
+ world.afterEvents.playerLeave.subscribe(({ playerId }) => {
64
+ if (!cache.has(playerId)) return;
65
+ cache.delete(playerId);
66
+ })
67
+
68
+ //Save Player data on shutdown or reset
69
+ system.beforeEvents.shutdown.subscribe(() => {
70
+ for (const p of WrapperCore.iterateAll<IPlayer<any>>(cache)) {
71
+ p.saveData();
72
+ }
73
+ });
74
+
75
+ //Repopulate Player data on load
76
+ world.afterEvents.playerSpawn.subscribe(({ player }) => {
77
+ PlayerWrapper.repopulatePlayer(player);
78
+ });
79
+
80
+ //Repopulate All Player data on reload
81
+ world.afterEvents.worldLoad.subscribe(() => {
82
+ const players = world.getAllPlayers();
83
+ players.forEach((player: Player) => PlayerWrapper.repopulatePlayer(player));
84
+ });
@@ -0,0 +1,130 @@
1
+ import type { Ctor, PropertyRecord, PropertyValue } from "../Globals";
2
+ import { Entity, EntityInventoryComponent, PlayAnimationOptions, Player, system, TicksPerSecond } from "@minecraft/server";
3
+ import { IContainer } from "./_Container";
4
+ import { Inventory } from "./_Container";
5
+ import { iSystem } from "../ISystem";
6
+
7
+ export const CLASS_I_UUID = "269160b9a432";
8
+
9
+ export type DynStore = {
10
+ /** schema builder */
11
+ data?(): PropertyRecord;
12
+ getDynamicProperty(key: string): PropertyValue | undefined;
13
+ setDynamicProperty(key: string, value: PropertyValue | undefined): void;
14
+ [key: string]: any;
15
+ };
16
+
17
+ /**
18
+ * Shared wrapper core for Entity + Player.
19
+ */
20
+ export class WrapperCore {
21
+ public static wrap<S extends { id: string; setDynamicProperty(k: string, v: any): void }, W extends { loadData(): void; onReady?: () => void }>(
22
+ cache: Map<string, Map<Function, any>>,
23
+ source: S,
24
+ Ctor: Ctor<any>
25
+ ): W {
26
+ let byCtor = cache.get(source.id);
27
+ if (!byCtor) { byCtor = new Map(); cache.set(source.id, byCtor); }
28
+
29
+ const existing = byCtor.get(Ctor) as W | undefined;
30
+ if (existing) return existing;
31
+
32
+ const inst = new (Ctor as any)(source);
33
+ const proxied = iSystem.ProxyConstructor(inst, source) as W;
34
+
35
+ proxied.loadData();
36
+ proxied.onReady?.();
37
+
38
+ byCtor.set(Ctor, proxied);
39
+ source.setDynamicProperty(CLASS_I_UUID, Ctor.name);
40
+
41
+ return proxied;
42
+ }
43
+
44
+ public static repopulate<S extends { id: string; getDynamicProperty(k: string): any, isValid: boolean }>(
45
+ cache: Map<string, Map<Function, any>>,
46
+ source: S,
47
+ registry: Map<string, Ctor<any>>,
48
+ wrapFn: (source: any, ctor: Ctor<any>) => any
49
+ ): void {
50
+ if (cache.has(source.id) || !source.isValid) return;
51
+
52
+ const classId = source.getDynamicProperty(CLASS_I_UUID) as string | undefined;
53
+ if (!classId) return;
54
+
55
+ const Ctor = registry.get(classId);
56
+ if (!Ctor) return;
57
+
58
+ wrapFn(source as any, Ctor);
59
+ }
60
+
61
+ public static *iterateAll<W>(
62
+ cache: Map<string, Map<Function, any>>
63
+ ): IterableIterator<W> {
64
+ for (const [, byCtor] of cache) {
65
+ for (const [, instance] of byCtor) {
66
+ yield instance as W;
67
+ }
68
+ }
69
+ }
70
+
71
+ public static *iterateByCtor<W>(
72
+ cache: Map<string, Map<Function, any>>,
73
+ Ctor: Function
74
+ ): IterableIterator<W> {
75
+ for (const [, byCtor] of cache) {
76
+ const inst = byCtor.get(Ctor);
77
+ if (inst) yield inst as W;
78
+ }
79
+ }
80
+
81
+ public static getAllByCtor<W>(
82
+ cache: Map<string, Map<Function, any>>,
83
+ Ctor: Function
84
+ ): W[] {
85
+ const out: W[] = [];
86
+ for (const [, byCtor] of cache) {
87
+ const inst = byCtor.get(Ctor);
88
+ if (inst) out.push(inst as W);
89
+ }
90
+ return out;
91
+ }
92
+
93
+ public static getAll<W>(cache: Map<string, Map<Function, any>>): W[] {
94
+ const out: W[] = [];
95
+ for (const [, byCtor] of cache) {
96
+ for (const [, inst] of byCtor) out.push(inst as W);
97
+ }
98
+ return out;
99
+ }
100
+
101
+ public static loadDynData(target: DynStore): void {
102
+ if (!target.data) return;
103
+
104
+ const schema = target.data() as Record<string, unknown>;
105
+ for (const key of Object.keys(schema)) {
106
+ const v = target.getDynamicProperty(key);
107
+ if (v !== undefined) (target as any)[key] = v;
108
+ }
109
+ }
110
+
111
+ public static saveDynData(target: DynStore): void {
112
+ if (!target.isValid || !target.data) return;
113
+
114
+ const saveData = target.data() as Record<string, unknown>;
115
+ for (const [key, value] of Object.entries(saveData)) {
116
+ target.setDynamicProperty(key, value as PropertyValue);
117
+ }
118
+ }
119
+
120
+ public static getInventory(source: Entity | Player): Inventory | undefined {
121
+ const i = source.getComponent(EntityInventoryComponent.componentId) as EntityInventoryComponent | undefined;
122
+ if (!i?.container) return undefined;
123
+ return IContainer.wrap(i.container);
124
+ }
125
+
126
+ public static async playAnimationAsync(source: Entity | Player, id: string, length: number, options?: PlayAnimationOptions): Promise<void> {
127
+ source.playAnimation(id, options);
128
+ await system.waitTicks(Math.round(length * TicksPerSecond));
129
+ }
130
+ }
@@ -1,18 +1,18 @@
1
1
  import { Container, ItemStack} from "@minecraft/server";
2
- import { System } from "../System";
2
+ import { iSystem } from "../ISystem";
3
3
 
4
- export type Inventory = ContainerWrapper & Container;
4
+ export type Inventory = IContainer & Container;
5
5
 
6
- export class ContainerWrapper {
6
+ export class IContainer {
7
7
  public readonly source: Container;
8
8
 
9
9
  private constructor(source: Container) {
10
10
  this.source = source;
11
- return System.ProxyConstructor(this, source);
11
+ return iSystem.ProxyConstructor(this, source);
12
12
  }
13
13
 
14
14
  public static wrap(source: Container): Inventory {
15
- return new ContainerWrapper(source) as Inventory;
15
+ return new IContainer(source) as Inventory;
16
16
  }
17
17
 
18
18
  //======================== Interal ========================