@vaitosoi/minejs 0.2.2 → 0.4.0

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 (149) hide show
  1. package/README.md +1 -1
  2. package/assets/minecraft/26.2/packets.json +4758 -0
  3. package/dist/base/buffer.d.ts +2 -0
  4. package/dist/base/buffer.d.ts.map +1 -0
  5. package/dist/base/buffer.js +12 -0
  6. package/dist/base/buffer.js.map +1 -0
  7. package/dist/base/error.d.ts +59 -3
  8. package/dist/base/error.d.ts.map +1 -1
  9. package/dist/base/error.js +123 -7
  10. package/dist/base/error.js.map +1 -1
  11. package/dist/base/http.d.ts +2 -0
  12. package/dist/base/http.d.ts.map +1 -0
  13. package/dist/base/http.js +14 -0
  14. package/dist/base/http.js.map +1 -0
  15. package/dist/base/math.d.ts +22 -1
  16. package/dist/base/math.d.ts.map +1 -1
  17. package/dist/base/math.js +49 -2
  18. package/dist/base/math.js.map +1 -1
  19. package/dist/base/typing.d.ts +7 -1
  20. package/dist/base/typing.d.ts.map +1 -1
  21. package/dist/client/auth.d.ts +41 -7
  22. package/dist/client/auth.d.ts.map +1 -1
  23. package/dist/client/auth.js +96 -39
  24. package/dist/client/auth.js.map +1 -1
  25. package/dist/client/client.d.ts +73 -17
  26. package/dist/client/client.d.ts.map +1 -1
  27. package/dist/client/client.js +111 -44
  28. package/dist/client/client.js.map +1 -1
  29. package/dist/client/tcp.d.ts +17 -206
  30. package/dist/client/tcp.d.ts.map +1 -1
  31. package/dist/client/tcp.js +37 -715
  32. package/dist/client/tcp.js.map +1 -1
  33. package/dist/index.d.ts +12 -12
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +12 -12
  36. package/dist/index.js.map +1 -1
  37. package/dist/message/link.d.ts +10 -0
  38. package/dist/message/link.d.ts.map +1 -0
  39. package/dist/message/link.js +22 -0
  40. package/dist/message/link.js.map +1 -0
  41. package/dist/message/sigCache.d.ts +13 -0
  42. package/dist/message/sigCache.d.ts.map +1 -0
  43. package/dist/message/sigCache.js +26 -0
  44. package/dist/message/sigCache.js.map +1 -0
  45. package/dist/{translator → packet}/decoder.d.ts +14 -1
  46. package/dist/packet/decoder.d.ts.map +1 -0
  47. package/dist/{translator → packet}/decoder.js +24 -3
  48. package/dist/packet/decoder.js.map +1 -0
  49. package/dist/packet/dispatcher.d.ts +53 -0
  50. package/dist/packet/dispatcher.d.ts.map +1 -0
  51. package/dist/packet/dispatcher.js +738 -0
  52. package/dist/packet/dispatcher.js.map +1 -0
  53. package/dist/{translator → packet}/encoder.d.ts +4 -3
  54. package/dist/packet/encoder.d.ts.map +1 -0
  55. package/dist/{translator → packet}/encoder.js +18 -7
  56. package/dist/packet/encoder.js.map +1 -0
  57. package/dist/packet/static.d.ts.map +1 -0
  58. package/dist/packet/static.js.map +1 -0
  59. package/dist/{base → physics}/aabb.d.ts +2 -2
  60. package/dist/physics/aabb.d.ts.map +1 -0
  61. package/dist/{base → physics}/aabb.js +5 -5
  62. package/dist/physics/aabb.js.map +1 -0
  63. package/dist/physics/direction.d.ts.map +1 -0
  64. package/dist/physics/direction.js.map +1 -0
  65. package/dist/{base → physics}/player.d.ts +25 -28
  66. package/dist/physics/player.d.ts.map +1 -0
  67. package/dist/{base → physics}/player.js +81 -68
  68. package/dist/physics/player.js.map +1 -0
  69. package/dist/version/codec.d.ts +18 -0
  70. package/dist/version/codec.d.ts.map +1 -0
  71. package/dist/version/codec.js +235 -0
  72. package/dist/version/codec.js.map +1 -0
  73. package/dist/version/registry.d.ts +236 -0
  74. package/dist/version/registry.d.ts.map +1 -0
  75. package/dist/version/registry.js +284 -0
  76. package/dist/version/registry.js.map +1 -0
  77. package/dist/{base → world}/block.d.ts +5 -5
  78. package/dist/world/block.d.ts.map +1 -0
  79. package/dist/{base → world}/block.js +13 -13
  80. package/dist/world/block.js.map +1 -0
  81. package/dist/{base → world}/entity.d.ts +4 -4
  82. package/dist/world/entity.d.ts.map +1 -0
  83. package/dist/{base → world}/entity.js +6 -6
  84. package/dist/world/entity.js.map +1 -0
  85. package/dist/world/state.d.ts +175 -0
  86. package/dist/world/state.d.ts.map +1 -0
  87. package/dist/world/state.js +163 -0
  88. package/dist/world/state.js.map +1 -0
  89. package/dist/world/tick.d.ts.map +1 -0
  90. package/dist/world/tick.js.map +1 -0
  91. package/package.json +7 -2
  92. package/src/base/buffer.ts +12 -0
  93. package/src/base/error.ts +93 -4
  94. package/src/base/http.ts +10 -0
  95. package/src/base/math.ts +55 -3
  96. package/src/base/typing.ts +9 -1
  97. package/src/client/auth.ts +130 -52
  98. package/src/client/client.ts +182 -50
  99. package/src/client/tcp.ts +45 -967
  100. package/src/index.ts +12 -12
  101. package/src/message/link.ts +17 -0
  102. package/src/message/sigCache.ts +25 -0
  103. package/src/{translator → packet}/decoder.ts +24 -3
  104. package/src/packet/dispatcher.ts +919 -0
  105. package/src/{translator → packet}/encoder.ts +20 -9
  106. package/src/{base → physics}/aabb.ts +5 -5
  107. package/src/{base → physics}/player.ts +106 -65
  108. package/src/version/codec.ts +248 -0
  109. package/src/version/registry.ts +328 -0
  110. package/src/{base → world}/block.ts +12 -11
  111. package/src/{base → world}/entity.ts +5 -5
  112. package/src/world/state.ts +300 -0
  113. package/test/adapter/index.ts +14 -0
  114. package/test/client/index.ts +28 -21
  115. package/test/json/index.ts +3 -0
  116. package/dist/base/aabb.d.ts.map +0 -1
  117. package/dist/base/aabb.js.map +0 -1
  118. package/dist/base/block.d.ts.map +0 -1
  119. package/dist/base/block.js.map +0 -1
  120. package/dist/base/direction.d.ts.map +0 -1
  121. package/dist/base/direction.js.map +0 -1
  122. package/dist/base/entity.d.ts.map +0 -1
  123. package/dist/base/entity.js.map +0 -1
  124. package/dist/base/player.d.ts.map +0 -1
  125. package/dist/base/player.js.map +0 -1
  126. package/dist/base/registry.d.ts +0 -50
  127. package/dist/base/registry.d.ts.map +0 -1
  128. package/dist/base/registry.js +0 -89
  129. package/dist/base/registry.js.map +0 -1
  130. package/dist/base/tick.d.ts.map +0 -1
  131. package/dist/base/tick.js.map +0 -1
  132. package/dist/translator/decoder.d.ts.map +0 -1
  133. package/dist/translator/decoder.js.map +0 -1
  134. package/dist/translator/encoder.d.ts.map +0 -1
  135. package/dist/translator/encoder.js.map +0 -1
  136. package/dist/translator/static.d.ts.map +0 -1
  137. package/dist/translator/static.js.map +0 -1
  138. package/src/base/registry.ts +0 -87
  139. /package/assets/minecraft/{blocks.json → 26.2/blocks.json} +0 -0
  140. /package/assets/minecraft/{entities.json → 26.2/entities.json} +0 -0
  141. /package/dist/{translator → packet}/static.d.ts +0 -0
  142. /package/dist/{translator → packet}/static.js +0 -0
  143. /package/dist/{base → physics}/direction.d.ts +0 -0
  144. /package/dist/{base → physics}/direction.js +0 -0
  145. /package/dist/{base → world}/tick.d.ts +0 -0
  146. /package/dist/{base → world}/tick.js +0 -0
  147. /package/src/{translator → packet}/static.ts +0 -0
  148. /package/src/{base → physics}/direction.ts +0 -0
  149. /package/src/{base → world}/tick.ts +0 -0
