@vaitosoi/minejs 0.6.0 → 0.6.1

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 (60) hide show
  1. package/README.md +23 -11
  2. package/assets/minecraft/26.2/packets.json +0 -12
  3. package/assets/minecraft/26.3/packets.json +228 -38
  4. package/dist/binary/encoder.d.ts.map +1 -1
  5. package/dist/binary/encoder.js +4 -4
  6. package/dist/binary/encoder.js.map +1 -1
  7. package/dist/client/client.d.ts +127 -28
  8. package/dist/client/client.d.ts.map +1 -1
  9. package/dist/client/client.js +108 -27
  10. package/dist/client/client.js.map +1 -1
  11. package/dist/net/tcp.d.ts +0 -7
  12. package/dist/net/tcp.d.ts.map +1 -1
  13. package/dist/net/tcp.js +12 -18
  14. package/dist/net/tcp.js.map +1 -1
  15. package/dist/packet/listener.d.ts +1 -1
  16. package/dist/packet/listener.d.ts.map +1 -1
  17. package/dist/packet/listener.js +142 -55
  18. package/dist/packet/listener.js.map +1 -1
  19. package/dist/packet/sender.d.ts +2 -1
  20. package/dist/packet/sender.d.ts.map +1 -1
  21. package/dist/packet/sender.js +17 -6
  22. package/dist/packet/sender.js.map +1 -1
  23. package/dist/physics/aabb.d.ts.map +1 -1
  24. package/dist/physics/aabb.js +10 -2
  25. package/dist/physics/aabb.js.map +1 -1
  26. package/dist/physics/direction.js +2 -2
  27. package/dist/physics/direction.js.map +1 -1
  28. package/dist/version/codec.d.ts.map +1 -1
  29. package/dist/version/codec.js +38 -0
  30. package/dist/version/codec.js.map +1 -1
  31. package/dist/version/registry.d.ts +6 -1
  32. package/dist/version/registry.d.ts.map +1 -1
  33. package/dist/version/registry.js +26 -3
  34. package/dist/version/registry.js.map +1 -1
  35. package/dist/world/block.d.ts +44 -2
  36. package/dist/world/block.d.ts.map +1 -1
  37. package/dist/world/block.js +61 -21
  38. package/dist/world/block.js.map +1 -1
  39. package/dist/world/entity.d.ts +3 -0
  40. package/dist/world/entity.d.ts.map +1 -1
  41. package/dist/world/entity.js +3 -0
  42. package/dist/world/entity.js.map +1 -1
  43. package/dist/world/state.d.ts +3 -0
  44. package/dist/world/state.d.ts.map +1 -1
  45. package/dist/world/state.js +9 -0
  46. package/dist/world/state.js.map +1 -1
  47. package/package.json +2 -2
  48. package/src/binary/encoder.ts +5 -6
  49. package/src/client/client.ts +174 -51
  50. package/src/net/tcp.ts +12 -18
  51. package/src/packet/listener.ts +172 -70
  52. package/src/packet/sender.ts +18 -7
  53. package/src/physics/aabb.ts +11 -2
  54. package/src/physics/direction.ts +2 -2
  55. package/src/version/codec.ts +32 -0
  56. package/src/version/registry.ts +35 -5
  57. package/src/world/block.ts +64 -22
  58. package/src/world/entity.ts +3 -0
  59. package/src/world/state.ts +10 -0
  60. package/test/client/index.ts +13 -6
@@ -19,12 +19,15 @@ import { CacheImplementation } from "../base/cache";
19
19
  import { Container, Slot } from "../world/container";
20
20
 
