@vaitosoi/minejs 0.3.1 → 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 (78) hide show
  1. package/README.md +1 -1
  2. package/assets/minecraft/26.2/packets.json +27 -10
  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 +33 -6
  8. package/dist/base/error.d.ts.map +1 -1
  9. package/dist/base/error.js +74 -11
  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/client/auth.d.ts +41 -7
  20. package/dist/client/auth.d.ts.map +1 -1
  21. package/dist/client/auth.js +96 -39
  22. package/dist/client/auth.js.map +1 -1
  23. package/dist/client/client.d.ts +50 -5
  24. package/dist/client/client.d.ts.map +1 -1
  25. package/dist/client/client.js +47 -18
  26. package/dist/client/client.js.map +1 -1
  27. package/dist/client/tcp.d.ts +1 -5
  28. package/dist/client/tcp.d.ts.map +1 -1
  29. package/dist/client/tcp.js +8 -21
  30. package/dist/client/tcp.js.map +1 -1
  31. package/dist/message/link.d.ts +10 -0
  32. package/dist/message/link.d.ts.map +1 -0
  33. package/dist/message/link.js +22 -0
  34. package/dist/message/link.js.map +1 -0
  35. package/dist/message/sigCache.d.ts +13 -0
  36. package/dist/message/sigCache.d.ts.map +1 -0
  37. package/dist/message/sigCache.js +26 -0
  38. package/dist/message/sigCache.js.map +1 -0
  39. package/dist/packet/decoder.d.ts +13 -0
  40. package/dist/packet/decoder.d.ts.map +1 -1
  41. package/dist/packet/decoder.js +21 -0
  42. package/dist/packet/decoder.js.map +1 -1
  43. package/dist/packet/dispatcher.d.ts +8 -1
  44. package/dist/packet/dispatcher.d.ts.map +1 -1
  45. package/dist/packet/dispatcher.js +142 -30
  46. package/dist/packet/dispatcher.js.map +1 -1
  47. package/dist/physics/player.d.ts +17 -23
  48. package/dist/physics/player.d.ts.map +1 -1
  49. package/dist/physics/player.js +59 -61
  50. package/dist/physics/player.js.map +1 -1
  51. package/dist/version/codec.d.ts.map +1 -1
  52. package/dist/version/codec.js +28 -7
  53. package/dist/version/codec.js.map +1 -1
  54. package/dist/version/registry.d.ts +1 -1
  55. package/dist/version/registry.d.ts.map +1 -1
  56. package/dist/version/registry.js +2 -7
  57. package/dist/version/registry.js.map +1 -1
  58. package/dist/world/state.d.ts +26 -2
  59. package/dist/world/state.d.ts.map +1 -1
  60. package/dist/world/state.js +58 -3
  61. package/dist/world/state.js.map +1 -1
  62. package/package.json +1 -1
  63. package/src/base/buffer.ts +12 -0
  64. package/src/base/error.ts +63 -10
  65. package/src/base/http.ts +10 -0
  66. package/src/base/math.ts +55 -3
  67. package/src/client/auth.ts +130 -52
  68. package/src/client/client.ts +96 -21
  69. package/src/client/tcp.ts +10 -27
  70. package/src/message/link.ts +17 -0
  71. package/src/message/sigCache.ts +25 -0
  72. package/src/packet/decoder.ts +21 -0
  73. package/src/packet/dispatcher.ts +169 -35
  74. package/src/physics/player.ts +83 -58
  75. package/src/version/codec.ts +32 -11
  76. package/src/version/registry.ts +2 -7
  77. package/src/world/state.ts +72 -4
  78. package/test/client/index.ts +27 -20
@@ -1,9 +1,8 @@
1
- import z, { file } from "zod";
2
- import { MissingField, MissingPacketField, UnexpectedValue } from "../base/error";
1
+ import z from "zod";
2
+ import { MissingField, MissingPacketField, NotImplemented, UnexpectedValue } from "../base/error";
3
3
  import { BinaryDecoder } from "../packet/decoder";
4
4
  import { ConnectionState, SharedState } from "../world/state";
5
5
  import { FieldNode, PacketObject, PacketRegistry, VersionDefinitions } from "./registry";
6
- import { Dispatcher } from "../packet/dispatcher";
7
6
  import { BinaryEncoder } from "../packet/encoder";
8
7
 