@@ -0,0 +1,248 @@
1
+ import z from "zod";
2
+ import { MissingField, MissingPacketField, NotImplemented, UnexpectedValue } from "../base/error";
3
+ import { BinaryDecoder } from "../packet/decoder";
4
+ import { ConnectionState, SharedState } from "../world/state";
5
+ import { FieldNode, PacketObject, PacketRegistry, VersionDefinitions } from "./registry";
6
+ import { BinaryEncoder } from "../packet/encoder";
7
+
8
+ const TCPStateMapping: Record<ConnectionState, keyof z.infer<typeof VersionDefinitions> | null> = {
9
+ [ConnectionState.Disconnected]: null,
10
+ [ConnectionState.Handshake]: "handshaking",
11
+ [ConnectionState.Login]: "login",
12
+ [ConnectionState.Configure]: "configuration",
13
+ [ConnectionState.Play]: "play",
14
+ };
15
+
16
+ export interface EncodeResult {
17
+ buffer: Buffer,
18
+ packet: z.infer<typeof PacketObject>,
19
+ }
20
+
21
+ export class VersionCodec {
22
+ constructor(private readonly state: SharedState) { }
23
+
24
+ public consumePacket: (packetId: string, data: object) => void = () => { throw new NotImplemented(); };
25
+
26
+ public handlePacket(packetId: number, decoder: BinaryDecoder) {
27
+ const mappedState = TCPStateMapping[this.state.state];
28
+ if (!mappedState) return; // console.log("no state");
29
+ const packet = PacketRegistry.getPacket(mappedState, "clientbound", packetId);
30
+ if (!packet) return; // console.log("no packet");
31
+
32
+ const { name, structure, skipForNow } = packet;
33
+
34
+ if (skipForNow) return;
35
+
36
+ const resolvedObject: Record<string, any> = {};
37
+ if (Object.keys(structure).length > 0)
38
+ for (const [name, field] of Object.entries(structure))
39
+ try {
40
+ resolvedObject[name] = this.readField(packetId, resolvedObject, name, field, decoder);
41
+ } catch (err) {
42
+ if (err instanceof NotImplemented) return;
43
+ throw err;
44
+ }
45
+ if (this.state.clientOptions.debug?.packetLogger)
46
+ console.dir({
47
+ dir: "to client",
48
+ state: mappedState,
49
+ name,
50
+ data: resolvedObject,
51
+ });
52
+
53
+ this.consumePacket(`${TCPStateMapping[this.state.state]}:${name}`, resolvedObject);
54
+ }
55
+
56
+ public encodePacket(packetId: number | string, data: Record<string, any>): EncodeResult {
57
+ const mappedState = TCPStateMapping[this.state.state];
58
+ if (!mappedState) throw new UnexpectedValue("map-able state", this.state.state.toString());
59
+ const packet = PacketRegistry.getPacket(mappedState, "serverbound", packetId);
60
+ if (!packet) throw new UnexpectedValue("valid serverbound packet id", packetId.toString());
61
+
62
+ if (this.state.clientOptions.debug?.packetLogger)
63
+ console.dir({
64
+ dir: "to server",
65
+ state: mappedState,
66
+ id: packetId,
67
+ data,
68
+ });
69
+
70
+ const { name: packetName, structure } = packet;
71
+ let encoder = new BinaryEncoder();
72
+ for (const [name, field] of Object.entries(structure)) {
73
+ if (!(name in data))
74
+ throw new MissingField(name, `packet ${packetName}`);
75
+ if (typeof data[name] === "object")
76
+ if ("value" in data[name])
77
+ if ("metadata" in data[name])
78
+ encoder = this.writeField(field, encoder, data[name]["value"], data[name]["metadata"]);
79
+ else if ("length" in data[name])
80
+ encoder = this.writeField(field, encoder, data[name]["value"], { length: data[name]["length"] });
81
+ else
82
+ encoder = this.writeField(field, encoder, data[name]["value"]);
83
+ else
84
+ encoder = this.writeField(field, encoder, data[name]);
85
+ else
86
+ encoder = this.writeField(field, encoder, data[name]);
87
+ }
88
+ return { buffer: encoder.getBuffer(), packet };
89
+ }
90
+
91
+ private readField(
92
+ // Current reading state
93
+ packetId: number,
94
+ readObject: Record<string, any>,
95
+
96
+ // Current field
97
+ fieldName: string,
98
+ field: FieldNode,
99
+ decoder: BinaryDecoder
100
+ ): any {
101
+ switch (field.type) {
102
+ case "string": return decoder.readString();
103
+ case "boolean": return decoder.readBoolean();
104
+ case "byte": return decoder.readByte();
105
+ case "unsigned_byte": return decoder.readUByte();
106
+ case "short": return decoder.readShort();
107
+ case "unsigned_short": return decoder.readUShort();
108
+ case "int": return decoder.readInt();
109
+ case "long": return decoder.readLong();
110
+ case "float": return decoder.readFloat();
111
+ case "double": return decoder.readDouble();
112
+ case "var_int": return decoder.readVarInt();
113
+ case "var_long": return decoder.readVarLong();
114
+ case "uuid": return decoder.readUUID();
115
+ case "position": return decoder.readPosition();
116
+ case "array": {
117
+ let length;
118
+ if (!isNaN(Number(field.length)))
119
+ length = Number(field.length);
120
+ else
121
+ length = readObject[field.length];
122
+ if (length === undefined) throw new MissingPacketField(packetId, fieldName, field.length.toString());
123
+ return decoder.readArray(length, (decoder) => this.readField(packetId, readObject, fieldName, field.subType, decoder));
124
+ }
125
+ case "prefixed_array": return decoder.readPrefixedArray((decoder) => this.readField(packetId, readObject, fieldName, field.subType, decoder));
126
+ case "teleport_flag": return decoder.readTeleportFlag();
127
+ case "prefixed_optional": return decoder.readPrefixedOptional((decoder) => this.readField(packetId, readObject, fieldName, field.subType, decoder));
128
+ case "nbt": return decoder.readNBT();
129
+ case "lpvec3": return decoder.readLpVec3();
130
+ case "fixed_point": {
131
+ const value = this.readField(packetId, readObject, fieldName, field.subType, decoder);
132
+ return decoder.readFixedPoint(value, field.fractionBits);
133
+ }
134
+ case "angle": return decoder.readAngle();
135
+ case "id_or_x": return decoder.readIdOrX((decoder) => this.readField(packetId, readObject, fieldName, field.subType, decoder));
136
+ case "id_set": return decoder.readIdSet();
137
+ case "chat_type_decoration": return decoder.readChatTypeDecoration();
138
+ case "chat_type": return decoder.readChatType();
139
+ case "enum": return this.readField(packetId, readObject, fieldName, field.subType, decoder);
140
+ case "json_text": return JSON.parse(decoder.readString());
141
+ case "object": {
142
+ const obj: Record<string, any> = {};
143
+ for (const [key, type] of Object.entries(field.fields))
144
+ obj[key] = this.readField(packetId, readObject, key, type, decoder);
145
+ return obj;
146
+ }
147
+ case "game_profile": {
148
+ return {
149
+ uuid: decoder.readUUID(),
150
+ username: decoder.readString(),
151
+ pproperties: decoder.readPrefixedArray((decoder) => ({
152
+ name: decoder.readString(),
153
+ value: decoder.readString(),
154
+ signature: decoder.readPrefixedOptional((decoder) => decoder.readString())
155
+ }))
156
+ };
157
+ }
158
+ case "heightmap": {
159
+ return {
160
+ type: decoder.readVarInt(),
161
+ data: decoder.readPrefixedArray((decoder) => decoder.readLong())
162
+ };
163
+ }
164
+ case "bitset": return decoder.readPrefixedArray((decoder) => decoder.readLong());
165
+ case "teleport_flags": return decoder.readTeleportFlag();
166
+ case "null": return null;
167
+ case "not_implemented": throw new NotImplemented();
168
+ case "switch": {
169
+ if (!(field.dependsOn in readObject)) throw new MissingPacketField(packetId, fieldName, field.dependsOn);
170
+ const dependencyValue = readObject[field.dependsOn];
171
+ for (const [value, type] of field.cases)
172
+ if (dependencyValue === value)
173
+ return this.readField(packetId, readObject, fieldName, type, decoder);
174
+ if (field.default)
175
+ return this.readField(packetId, readObject, fieldName, field.default, decoder);
176
+ return null;
177
+ }
178
+ }
179
+ }
180
+
181
+
182
+ private writeField(
183
+ field: FieldNode,
184
+ encoder: BinaryEncoder,
185
+ value: any,
186
+ metadata: Record<string, any> = {}
187
+ ): BinaryEncoder {
188
+ switch (field.type) {
189
+ case "string": return encoder.writeString(value);
190
+ case "boolean": return encoder.writeBoolean(value);
191
+ case "byte": return encoder.writeByte(value);
192
+ case "unsigned_byte": return encoder.writeUByte(value);
193
+ case "short": return encoder.writeShort(value);
194
+ case "unsigned_short": return encoder.writeUShort(value);
195
+ case "int": return encoder.writeInt(value);
196
+ case "long": return encoder.writeLong(value);
197
+ case "float": return encoder.writeFloat(value);
198
+ case "double": return encoder.writeDouble(value);
199
+ case "var_int": return encoder.writeVarInt(value);
200
+ case "var_long": return encoder.writeVarLong(value);
201
+ case "uuid": return encoder.writeRaw(value);
202
+ case "position": return encoder.writePosition(value);
203
+ // case "array": {
204
+ // const length = metadata["length"];
205
+ // // if (!length) throw new MissingField(packetId, fieldName, field.length);
206
+ // return encoder.writeArray(length, (decoder) => this.readField(packetId, readObject, fieldName, field.subType, decoder));
207
+ // }
208
+ case "prefixed_array": {
209
+ const length = metadata["length"] || field.length;
210
+ if (!length) throw new MissingField("length", "prefixed array metadata object");
211
+ if (!Array.isArray(value))
212
+ throw new UnexpectedValue("an array for prefixed array", typeof value);
213
+ return encoder.writePrefixedArray(length, (encoder, index) => this.writeField(field.subType, encoder, value[index], metadata));
214
+ }
215
+ // case "teleport_flag": return encoder.writeTeleportFlag();
216
+ // case "prefixed_optional": return encoder.writePrefixedOptional((decoder) => this.readField(packetId, readObject, fieldName, field.subType, decoder));
217
+ // case "nbt": return encoder.writeNBT(value);
218
+ case "lpvec3": return encoder.writeLpVec3(value);
219
+ // case "fixed_point": {
220
+ // const value = this.readField(packetId, readObject, fieldName, field.subType, encoder);
221
+ // return encoder.writeFixedPoint(value, field.fractionBits);
222
+ // }
223
+ // case "angle": return encoder.writeAngle();
224
+ // case "id_or_x": return encoder.writeIdOrX((decoder) => this.readField(packetId, readObject, fieldName, field.subType, decoder));
225
+ // case "chat_type_decoration": return encoder.writeChatTypeDecoration();
226
+ // case "chat_type": return encoder.writeChatType();
227
+ case "enum": return this.writeField(field.subType, encoder, value, metadata);
228
+ case "json_text": return encoder.writeString(JSON.parse(value));
229
+ case "object": {
230
+ for (const [name, subField] of Object.entries(field.fields))
231
+ encoder = this.writeField(subField, encoder, value[name], metadata);
232
+ return encoder;
233
+ }
234
+ // case "switch": {
235
+ // const dependencyValue = readObject[field.dependsOn];
236
+ // if (!dependencyValue) throw new MissingField(packetId, fieldName, field.dependsOn);
237
+ // for (const [value, type] of field.cases)
238
+ // if (dependencyValue === value)
239
+ // return this.readField(packetId, readObject, fieldName, type, encoder);
240
+ // if (field.default)
241
+ // return this.readField(packetId, readObject, fieldName, field.default, encoder);
242
+ // break;
243
+ // }
244
+ default:
245
+ throw new UnexpectedValue("encoding type", field.type);
246
+ }
247
+ }
248
+ }
@@ -0,0 +1,328 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { RegistryItemNotFound, UnexpectedValue, VersionNotSupport } from "../base/error";
4
+ import { Block, BlockState } from "../world/block";
5
+ import z from "zod";
6
+
7
+ export const BASE_REGISTRY_PATH = join(__dirname, "..", "..", "assets", "minecraft");
8
+ export const SupportVersions = [
9
+ "26.2"
10
+ ];
11
+
12
+ export const ProtocolVersionMapping: Record<string, number> = {
13
+ "26.2": 776
14
+ };
15
+
16
+ export class EntityRegistry {
17
+ private static loaded: boolean = false;
18
+ public static readonly data: Record<string, { height: number, width: number, type: number }> = {};
19
+ public static readonly mapTypeToData: Record<number, string> = {};
20
+
21
+ /**
22
+ * Load entity registry from JSON file.
23
+ *
24
+ * Should be called once time
25
+ */
26
+ public static async load(version: string) {
27
+ if (!SupportVersions.includes(version))
28
+ throw new VersionNotSupport(version);
29
+ if (this.loaded) return;
30
+ this.loaded = true;
31
+
32
+ const file = await readFile(join(BASE_REGISTRY_PATH, version, "entities.json"), { encoding: "utf8" });
33
+ const json = JSON.parse(file);
34
+ for (const entity in json) {
35
+ this.data[entity] = json[entity];
36
+ this.mapTypeToData[json[entity]["id"]] = entity;
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Get an entity registy item
42
+ */
43
+ public static get(entity: string | number) {
44
+ if (typeof entity === "string") {
45
+ return this.data[entity];
46
+ } else {
47
+ const key = this.mapTypeToData[entity];
48
+ return this.data[key!];
49
+ }
50
+ }
51
+ };
52
+
53
+ export class BlockRegistry {
54
+ private static loaded: boolean = false;
55
+ /**
56
+ * block id to block
57
+ */
58
+ public static readonly blocks: Record<string, Block> = {};
59
+ /**
60
+ * state id to block state
61
+ */
62
+ public static readonly states: Record<string, BlockState> = {};
63
+
64
+ /**
65
+ * Load block registry from JSON file.
66
+ *
67
+ * Should be called once time
68
+ */
69
+ public static async load(version: string) {
70
+ if (!SupportVersions.includes(version))
71
+ throw new VersionNotSupport(version);
72
+ if (this.loaded) return;
73
+ this.loaded = true;
74
+
75
+ const file = await readFile(join(BASE_REGISTRY_PATH, version, "blocks.json"), { encoding: "utf8" });
76
+ const json = JSON.parse(file) as Record<string, any>;
77
+ for (const [type, blockRaw] of Object.entries(json)) {
78
+ const block = new Block(type, blockRaw['definition'], blockRaw['properties'], blockRaw['states']);
79
+ this.blocks[type] = block;
80
+ for (const state of block.states)
81
+ this.states[state.id] = state;
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Get a block registy item
87
+ */
88
+ public static getBlock(type: string) {
89
+ if (!(type in this.blocks))
90
+ throw new RegistryItemNotFound(`block ${type}`);
91
+ return this.blocks[type]!;
92
+ }
93
+
94
+ /**
95
+ * Get a block state registy item
96
+ */
97
+ public static getState(id: string) {
98
+ if (!(id in this.states))
99
+ throw new RegistryItemNotFound(`block state ${id}`);
100
+ return this.states[id]!;
101
+ }
102
+ };
103
+
104
+ export const SupportTypes = z.enum([
105
+ "byte",
106
+ "unsigned_byte",
107
+ "short",
108
+ "unsigned_short",
109
+ "int",
110
+ "long",
111
+ "float",
112
+ "double",
113
+ "boolean",
114
+ "var_int",
115
+ "var_long",
116
+ "string",
117
+ "uuid",
118
+ "position",
119
+ "array",
120
+ "prefixed_array",
121
+ "teleport_flag",
122
+ "prefixed_optional",
123
+ "nbt",
124
+ "lpvec3",
125
+ "fixed_point",
126
+ "angle",
127
+ "id_or_x",
128
+ "id_set",
129
+ "chat_type_decoration",
130
+ "chat_type",
131
+ "enum",
132
+ "json_text",
133
+ "not_implemented",
134
+ "varies",
135
+ "object",
136
+ "game_profile",
137
+ "heightmap",
138
+ "teleport_flags",
139
+ "bitset",
140
+ "null", // a constant
141
+
142
+ /**
143
+ * This is used for handle field that its type depends on previous field type, or value
144
+ */
145
+ "switch"
146
+ ]);
147
+ export type FieldNode = {
148
+ type: Exclude<z.infer<typeof SupportTypes>, "prefixed_array" | "prefixed_optional" | "string" | "id_or_x" | "array" | "switch" | "fixed_point" | "enum" | "object" | "not_implemented">,
149
+ } | {
150
+ type: "not_implemented",
151
+ comment?: string | undefined
152
+ } | {
153
+ type: "object",
154
+ fields: Record<string, FieldNode>
155
+ } | {
156
+ type: "prefixed_array",
157
+ subType: FieldNode,
158
+ length?: number | undefined,
159
+ } | {
160
+ type: "id_or_x" | "enum" | "prefixed_optional",
161
+ subType: FieldNode
162
+ } | {
163
+ type: "string",
164
+ length?: number | undefined
165
+ } | {
166
+ type: "array",
167
+ subType: FieldNode,
168
+ length: string | number,
169
+ } | {
170
+ type: "switch",
171
+ dependsOn: string,
172
+ cases: [string, FieldNode][],
173
+ default?: FieldNode | undefined
174
+ } | {
175
+ type: "fixed_point",
176
+ subType: FieldNode,
177
+ fractionBits: number
178
+ }
179
+ export const Field: z.ZodType<FieldNode> = z.union([
180
+ z.object({
181
+ type: SupportTypes.exclude(["prefixed_array", "prefixed_optional", "string", "id_or_x", "array", "switch", "fixed_point", "enum", "not_implemented", "object"]),
182
+ }),
183
+ z.object({
184
+ type: z.literal(["not_implemented"]),
185
+ comment: z.string().optional()
186
+ }),
187
+ z.object({
188
+ type: z.literal(["object"]),
189
+ fields: z.record(z.string(), z.lazy(() => Field))
190
+ }),
191
+ z.object({
192
+ type: z.literal(["prefixed_array"]),
193
+ /**
194
+ * For case like `Array<subType>` or `PrefixedOptional<subType>`
195
+ */
196
+ subType: z.lazy(() => Field),
197
+ /**
198
+ * The length of the array type, not used.
199
+ */
200
+ length: z.number().optional()
201
+ }),
202
+ z.object({
203
+ type: z.literal(["id_or_x", "enum", "prefixed_optional"]),
204
+ /**
205
+ * For case like `Array<subType>` or `PrefixedOptional<subType>`
206
+ */
207
+ subType: z.lazy(() => Field),
208
+ }),
209
+ z.object({
210
+ type: z.literal("string"),
211
+ /**
212
+ * The length of the string, not used.
213
+ */
214
+ length: z.number().optional(),
215
+ }),
216
+ z.object({
217
+ type: z.literal("array"),
218
+ /**
219
+ * For case like `Array<subType>` or `PrefixedOptional<subType>`
220
+ */
221
+ subType: z.lazy(() => Field),
222
+ /**
223
+ * Specially for `array` type, which is not prefixed with length.
224
+ *
225
+ * Point to another field, should be numberic field
226
+ */
227
+ length: z.string().or(z.number()),
228
+ }),
229
+ z.object({
230
+ type: z.literal("switch"),
231
+ /**
232
+ * Specially for case that this field type depends on other field value.
233
+ *
234
+ * Point to the field that this field is depended on.
235
+ */
236
+ dependsOn: z.string(),
237
+ /**
238
+ * Mapping depend value to type
239
+ */
240
+ cases: z.array(z.tuple([z.any(), z.lazy(() => Field)])),
241
+ default: z.lazy(() => Field).optional(),
242
+ }),
243
+ z.object({
244
+ type: z.literal(["fixed_point"]),
245
+ /**
246
+ * For case like `Array<subType>` or `PrefixedOptional<subType>`
247
+ */
248
+ subType: z.lazy(() => Field),
249
+ fractionBits: z.number()
250
+ }),
251
+
252
+ ]);
253
+ export const PacketObject = z.object({
254
+ name: z.string(),
255
+ id: z.number(),
256
+ structure: z.record(z.string(), Field),
257
+ skipForNow: z.boolean().default(false)
258
+ });
259
+ export const PacketsDefinition = z.record(z.string().or(z.number()), PacketObject);
260
+ export const StatesDefinition = z.object({
261
+ clientbound: PacketsDefinition,
262
+ serverbound: PacketsDefinition,
263
+ });
264
+ export const VersionDefinitions = z.object({
265
+ handshaking: StatesDefinition,
266
+ status: StatesDefinition,
267
+ login: StatesDefinition,
268
+ configuration: StatesDefinition,
269
+ play: StatesDefinition,
270
+ });
271
+
272
+ export class PacketRegistry {
273
+ private static loaded: boolean = false;
274
+ private static definition: z.infer<typeof VersionDefinitions>;
275
+ private static nameToId: Record<
276
+ string, // State
277
+ Record<
278
+ string, // Type
279
+ Record<string, number> // Packet
280
+ >
281
+ > = {};
282
+
283
+ /**
284
+ * Load block registry from JSON file.
285
+ *
286
+ * Should be called once time
287
+ */
288
+ public static async load(version: string) {
289
+ if (!SupportVersions.includes(version))
290
+ throw new VersionNotSupport(version);
291
+ if (this.loaded) return;
292
+ this.loaded = true;
293
+
294
+ const file = await readFile(join(BASE_REGISTRY_PATH, version, "packets.json"), { encoding: "utf8" });
295
+ const json = JSON.parse(file) as Record<string, any>;
296
+
297
+ const definition = VersionDefinitions.parse(json);
298
+ for (const [stateKey, state] of Object.entries(definition)) {
299
+ this.nameToId[stateKey] = {};
300
+ for (const [typeKey, type] of Object.entries(state)) {
301
+ this.nameToId[stateKey]![typeKey] = {};
302
+ // eslint-disable-next-line prefer-const
303
+ for (let [key, value] of Object.entries(type) as [string | number, any]) {
304
+ this.nameToId[stateKey]![typeKey]![value.name] = key;
305
+ }
306
+ }
307
+ }
308
+ this.definition = definition;
309
+ }
310
+
311
+ public static getPacket(
312
+ state: keyof z.infer<typeof VersionDefinitions>,
313
+ type: keyof z.infer<typeof StatesDefinition>,
314
+ id: string | number
315
+ ): z.infer<typeof PacketObject> | undefined {
316
+ const nameToId = this.nameToId[state]?.[type]?.[id]?.toString();
317
+ if (nameToId)
318
+ return this.definition[state][type][nameToId]!;
319
+ let numbericId;
320
+ if (typeof id === "string")
321
+ numbericId = parseInt(id, 16);
322
+ else numbericId = id;
323
+ if (isNaN(numbericId))
324
+ throw new UnexpectedValue("valid packet id", id.toString(), "name or number or hex");
325
+ else
326
+ return this.definition[state][type]["0x" + numbericId.toString(16).toUpperCase()];
327
+ }
328
+ }
@@ -1,8 +1,9 @@
1
1
  import { TCPClient } from "../client/tcp";
2
- import { AABB, BaseAABB, VoxelShape } from "./aabb";
3
- import { Epsilon, getFrac, getSign, lerp } from "./math";
4
- import { BlockRegistry } from "./registry";
5
- import { BaseVec3, Direction, Vec3 } from "./direction";
2
+ import { AABB, BaseAABB, VoxelShape } from "../physics/aabb";
3
+ import { Epsilon, getFrac, getSign, lerp } from "../base/math";
4
+ import { BlockRegistry } from "../version/registry";
5
+ import { BaseVec3, Direction, Vec3 } from "../physics/direction";
6
+ import { SharedState } from "./state";
6
7
 
7
8
  /**
8
9
  * Represent a state of a block
@@ -76,7 +77,7 @@ export class Block {
76
77
  * Somehow, this is an equivalent implementation of Minecraft `Level`
77
78
  */
78
79
  export class BlockManager {
79
- constructor(private tcp: TCPClient) { }
80
+ constructor(private state: SharedState) { }
80
81
 
81
82
  /**
82
83
  * Check if the chunk section is loaded
@@ -84,14 +85,14 @@ export class BlockManager {
84
85
  public hasChunkAt(x: number, y: number, z: number): boolean;
85
86
  public hasChunkAt(position: BaseVec3): boolean;
86
87
  public hasChunkAt(a: BaseVec3 | number, b?: number, c?: number): boolean {
87
- this.tcp.checkReady();
88
+ this.state.checkReady();
88
89
  const { x, y, z } = Vec3.loadArgs(a, b, c);
89
90
  const sx = Math.floor(x / 16), sy = Math.floor((y + 64) / 16), sz = Math.floor(z / 16);
90
- // // console.log({
91
+ // console.log({
91
92
  // key: `${this.tcp.player!.dimension}:${sx}:${sz}`,
92
93
  // hasKey: `${this.tcp.player!.dimension}:${sx}:${sz}` in this.tcp.world!.chunks
93
94
  // });
94
- const section = this.tcp.world!.chunks[`${this.tcp.player!.dimension}:${sx}:${sz}`]?.sections[sy];
95
+ const section = this.state.world!.chunks[`${this.state.player!.dimension}:${sx}:${sz}`]?.sections[sy];
95
96
  if (!section) return false;
96
97
  return true;
97
98
  }
@@ -102,12 +103,12 @@ export class BlockManager {
102
103
  public at(x: number, y: number, z: number): BlockState | null;
103
104
  public at(position: BaseVec3): BlockState | null;
104
105
  public at(a: BaseVec3 | number, b?: number, c?: number): BlockState | null {
105
- this.tcp.checkReady();
106
+ this.state.checkReady();
106
107
  let { x, y, z } = Vec3.loadArgs(a, b, c);
107
108
  x = Math.floor(x); y = Math.floor(y); z = Math.floor(z);
108
109
  const sx = Math.floor(x / 16), sy = Math.floor((y + 64) / 16), sz = Math.floor(z / 16);
109
110
  const px = ((x % 16) + 16) % 16, py = ((y % 16) + 16) % 16, pz = ((z % 16) + 16) % 16;
110
- const section = this.tcp.world!.chunks[`${this.tcp.player!.dimension}:${sx}:${sz}`]?.sections[sy]?.block;
111
+ const section = this.state.world!.chunks[`${this.state.player!.dimension}:${sx}:${sz}`]?.sections[sy]?.block;
111
112
  if (!section) return null;
112
113
  if (section.data === null) return BlockRegistry.getState(section.palette[0]!.toString())!;
113
114
  const dataArray = section.data!;
@@ -128,7 +129,7 @@ export class BlockManager {
128
129
  * @returns
129
130
  */
130
131
  public queryAABB(queryBB: AABB) {
131
- this.tcp.checkReady();
132
+ this.state.checkReady();
132
133
 
133
134
  const { minX, minY, minZ, maxX, maxY, maxZ } = queryBB;
134
135
  const sx0 = Math.floor(minX), sx1 = Math.floor(maxX);
@@ -1,5 +1,5 @@
1
- import { TCPClient } from "../client/tcp";
2
- import { AABB } from "./aabb";
1
+ import { AABB } from "../physics/aabb";
2
+ import { SharedState } from "./state";
3
3
 
4
4
  export class EntitiesManager {
5
5
  /**
@@ -13,7 +13,7 @@ export class EntitiesManager {
13
13
  */
14
14
  public entityToSection: Map<number, string> = new Map();
15
15
 
16
- constructor(private tcp: TCPClient) { }
16
+ constructor(private state: SharedState) { }
17
17
 
18
18
  /**
19
19
  * Clear data
@@ -70,7 +70,7 @@ export class EntitiesManager {
70
70
  * @returns
71
71
  */
72
72
  public queryAABB(queryBB: AABB, exclude: number[] = []) {
73
- this.tcp.checkReady();
73
+ this.state.checkReady();
74
74
 
75
75
  const { minX, minY, minZ, maxX, maxY, maxZ } = queryBB;
76
76
  const results: AABB[] = [];
@@ -84,7 +84,7 @@ export class EntitiesManager {
84
84
  const bucket = this.sections.get(`${sx},${sy},${sz}`);
85
85
  if (bucket) for (const id of bucket) {
86
86
  if (exclude.includes(id)) continue;
87
- const entity = this.tcp.world!.entities[id];
87
+ const entity = this.state.world!.entities[id];
88
88
  const entityBB = AABB.fromEntityType(entity!.type);
89
89
  if (queryBB.isIntersect(entityBB))
90
90
  results.push(entityBB);