21
21
  export type ClientOption = Omit<TCPClientOption, "protocolVersion"> & {
22
+ /**
23
+ * The client version. Supports 26.2 and 26.3
24
+ */
22
25
  version: string,
23
26
 
24
27
  /**
25
- * Whenever should the client verify the message against the signature, if any
28
+ * Whenever should the client verify the message against the signature, if any.
26
29
  *
27
- * When set this option to true, you HAVE TO set `shouldVerifyMessageOrder` to true
30
+ * In order to set this option to true, you HAVE TO set `shouldVerifyMessageOrder` to true
28
31
  */
29
32
  shouldVerifyMessageSignature?: boolean,
30
33
 
@@ -58,28 +61,50 @@ export interface ClientEvents {
58
61
  /**
59
62
  * When the client is ready.
60
63
  *
61
- * Specifically, when the packet `Login (play)` is received
64
+ * Specifically, when the packet `Login (play)` is received.
65
+ *
66
+ * Most of the client method require this event to be fired in order to work.
62
67
  */
63
68
  ready: [],
64
69
  /**
65
- * When the client got disconnected from sevrer
70
+ * When the client got disconnected from server.
71
+ *
72
+ * This event only provide the parsed string. To get the unparsed, or the raw one with decoration, use `disconnectRaw`.
73
+ * But, the `disconnectRaw` is only fired when the disconnection come from the server, or the server disconnect the client.
74
+ * Sometime, the disconnect will be fired within the client, meaning that there will be no `disconnectRaw`
66
75
  */
67
76
  disconnect: [reason: string],
68
77
  /**
69
- * When the disconection come with the raw Text Component
78
+ * When the disconnection come with the raw Text Component.
70
79
  */
71
80
  disconnectRaw: [textComponent: TextComponent],
72
81
 
82
+ /** The registry_data packet */
83
+ registryData: [data: RegistryData],
84
+
73
85
  /**
74
- * When server send `Player Position Synchronization` packet
86
+ * When server send `Player Position Synchronization` packet.
87
+ *
88
+ * **Note:** this event is not fired when the client is moved by holding the input.
75
89
  *
76
- * Note: this event is not fired when the client is moved by holding the input
90
+ * To get the player current position, use `getPosition` method
77
91
  */
78
92
  playerPosition: [position: BaseVec3],
79
93
 
80
- /** When the chunk is loaded */
94
+ /**
95
+ * When the chunk is loaded
96
+ *
97
+ * Explaining the `chunkX`, `sectionY`, `chunkZ`: A chunk in Minecraft is a region which length and width is 16 and the height extends to the limit.
98
+ * But when sending the packet, the chunk is divided into smaller section which volume is 16x16x16.
99
+ * So the `chunkX` and `chunkZ` is the chunk position and `sectionY` is the section within that chunk,
100
+ * counting from the bedrock up to the sky
101
+ */
81
102
  loadChunk: [chunkX: number, sectionY: number, chunkZ: number],
82
- /** When the chunk got deleted by the server */
103
+ /**
104
+ * When the chunk got deleted by the server
105
+ *
106
+ * See the `chunkX` and `chunkZ` explaination in `loadChunk` events
107
+ */
83
108
  unloadChunk: [chunkX: number, chunkZ: number],
84
109
 
85
110
  /** When an entity is spawned or appeared in the client view distance */
@@ -88,25 +113,38 @@ export interface ClientEvents {
88
113
  updateEntity: [entityId: number],
89
114
  /** When an entity go out of client view distance or despawned */
90
115
  removeEntity: [entityId: number],
91
- /** The registry_data packet */
92
- registryData: [data: RegistryData],
93
-
116
+
94
117
  // Chat
95
118
  /** When a chat message got broadcasted */
96
119
  message: [message: Message],
97
120
  /** When a message is failed to be verified against the signature sent with it */
98
121
  failedMessage: [message: Message],
99
- /** The system message */
122
+ /**
123
+ * The system message
124
+ *
125
+ * This event only provide the parsed string. To get the unparsed, or the raw one with decoration, use `systemMessageRaw`
126
+ */
100
127
  systemMessage: [message: string],
101
- /** The raw system message */
128
+ /**
129
+ * The system message with the Text Component for decorations
130
+ */
102
131
  systemMessageRaw: [textComponent: TextComponent],
103
- /** The action bar */
132
+ /**
133
+ * The action bar
134
+ *
135
+ * This event only provide the parsed string. To get the unparsed, or the raw one with decoration, use `actionBarRaw`
136
+ */
104
137
  actionBar: [message: string],
105
138
  /** The raw action bar */
106
139
  actionBarRaw: [textComponent: TextComponent],
107
140
 
108
141
  // Container
109
- /** When a container is opened */
142
+ /**
143
+ * When a container is opened
144
+ *
145
+ * This event will be fired when client click on a block (with `openBlock` method) that is a container
146
+ * or when server believe that you just open a container (with open_screen packet).
147
+ */
110
148
  openContainer: [container: Container],
111
149
  /** When the opening container is closed */
112
150
  closeContainer: [],
@@ -125,6 +163,34 @@ export interface ClientEvents {
125
163
 
126
164
  /**
127
165
  * High-level client.
166
+ *
167
+ * @example
168
+ * ```ts
169
+ * const client = new Client({
170
+ * playerName: "bot", // Put your name here
171
+ * host: "localhost", // Put the server address
172
+ * port: 25565, // 25565 is the default port, change to your server's port if needed
173
+ * version: "26.2", // The client version
174
+ * loadRegistry: false, //
175
+ * // auth: { // This things is not working yet
176
+ * // client_id: "d86254d8-edf7-4640-90eb-643c99af188e",
177
+ * // method: "loopback",
178
+ * // openBrowser: true,
179
+ * // port: 12345
180
+ * // },
181
+ * // shouldVerifyMessageOrder: true,
182
+ * // shouldVerifyMessageSignature: true
183
+ * });
184
+ *
185
+ * // MineJs is a event driven library, the same way DiscordJS work (if you know them).
186
+ * // One thing to note is most of method in this client require client to be ready,
187
+ * // meaning that unless the "ready" event is fired, you can't do, mostly, anything :D
188
+ * client.on("ready", () => console.log("Client is ready :yay:"));
189
+ *
190
+ * // The init step before is just init the data and does not create the connection yet.
191
+ * // This method will create that connection.
192
+ * client.connect();
193
+ * ```
128
194
  */
129
195
  export class Client extends (EventEmitter as new () => TypedEmitter<ClientEvents>) {
130
196
  private static loadRegistry: boolean = false;
@@ -145,6 +211,7 @@ export class Client extends (EventEmitter as new () => TypedEmitter<ClientEvents
145
211
  throw new HaveSignatureButNotIndex();
146
212
 
147
213
  super();
214
+ this.loadRegistries();
148
215
 
149
216
  // For managing shared data
150
217
  this.state = new SharedState(options);
@@ -209,14 +276,14 @@ export class Client extends (EventEmitter as new () => TypedEmitter<ClientEvents
209
276
  this.emit(event.event, ...event.args);
210
277
  }
211
278
 
212
- private async loadRegistries() {
279
+ private loadRegistries() {
213
280
  if (Client.loadRegistry) return;
214
281
  Client.loadRegistry = true;
215
282
  const { version } = this.options;
216
- await BlockRegistry.load(version);
217
- await EntityRegistry.load(version);
218
- await PacketRegistry.load(version);
219
- await EffectRegistry.load(version);
283
+ BlockRegistry.load(version);
284
+ EntityRegistry.load(version);
285
+ PacketRegistry.load(version);
286
+ EffectRegistry.load(version);
220
287
  }
221
288
 
222
289
  // Start / stop
@@ -225,7 +292,6 @@ export class Client extends (EventEmitter as new () => TypedEmitter<ClientEvents
225
292
  * Create a connection to the server
226
293
  */
227
294
  public async connect() {
228
- await this.loadRegistries();
229
295
  if (!this.state.playerUUID) {
230
296
  if (this.options.auth) {
231
297
  this.state.authClient = new AuthClient(this.options.auth);
@@ -249,7 +315,9 @@ export class Client extends (EventEmitter as new () => TypedEmitter<ClientEvents
249
315
  });
250
316
  }
251
317
  /**
252
- * Destroy the connection
318
+ * Destroy the connection.
319
+ *
320
+ * Calling this method will also fire the "disconnect" event.
253
321
  */
254
322
  public disconnect(reason: string = "Disconnect", raw?: TextComponent) {
255
323
  this.state.state = ConnectionState.Disconnected;
@@ -267,81 +335,131 @@ export class Client extends (EventEmitter as new () => TypedEmitter<ClientEvents
267
335
 
268
336
  // Inputs
269
337
  /**
270
- * Press and hold a key
338
+ * Press and hold a key.
339
+ *
340
+ * This is pretty the same idea as when you play Minecraft.
341
+ * When you need to go forward, you have to hold the key `W`, `S` for backward, etc...
342
+ *
343
+ * @throws {ClientNotReady} When the client is not ready
271
344
  */
272
- public hold(inout: Input) {
273
- this.player.input(inout);
345
+ public hold(input: Input) {
346
+ this.state.checkReady();
347
+ this.player.input(input);
274
348
  }
275
349
  /**
276
- * Release a key
350
+ * Release a key.
351
+ *
352
+ * This is also pretty the same idea as when you play Minecraft.
353
+ * Need to stop, stop holding `W`, `S` key, etc...
354
+ *
355
+ * @throws {ClientNotReady} When the client is not ready
277
356
  */
278
357
  public release(input: Input) {
358
+ this.state.checkReady();
279
359
  this.player.releaseInput(input);
280
360
  }
281
361
  /**
282
- * Release all key
362
+ * Release all key.
363
+ *
364
+ * This is the shortcut for release all keys.
365
+ * Instead of calling `release` method for each keys, just call this
366
+ *
367
+ * @throws {ClientNotReady} When the client is not ready
283
368
  */
284
369
  public stopMoving() {
370
+ this.state.checkReady();
285
371
  this.player.releaseAllInputs();
286
372
  }
287
373
 
288
374
  /**
289
375
  * Send a message
376
+ *
377
+ * @throws {ClientNotReady} When the client is not ready
290
378
  */
291
379
  public chat(message: string) {
380
+ this.state.checkReady();
292
381
  this.packetSender.sendMessage(message);
293
382
  }
294
383
 
295
384
  // World
296
385
  /**
297
386
  * Get the block state at position
387
+ *
388
+ * @throws {ClientNotReady} When the client is not ready
298
389
  */
299
- public at(x: number, y: number, z: number): BlockState | null;
300
- public at(position: BaseVec3): BlockState | null;
301
- public at(a: BaseVec3 | number, b?: number, c?: number): BlockState | null {
390
+ public getBlock(x: number, y: number, z: number): BlockState | null;
391
+ public getBlock(position: BaseVec3): BlockState | null;
392
+ public getBlock(a: BaseVec3 | number, b?: number, c?: number): BlockState | null {
302
393
  const vec3 = Vec3.loadArgs(a, b, c);
303
394
  return this.blocks.at(vec3);
304
395
  }
305
396
 
306
397
  /**
307
- * Rotate your face
398
+ * Get player position
308
399
  *
309
- * @see https://minecraft.wiki/w/Java_Edition_protocol/Packets#Synchronize_Player_Position For Yaw and Pitch definition:
400
+ * @throws {ClientNotReady} When the client is not ready
401
+ */
402
+ public getPosition() {
403
+ this.state.checkReady();
404
+ return structuredClone(this.state.player!.position);
405
+ }
406
+
407
+ /**
408
+ * Rotate your face
310
409
  *
311
- * "Yaw is measured in degrees and does not follow classical trigonometry rules.
410
+ * "Yaw is measured in degrees and does not follow classical trigonometry rules.
312
411
  * The unit circle of yaw on the XZ-plane starts at (0, 1) and turns counterclockwise, with 90 at (-1, 0), 180 at (0, -1) and 270 at (1, 0).
313
- *
412
+ *
314
413
  * Pitch is measured in degrees, where 0 is looking straight ahead, -90 is looking straight up, and 90 is looking straight down."
315
- *
414
+ *
415
+ * @see https://minecraft.wiki/w/Java_Edition_protocol/Packets#Synchronize_Player_Position For Yaw and Pitch definition
416
+ *
316
417
  * @param yaw In degree
317
418
  * @param pitch In degree
419
+ *
420
+ * @throws {ClientNotReady} When the client is not ready
318
421
  */
319
422
  public lookAt(yaw: number, pitch: number) {
423
+ this.state.checkReady();
320
424
  this.player.setAngle(yaw, pitch);
321
425
  }
322
426
 
323
427
  /**
324
428
  * Get opening container.
325
429
  *
326
- * Return `undefined` if not any is opening
430
+ * Return `undefined` if nothing is opening
431
+ *
432
+ * @throws {ClientNotReady} When the client is not ready
327
433
  */
328
- public getOpeningContainer() { return this.state.openingContainer; }
434
+ public getOpeningContainer() {
435
+ this.state.checkReady();
436
+ return this.state.openingContainer;
437
+ }
329
438
  /**
330
439
  * Get carrying item.
331
440
  *
332
- * Return `undefined` if not any is carried
441
+ * Return `undefined` if nothing is carried
442
+ *
443
+ * @throws {ClientNotReady} When the client is not ready
333
444
  */
334
- public getCarryingItem() { return this.state.player?.carryingItem; }
445
+ public getCarryingItem() {
446
+ this.state.checkReady();
447
+ return this.state.player?.carryingItem;
448
+ }
335
449
  /**
336
450
  * Get player's inventory
451
+ *
452
+ * @throws {ClientNotReady} When the client is not ready
337
453
  */
338
- public getInventory() { return this.state.player?.inventory; }
454
+ public getInventory() {
455
+ this.state.checkReady();
456
+ return this.state.player?.inventory;
457
+ }
339
458
 
340
459
  /**
341
460
  * Open a block.
342
461
  *
343
- * **NOTE** Not working for now
344
- *
462
+ * @throws {ClientNotReady} When the client is not ready
345
463
  * @returns `true` if the target block is a solid block, or else, return `false`
346
464
  */
347
465
  public openBlock(x: number, y: number, z: number): boolean;
@@ -349,22 +467,27 @@ export class Client extends (EventEmitter as new () => TypedEmitter<ClientEvents
349
467
  public openBlock(a: BaseVec3 | number, b?: number, c?: number): boolean {
350
468
  this.state.checkReady();
351
469
  const vec3 = Vec3.loadArgs(a, b, c);
470
+ const eyePos = new Vec3(this.state.player!.position).add(0, 1.62, 0);
471
+ const target = new Vec3(vec3).add(0.5, 0.5, 0.5); // aim at block center, or use look direction
472
+ const reach = 4.5; // survival reach, adjust as needed
473
+ const dir = target.subtract(eyePos).normalize();
474
+ const to = eyePos.add(dir.scale(reach));
352
475
  const clipResult = BlockGetter.clip(
353
- new Vec3(this.state.player!.position),
354
- new Vec3(vec3),
476
+ eyePos,
477
+ to,
355
478
  (pos) => this.blocks.at(pos)!.shape
356
479
  )!;
357
- console.log(clipResult);
358
480
  if (clipResult.miss) return false;
481
+ const cursorX = Math.abs(clipResult.location.x - clipResult.pos.x),
482
+ cursorY = Math.abs(clipResult.location.y - clipResult.pos.y),
483
+ cursorZ = Math.abs(clipResult.location.z - clipResult.pos.z);
484
+ if (cursorX > 1 || cursorY > 1 || cursorZ > 1)
485
+ return false;
359
486
  this.packetSender.sendUseItemOn(
360
487
  "main",
361
488
  vec3,
362
489
  clipResult.direction,
363
- {
364
- x: Math.abs(clipResult.location.x - vec3.x),
365
- y: Math.abs(clipResult.location.y - vec3.y),
366
- z: Math.abs(clipResult.location.z - vec3.z),
367
- },
490
+ { x: cursorX, y: cursorY, z: cursorZ },
368
491
  false,
369
492
  false
370
493
  );
package/src/net/tcp.ts CHANGED
@@ -78,13 +78,6 @@ export class TCPClient extends (EventEmitter as new () => TypedEmmiter<TCPClient
78
78
  public readonly socket: Socket;
79
79
 
80
80
  // Internal var
81
- /**
82
- * | Value | Meaning |
83
- * |-------|---------|
84
- * | 0 | not set yet |
85
- * | -1 | dont compress |
86
- * | > 0 | compression threshold |
87
- */
88
81
  private bufferPool: Buffer = Buffer.alloc(0);
89
82
 
90
83
  constructor(
@@ -141,7 +134,7 @@ export class TCPClient extends (EventEmitter as new () => TypedEmmiter<TCPClient
141
134
 
142
135
  private handlePacket() {
143
136
  while (this.bufferPool.length > 0) {
144
- let decoder = new BinaryDecoder(this.bufferPool);
137
+ const decoder = new BinaryDecoder(this.bufferPool);
145
138
  let packetLength: number;
146
139
  try {
147
140
  packetLength = decoder.readVarInt();
@@ -153,22 +146,23 @@ export class TCPClient extends (EventEmitter as new () => TypedEmmiter<TCPClient
153
146
  const expectedPacketEnd = decoder.offset + packetLength;
154
147
 
155
148
  let packetID: number;
149
+ let packetDecoder = new BinaryDecoder(decoder.buffer.subarray(decoder.offset, expectedPacketEnd));
156
150
  if (this.state.compressionThreshold === 0)
157
- packetID = decoder.readVarInt();
151
+ packetID = packetDecoder.readVarInt();
158
152
  else if (this.state.compressionThreshold === -1) {
159
- decoder.readVarInt();
160
- packetID = decoder.readVarInt();
153
+ packetDecoder.readVarInt();
154
+ packetID = packetDecoder.readVarInt();
161
155
  } else {
162
- const dataLength = decoder.readVarInt();
156
+ const dataLength = packetDecoder.readVarInt();
163
157
  if (dataLength === 0) {
164
- packetID = decoder.readVarInt();
158
+ packetID = packetDecoder.readVarInt();
165
159
  } else {
166
- const dataBuffer = decoder.buffer.subarray(decoder.offset, expectedPacketEnd);
160
+ const dataBuffer = packetDecoder.buffer.subarray(packetDecoder.offset);
167
161
  const decompressed = inflateSync(dataBuffer);
168
162
  if (decompressed.length !== dataLength)
169
163
  throw new UnexpectedValue("decompressed packet data", decompressed.length.toString(), dataLength.toString());
170
- decoder = new BinaryDecoder(decompressed);
171
- packetID = decoder.readVarInt();
164
+ packetDecoder = new BinaryDecoder(decompressed);
165
+ packetID = packetDecoder.readVarInt();
172
166
  }
173
167
  }
174
168
  if (
@@ -179,8 +173,8 @@ export class TCPClient extends (EventEmitter as new () => TypedEmmiter<TCPClient
179
173
  (!this.state.clientOptions.debug.ignoreTCPPacketLogs)
180
174
  )
181
175
  )
182
- console.log("packet", packetID, "length", decoder.buffer.length);
183
- this.forwardPacket(packetID, decoder);
176
+ console.log("packet", packetID, "length", packetDecoder.buffer.length);
177
+ this.forwardPacket(packetID, packetDecoder);
184
178
 
185
179
  this.bufferPool = this.bufferPool.subarray(expectedPacketEnd);
186
180
  }