9
8
  const TCPStateMapping: Record<ConnectionState, keyof z.infer<typeof VersionDefinitions> | null> = {
@@ -22,7 +21,7 @@ export interface EncodeResult {
22
21
  export class VersionCodec {
23
22
  constructor(private readonly state: SharedState) { }
24
23
 
25
- public consumePacket: (packetId: string, data: object) => void = (...args) => { throw new Error("method not implemented"); };
24
+ public consumePacket: (packetId: string, data: object) => void = () => { throw new NotImplemented(); };
26
25
 
27
26
  public handlePacket(packetId: number, decoder: BinaryDecoder) {
28
27
  const mappedState = TCPStateMapping[this.state.state];
@@ -31,13 +30,25 @@ export class VersionCodec {
31
30
  if (!packet) return; // console.log("no packet");
32
31
 
33
32
  const { name, structure, skipForNow } = packet;
33
+
34
34
  if (skipForNow) return;
35
- // console.log(`> packet ${name}`);
35
+
36
36
  const resolvedObject: Record<string, any> = {};
37
- for (const [name, field] of Object.entries(structure)) {
38
- // console.log({ name, resolvedObject });
39
- resolvedObject[name] = this.readField(packetId, resolvedObject, name, field, decoder);
40
- }
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
+ });
41
52
 
42
53
  this.consumePacket(`${TCPStateMapping[this.state.state]}:${name}`, resolvedObject);
43
54
  }
@@ -48,6 +59,14 @@ export class VersionCodec {
48
59
  const packet = PacketRegistry.getPacket(mappedState, "serverbound", packetId);
49
60
  if (!packet) throw new UnexpectedValue("valid serverbound packet id", packetId.toString());
50
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
+
51
70
  const { name: packetName, structure } = packet;
52
71
  let encoder = new BinaryEncoder();
53
72
  for (const [name, field] of Object.entries(structure)) {
@@ -98,7 +117,7 @@ export class VersionCodec {
98
117
  let length;
99
118
  if (!isNaN(Number(field.length)))
100
119
  length = Number(field.length);
101
- else
120
+ else
102
121
  length = readObject[field.length];
103
122
  if (length === undefined) throw new MissingPacketField(packetId, fieldName, field.length.toString());
104
123
  return decoder.readArray(length, (decoder) => this.readField(packetId, readObject, fieldName, field.subType, decoder));
@@ -114,6 +133,7 @@ export class VersionCodec {
114
133
  }
115
134
  case "angle": return decoder.readAngle();
116
135
  case "id_or_x": return decoder.readIdOrX((decoder) => this.readField(packetId, readObject, fieldName, field.subType, decoder));
136
+ case "id_set": return decoder.readIdSet();
117
137
  case "chat_type_decoration": return decoder.readChatTypeDecoration();
118
138
  case "chat_type": return decoder.readChatType();
119
139
  case "enum": return this.readField(packetId, readObject, fieldName, field.subType, decoder);
@@ -144,6 +164,7 @@ export class VersionCodec {
144
164
  case "bitset": return decoder.readPrefixedArray((decoder) => decoder.readLong());
145
165
  case "teleport_flags": return decoder.readTeleportFlag();
146
166
  case "null": return null;
167
+ case "not_implemented": throw new NotImplemented();
147
168
  case "switch": {
148
169
  if (!(field.dependsOn in readObject)) throw new MissingPacketField(packetId, fieldName, field.dependsOn);
149
170
  const dependencyValue = readObject[field.dependsOn];
@@ -185,7 +206,7 @@ export class VersionCodec {
185
206
  // return encoder.writeArray(length, (decoder) => this.readField(packetId, readObject, fieldName, field.subType, decoder));
186
207
  // }
187
208
  case "prefixed_array": {
188
- const length = metadata["length"];
209
+ const length = metadata["length"] || field.length;
189
210
  if (!length) throw new MissingField("length", "prefixed array metadata object");
190
211
  if (!Array.isArray(value))
191
212
  throw new UnexpectedValue("an array for prefixed array", typeof value);
@@ -125,9 +125,9 @@ export const SupportTypes = z.enum([
125
125
  "fixed_point",
126
126
  "angle",
127
127
  "id_or_x",
128
+ "id_set",
128
129
  "chat_type_decoration",
129
130
  "chat_type",
130
- "chunk_data",
131
131
  "enum",
132
132
  "json_text",
133
133
  "not_implemented",
@@ -301,11 +301,6 @@ export class PacketRegistry {
301
301
  this.nameToId[stateKey]![typeKey] = {};
302
302
  // eslint-disable-next-line prefer-const
303
303
  for (let [key, value] of Object.entries(type) as [string | number, any]) {
304
- if (typeof key === "string" && !isNaN(parseInt(key))) {
305
- type[parseInt(key)] = value;
306
- delete type[key];
307
- key = parseInt(key);
308
- } // transform all string key into numberic key
309
304
  this.nameToId[stateKey]![typeKey]![value.name] = key;
310
305
  }
311
306
  }
@@ -328,6 +323,6 @@ export class PacketRegistry {
328
323
  if (isNaN(numbericId))
329
324
  throw new UnexpectedValue("valid packet id", id.toString(), "name or number or hex");
330
325
  else
331
- return this.definition[state][type][numbericId.toString()];
326
+ return this.definition[state][type]["0x" + numbericId.toString(16).toUpperCase()];
332
327
  }
333
328
  }
@@ -1,10 +1,11 @@
1
1
  import { Cipheriv, Decipheriv } from "node:crypto";
2
2
  import { If } from "../base/typing";
3
3
  import { Angle, BaseVec3 } from "../physics/direction";
4
- import { ClientNotReady } from "../base/error";
4
+ import { AuthRelatedNotFound, ClientNotReady } from "../base/error";
5
5
  import { ClientEvents, ClientOption } from "../client/client";
6
- import { TypedEmmiter } from "../base/event";
7
- import { TCPClient } from "../client/tcp";
6
+ import { AuthClient } from "../client/auth";
7
+ import { MessageLink } from "../message/link";
8
+ import { SignatureCache } from "../message/sigCache";
8
9
 
9
10
  export interface Server {
10
11
  knownPacks?: ServerKnownPack
@@ -146,18 +147,36 @@ export class SharedState<IsReady extends boolean = boolean> {
146
147
  public status: ClientStatus = ClientStatus.Disconnected;
147
148
  public compressionThreshold: number = 0;
148
149
 
150
+ // Auth stuffs
151
+ public authClient: AuthClient | undefined = undefined;
152
+ public playerUUID: string | undefined = undefined;
153
+ public publicKey: Buffer | undefined = undefined;
154
+ public signature: Buffer | undefined = undefined;
155
+ public signatureExpireTime: number | undefined = undefined;
156
+ // eslint-disable-next-line no-undef
157
+ private signatureTimeoutInstance: NodeJS.Timeout | undefined = undefined;
158
+
149
159
  public sharedSecret: Buffer | undefined = undefined;
150
160
  public cipher: Cipheriv | undefined = undefined;
151
161
  public decipher: Decipheriv | undefined = undefined;
162
+ public useEncryption: boolean = false;
163
+ public sessionID: Buffer | undefined = undefined;
152
164
 
153
165
  public server: Server | undefined = undefined;
154
166
  public registry: Record<string, ServerRegistryEntry[]> | undefined = undefined;
155
167
  public world: If<IsReady, ServerWorld> = null as any;
156
168
  public player: If<IsReady, ClientPlayer> = null as any;
157
169
 
170
+ /**
171
+ * For maintaining message order.
172
+ *
173
+ * A UUID map to a MessageLink.
174
+ */
175
+ public messageLinks: Record<string, MessageLink> | undefined = undefined;
176
+ public messageSignatureCache: SignatureCache | undefined = undefined;
177
+
158
178
  constructor(
159
179
  public clientOptions: ClientOption,
160
- public getUuid: () => Buffer,
161
180
  ) { }
162
181
 
163
182
  public isReady(): this is SharedState<true> {
@@ -173,15 +192,64 @@ export class SharedState<IsReady extends boolean = boolean> {
173
192
  this.mutationQueue = [];
174
193
  this.eventQueue = [];
175
194
  this.sendQueue = [];
195
+
176
196
  this.state = ConnectionState.Disconnected;
197
+ this.status = ClientStatus.Disconnected;
177
198
  this.compressionThreshold = 0;
199
+
200
+ this.authClient = undefined;
201
+ this.playerUUID = undefined;
202
+ this.publicKey = undefined;
203
+ this.signature = undefined;
204
+
178
205
  this.sharedSecret = undefined;
179
206
  this.cipher = undefined;
180
207
  this.decipher = undefined;
208
+ this.useEncryption = false;
209
+ this.sessionID = undefined;
210
+ this.signatureTimeoutInstance = undefined;
211
+
181
212
  this.server = undefined;
182
213
  this.registry = undefined;
183
214
  this.world = undefined as any;
184
215
  this.player = undefined as any;
216
+
217
+ if (this.clientOptions.shouldVerifyMessageSignature)
218
+ this.messageSignatureCache = SignatureCache.default();
219
+ else
220
+ this.messageSignatureCache = undefined;
221
+ if (this.clientOptions.shouldVerifyMessageOrder)
222
+ this.messageLinks = {};
223
+ else
224
+ this.messageLinks = undefined;
225
+ }
226
+
227
+ public getSignature() {
228
+ if (this.publicKey && this.signature)
229
+ return {
230
+ publicKey: this.publicKey,
231
+ signature: this.signature
232
+ };
233
+ throw new AuthRelatedNotFound("public key and signature");
234
+ }
235
+
236
+ private async fetchSignature() {
237
+ if (!this.authClient)
238
+ throw new AuthRelatedNotFound("instance of class AuthClient");
239
+ const { publicKey, signature, expiresAt } = await this.authClient.getKeyPair();
240
+ this.publicKey = Buffer.from(publicKey);
241
+ this.signature = Buffer.from(signature);
242
+ return expiresAt;
243
+ }
244
+ public async startSignatureLoop() {
245
+ const expiresAt = await this.fetchSignature();
246
+ this.signatureTimeoutInstance = setTimeout(
247
+ async () => await this.startSignatureLoop(),
248
+ expiresAt - Date.now() - 10_000
249
+ );
250
+ }
251
+ public stopSignatureLoop() {
252
+ clearTimeout(this.signatureTimeoutInstance);
185
253
  }
186
254
 
187
255
  public enqueueMutation(mutation: MutateState) { this.mutationQueue.push(mutation); }
@@ -1,22 +1,25 @@
1
- import { MoveDirection, Client } from "../../src/";
1
+ import { Input, Client } from "../../src/index";
2
2
 
3
3
  const client = new Client({
4
4
  playerName: "bot",
5
5
  host: "localhost",
6
6
  port: 25565,
7
7
  version: "26.2",
8
- // debug: {
9
- // packetLogger: true
10
- // },
11
- isOffline: true,
8
+ debug: {
9
+ packetLogger: true
10
+ },
12
11
  loadRegistry: false,
13
- // auth: {
14
- // client_id: "d86254d8-edf7-4640-90eb-643c99af188e",
15
- // method: "loopback",
16
- // openBrowser: true
17
- // }
12
+ auth: {
13
+ client_id: "d86254d8-edf7-4640-90eb-643c99af188e",
14
+ method: "loopback",
15
+ openBrowser: true,
16
+ port: 12345
17
+ },
18
+ shouldVerifyMessageOrder: true,
19
+ shouldVerifyMessageSignature: true
18
20
  });
19
21
  client.connect();
22
+ client.hold(Input.Jump);
20
23
  client.on("disconnect", (...args) => console.dir({ name: "disconnect", args }, { depth: null }));
21
24
  client.on("disconnectRaw", (...args) => console.dir({ name: "disconnectRaw", args }, { depth: null }));
22
25
  // client.on("loadChunk", (...args) => console.dir({ name: "loadChunk", args }, { depth: null }));
@@ -34,18 +37,22 @@ client.on("message", (message) => {
34
37
  if (!message.content.startsWith("_")) return;
35
38
  const args = message.content.slice(1).split(" ");
36
39
  switch (args[0]) {
37
- case "w": client.hold(MoveDirection.Forward); break;
38
- case "sw": client.release(MoveDirection.Forward); break;
40
+ case "w": client.hold(Input.Forward); break;
41
+ case "sw": client.release(Input.Forward); break;
39
42
  case "ws":
40
- client.hold(MoveDirection.Forward);
43
+ client.hold(Input.Forward);
41
44
  setTimeout(() => client.disconnect(), 2000);
42
45
  break;
43
- case "s": client.hold(MoveDirection.Backward); break;
44
- case "ss": client.release(MoveDirection.Backward); break;
45
- case "a": client.hold(MoveDirection.Left); break;
46
- case "sa": client.release(MoveDirection.Left); break;
47
- case "d": client.hold(MoveDirection.Right); break;
48
- case "sd": client.release(MoveDirection.Right); break;
46
+ case "s": client.hold(Input.Backward); break;
47
+ case "ss": client.release(Input.Backward); break;
48
+ case "a": client.hold(Input.Left); break;
49
+ case "sa": client.release(Input.Left); break;
50
+ case "d": client.hold(Input.Right); break;
51
+ case "sd": client.release(Input.Right); break;
52
+ case "j": client.hold(Input.Jump); break;
53
+ case "sj": client.release(Input.Jump); break;
54
+ case "run": client.hold(Input.Sprint); break;
55
+ case "srun": client.release(Input.Sprint); break;
49
56
  case "stop": client.stopMoving(); break;
50
57
 
51
58
  case "at": {
@@ -56,7 +63,7 @@ client.on("message", (message) => {
56
63
  }
57
64
 
58
65
  case "disconnect":
59
- case "exit":
66
+ case "exit":
60
67
  client.disconnect();
61
68
  break;
62
69
  }