@vaitosoi/minejs 0.5.0 → 0.6.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 (109) hide show
  1. package/README.md +4 -0
  2. package/assets/minecraft/26.2/components.json +113 -0
  3. package/assets/minecraft/26.2/packets.json +8 -16
  4. package/assets/minecraft/26.3/packets.json +4767 -0
  5. package/assets/minecraft/versionMapping.json +16 -0
  6. package/dist/base/bitset.d.ts.map +1 -1
  7. package/dist/base/bitset.js.map +1 -1
  8. package/dist/base/buffer.d.ts +1 -0
  9. package/dist/base/buffer.d.ts.map +1 -1
  10. package/dist/base/buffer.js +1 -0
  11. package/dist/base/buffer.js.map +1 -1
  12. package/dist/base/http.d.ts +1 -0
  13. package/dist/base/http.d.ts.map +1 -1
  14. package/dist/base/http.js +1 -0
  15. package/dist/base/http.js.map +1 -1
  16. package/dist/base/math.d.ts +2 -0
  17. package/dist/base/math.d.ts.map +1 -1
  18. package/dist/base/math.js +2 -0
  19. package/dist/base/math.js.map +1 -1
  20. package/dist/base/typing.d.ts +5 -0
  21. package/dist/base/typing.d.ts.map +1 -1
  22. package/dist/binary/decoder.d.ts +39 -0
  23. package/dist/binary/decoder.d.ts.map +1 -1
  24. package/dist/binary/decoder.js +669 -2
  25. package/dist/binary/decoder.js.map +1 -1
  26. package/dist/binary/encoder.d.ts +7 -3
  27. package/dist/binary/encoder.d.ts.map +1 -1
  28. package/dist/binary/encoder.js +4 -0
  29. package/dist/binary/encoder.js.map +1 -1
  30. package/dist/client/client.d.ts +58 -1
  31. package/dist/client/client.d.ts.map +1 -1
  32. package/dist/client/client.js +38 -0
  33. package/dist/client/client.js.map +1 -1
  34. package/dist/message/link.d.ts +3 -0
  35. package/dist/message/link.d.ts.map +1 -1
  36. package/dist/message/link.js +3 -0
  37. package/dist/message/link.js.map +1 -1
  38. package/dist/message/sigCache.d.ts +5 -0
  39. package/dist/message/sigCache.d.ts.map +1 -1
  40. package/dist/message/sigCache.js +5 -0
  41. package/dist/message/sigCache.js.map +1 -1
  42. package/dist/net/tcp.d.ts +7 -3
  43. package/dist/net/tcp.d.ts.map +1 -1
  44. package/dist/net/tcp.js +6 -3
  45. package/dist/net/tcp.js.map +1 -1
  46. package/dist/packet/listener.d.ts +8 -0
  47. package/dist/packet/listener.d.ts.map +1 -1
  48. package/dist/packet/listener.js +81 -5
  49. package/dist/packet/listener.js.map +1 -1
  50. package/dist/packet/sender.d.ts +6 -1
  51. package/dist/packet/sender.d.ts.map +1 -1
  52. package/dist/packet/sender.js +18 -0
  53. package/dist/packet/sender.js.map +1 -1
  54. package/dist/physics/aabb.d.ts +3 -0
  55. package/dist/physics/aabb.d.ts.map +1 -1
  56. package/dist/physics/aabb.js +10 -7
  57. package/dist/physics/aabb.js.map +1 -1
  58. package/dist/physics/direction.d.ts +3 -0
  59. package/dist/physics/direction.d.ts.map +1 -1
  60. package/dist/physics/direction.js +3 -0
  61. package/dist/physics/direction.js.map +1 -1
  62. package/dist/physics/player.d.ts +3 -0
  63. package/dist/physics/player.d.ts.map +1 -1
  64. package/dist/physics/player.js +4 -5
  65. package/dist/physics/player.js.map +1 -1
  66. package/dist/version/codec.d.ts +4 -0
  67. package/dist/version/codec.d.ts.map +1 -1
  68. package/dist/version/codec.js +29 -14
  69. package/dist/version/codec.js.map +1 -1
  70. package/dist/version/registry.d.ts +30 -6
  71. package/dist/version/registry.d.ts.map +1 -1
  72. package/dist/version/registry.js +64 -19
  73. package/dist/version/registry.js.map +1 -1
  74. package/dist/world/block.d.ts +10 -8
  75. package/dist/world/block.d.ts.map +1 -1
  76. package/dist/world/block.js +17 -11
  77. package/dist/world/block.js.map +1 -1
  78. package/dist/world/container.d.ts +106 -0
  79. package/dist/world/container.d.ts.map +1 -0
  80. package/dist/world/container.js +240 -0
  81. package/dist/world/container.js.map +1 -0
  82. package/dist/world/state.d.ts +11 -5
  83. package/dist/world/state.d.ts.map +1 -1
  84. package/dist/world/state.js +5 -0
  85. package/dist/world/state.js.map +1 -1
  86. package/package.json +2 -2
  87. package/src/base/bitset.ts +0 -1
  88. package/src/base/buffer.ts +1 -0
  89. package/src/base/http.ts +1 -0
  90. package/src/base/math.ts +2 -0
  91. package/src/base/typing.ts +6 -0
  92. package/src/binary/decoder.ts +695 -2
  93. package/src/binary/encoder.ts +7 -3
  94. package/src/client/client.ts +81 -4
  95. package/src/message/link.ts +3 -0
  96. package/src/message/sigCache.ts +5 -0
  97. package/src/net/tcp.ts +17 -10
  98. package/src/packet/listener.ts +106 -6
  99. package/src/packet/sender.ts +29 -2
  100. package/src/physics/aabb.ts +10 -7
  101. package/src/physics/direction.ts +3 -0
  102. package/src/physics/player.ts +8 -5
  103. package/src/version/codec.ts +29 -14
  104. package/src/version/registry.ts +71 -18
  105. package/src/world/block.ts +19 -17
  106. package/src/world/container.ts +180 -0
  107. package/src/world/state.ts +14 -6
  108. package/test/client/index.ts +49 -21
  109. package/test/client/registry.json +1735 -0
@@ -1,8 +1,9 @@
1
- import { Vec3 } from "../physics/direction";
1
+ import { BaseVec3 } from "../physics/direction";
2
2
  import { CONTINUATION_FLAG, CONTINUE_BIT, isFixedSizeTag, isTag, MAX_QUANTIZED_VALUE, SCALE_BITS, SEGMENT_BITS } from "./static";
3
3
  import { InvalidValue, NumberTooBig, UnexpectedValue } from "../base/error";
4
4
  import { FixedSizeTag, Tag } from "./static";
5
5
 
6
+ /** @hidden */
6
7
  export function makeMovementFlag(onGround: boolean, pushingWall: boolean) {
7
8
  let flag = 0;
8
9
  if (onGround)
@@ -12,6 +13,9 @@ export function makeMovementFlag(onGround: boolean, pushingWall: boolean) {
12
13
  return flag;
13
14
  }
14
15
 
16
+ /**
17
+ * Binary encoding helper
18
+ */
15
19
  export class BinaryEncoder {
16
20
  private buffer = Buffer.alloc(0);
17
21
 
@@ -159,7 +163,7 @@ export class BinaryEncoder {
159
163
  *
160
164
  * @returns a Position
161
165
  */
162
- public writePosition(position: Vec3) {
166
+ public writePosition(position: BaseVec3) {
163
167
  const val = BigInt(
164
168
  ((position.x & 0x3FFFFFF) << 38) |
165
169
  ((position.z & 0x3FFFFFF) << 12) |
@@ -215,7 +219,7 @@ export class BinaryEncoder {
215
219
  *
216
220
  * Read this article for more information: https://minecraft.wiki/w/Java_Edition_protocol/Data_types#LpVec3
217
221
  */
218
- public writeLpVec3(vec3: Vec3) {
222
+ public writeLpVec3(vec3: BaseVec3) {
219
223
  const maxCoordinate = Math.max(Math.abs(vec3.x), Math.max(Math.abs(vec3.y), Math.abs(vec3.z)));
220
224
 
221
225
  // Checking for NaN values in our maxCoordinate
@@ -1,5 +1,5 @@
1
1
  import { EventEmitter } from "node:events";
2
- import { BlockManager, BlockState } from "../world/block";
2
+ import { BlockGetter, BlockManager, BlockState } from "../world/block";
3
3
  import { EntitiesManager } from "../world/entity";
4
4
  import { Input, Player } from "../physics/player";
5
5
  import { TickLoop } from "../world/tick";
@@ -16,6 +16,7 @@ import { computeUUID } from "../base/math";
16
16
  import { HaveSignatureButNotIndex } from "../base/error";
17
17
  import { Sender } from "../packet/sender";
18
18
  import { CacheImplementation } from "../base/cache";
19
+ import { Container, Slot } from "../world/container";
19
20
 
20
21
  export type ClientOption = Omit<TCPClientOption, "protocolVersion"> & {
21
22
  version: string,
@@ -103,12 +104,29 @@ export interface ClientEvents {
103
104
  actionBar: [message: string],
104
105
  /** The raw action bar */
105
106
  actionBarRaw: [textComponent: TextComponent],
107
+
108
+ // Container
109
+ /** When a container is opened */
110
+ openContainer: [container: Container],
111
+ /** When the opening container is closed */
112
+ closeContainer: [],
113
+ /** When the packet contain the content of the container is received */
114
+ containerContent: [container: Container, carryingItem: Slot],
115
+ /** When the packet contain the property of the container is received */
116
+ containerProperty: [container: Container],
117
+
118
+ /**
119
+ * When tab list is updated
120
+ *
121
+ * `{"text":""}` mean remove header or footer.
122
+ */
123
+ tablist: [newTablist: { header: { text: string, raw: TextComponent }, footer: { text: string, raw: TextComponent } }]
106
124
  }
107
125
 
108
126
  /**
109
127
  * High-level client.
110
128
  */
111
- export class Client<IsReady extends boolean = boolean> extends (EventEmitter as new () => TypedEmitter<ClientEvents>) {
129
+ export class Client extends (EventEmitter as new () => TypedEmitter<ClientEvents>) {
112
130
  private static loadRegistry: boolean = false;
113
131
 
114
132
  private tickLoop: TickLoop;
@@ -118,7 +136,7 @@ export class Client<IsReady extends boolean = boolean> extends (EventEmitter as
118
136
  private player: Player;
119
137
 
120
138
  private tcp: TCPClient;
121
- private state: SharedState<IsReady>;
139
+ private state: SharedState;
122
140
  private packetListener: Listener;
123
141
  private packetSender: Sender;
124
142
 
@@ -182,7 +200,7 @@ export class Client<IsReady extends boolean = boolean> extends (EventEmitter as
182
200
  this.tcp.on("destroy", () => this.emit("destroy"));
183
201
  this.on("loadChunk", (sx, sy, sz) => this.blocks.deleteChunkCache(sx, sy, sz));
184
202
  this.on("unloadChunk", (sx, sz) => {
185
- for (let sy = 0; sy < 16; sy++)
203
+ for (let sy = 0; sy < 16; sy++)
186
204
  this.blocks.deleteChunkCache(sx, sy, sz);
187
205
  });
188
206
  }
@@ -287,10 +305,69 @@ export class Client<IsReady extends boolean = boolean> extends (EventEmitter as
287
305
 
288
306
  /**
289
307
  * Rotate your face
308
+ *
309
+ * @see https://minecraft.wiki/w/Java_Edition_protocol/Packets#Synchronize_Player_Position For Yaw and Pitch definition:
310
+ *
311
+ * "Yaw is measured in degrees and does not follow classical trigonometry rules.
312
+ * 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
+ *
314
+ * Pitch is measured in degrees, where 0 is looking straight ahead, -90 is looking straight up, and 90 is looking straight down."
315
+ *
290
316
  * @param yaw In degree
291
317
  * @param pitch In degree
292
318
  */
293
319
  public lookAt(yaw: number, pitch: number) {
294
320
  this.player.setAngle(yaw, pitch);
295
321
  }
322
+
323
+ /**
324
+ * Get opening container.
325
+ *
326
+ * Return `undefined` if not any is opening
327
+ */
328
+ public getOpeningContainer() { return this.state.openingContainer; }
329
+ /**
330
+ * Get carrying item.
331
+ *
332
+ * Return `undefined` if not any is carried
333
+ */
334
+ public getCarryingItem() { return this.state.player?.carryingItem; }
335
+ /**
336
+ * Get player's inventory
337
+ */
338
+ public getInventory() { return this.state.player?.inventory; }
339
+
340
+ /**
341
+ * Open a block.
342
+ *
343
+ * **NOTE** Not working for now
344
+ *
345
+ * @returns `true` if the target block is a solid block, or else, return `false`
346
+ */
347
+ public openBlock(x: number, y: number, z: number): boolean;
348
+ public openBlock(position: BaseVec3): boolean;
349
+ public openBlock(a: BaseVec3 | number, b?: number, c?: number): boolean {
350
+ this.state.checkReady();
351
+ const vec3 = Vec3.loadArgs(a, b, c);
352
+ const clipResult = BlockGetter.clip(
353
+ new Vec3(this.state.player!.position),
354
+ new Vec3(vec3),
355
+ (pos) => this.blocks.at(pos)!.shape
356
+ )!;
357
+ console.log(clipResult);
358
+ if (clipResult.miss) return false;
359
+ this.packetSender.sendUseItemOn(
360
+ "main",
361
+ vec3,
362
+ 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
+ },
368
+ false,
369
+ false
370
+ );
371
+ return true;
372
+ }
296
373
  }
@@ -1,3 +1,6 @@
1
+ /**
2
+ * Represent a node in the linked-list of messages
3
+ */
1
4
  export class MessageLink {
2
5
  constructor (
3
6
  public index: number,
@@ -1,3 +1,8 @@
1
+ /**
2
+ * Used for caching the message signature
3
+ *
4
+ * Not used for now
5
+ */
1
6
  export class SignatureCache {
2
7
  private entries: Buffer[];
3
8
 
package/src/net/tcp.ts CHANGED
@@ -11,10 +11,6 @@ import { EncodeResult } from "../version/codec";
11
11
  import { TextComponent } from "../base/typing";
12
12
  import { NotImplemented, SockerIsNotWritable, UnexpectedValue } from "../base/error";
13
13
 
14
- // Minecraft related typing
15
-
16
-
17
-
18
14
  // TCP related typing
19
15
  export interface TCPClientOption {
20
16
  /** Server host */
@@ -38,7 +34,11 @@ export interface TCPClientOption {
38
34
 
39
35
  debug?: {
40
36
  /** Log the incoming packet */
41
- packetLogger: boolean
37
+ packetLogger: boolean,
38
+ /** Packet to ignore */
39
+ ignorePackets?: string[],
40
+ /** Ignore logs from TCP client (`packet X length Y`) */
41
+ ignoreTCPPacketLogs?: number[] | true
42
42
  },
43
43
 
44
44
  // For premium account
@@ -56,8 +56,8 @@ export interface Message {
56
56
  target: string | undefined,
57
57
  content: string,
58
58
  raw: {
59
- sender: TextComponent,
60
- target: TextComponent | undefined,
59
+ sender: string | TextComponent,
60
+ target: string | TextComponent | undefined,
61
61
  content: TextComponent | undefined
62
62
  }
63
63
  }
@@ -72,7 +72,7 @@ export interface Message {
72
72
  /**
73
73
  * Low-level client. You SHOULD NOT use this client unless you know what are you doing
74
74
  *
75
- * @see https://minecraft.wiki/w/Java_Edition_protocol/Packets?oldid=3445844
75
+ * @see https://minecraft.wiki/w/Java_Edition_protocol/Packets
76
76
  */
77
77
  export class TCPClient extends (EventEmitter as new () => TypedEmmiter<TCPClientEvents>) {
78
78
  public readonly socket: Socket;
@@ -163,7 +163,7 @@ export class TCPClient extends (EventEmitter as new () => TypedEmmiter<TCPClient
163
163
  if (dataLength === 0) {
164
164
  packetID = decoder.readVarInt();
165
165
  } else {
166
- const dataBuffer = this.bufferPool.subarray(decoder.offset, expectedPacketEnd);
166
+ const dataBuffer = decoder.buffer.subarray(decoder.offset, expectedPacketEnd);
167
167
  const decompressed = inflateSync(dataBuffer);
168
168
  if (decompressed.length !== dataLength)
169
169
  throw new UnexpectedValue("decompressed packet data", decompressed.length.toString(), dataLength.toString());
@@ -171,7 +171,14 @@ export class TCPClient extends (EventEmitter as new () => TypedEmmiter<TCPClient
171
171
  packetID = decoder.readVarInt();
172
172
  }
173
173
  }
174
- if (this.state.clientOptions.debug?.packetLogger == true)
174
+ if (
175
+ this.state.clientOptions.debug &&
176
+ this.state.clientOptions.debug.packetLogger &&
177
+ (
178
+ (Array.isArray(this.state.clientOptions.debug.ignoreTCPPacketLogs) && !this.state.clientOptions.debug.ignoreTCPPacketLogs.includes(packetID)) ||
179
+ (!this.state.clientOptions.debug.ignoreTCPPacketLogs)
180
+ )
181
+ )
175
182
  console.log("packet", packetID, "length", decoder.buffer.length);
176
183
  this.forwardPacket(packetID, decoder);
177
184
 
@@ -12,11 +12,15 @@ import { AuthRelatedNotFound, HaveSignatureButNotIndex, MessageLinkNotFound, Not
12
12
  import { uuidToBuffer } from "../base/math";
13
13
  import { MessageLink } from "../message/link";
14
14
  import { Sender } from "./sender";
15
+ import { Container, Slot } from "../world/container";
15
16
 
16
17
  function zodParse<Type extends zod.ZodType>(data: object, zod: Type): zod.infer<Type> {
17
18
  return zod.parse(data);
18
19
  }
19
20
 
21
+ /**
22
+ * Packet listener
23
+ */
20
24
  export class Listener {
21
25
  private readonly mapping: Record<string, (data: object) => void>;
22
26
 
@@ -59,6 +63,13 @@ export class Listener {
59
63
  "play:remove_mob_effect": this.handleRemoveMobEffect,
60
64
  // "play:rotate_head": ,
61
65
  "play:set_entity_motion": this.handleSetEntityVelocity,
66
+
67
+ "play:open_screen": this.handleOpenScreen,
68
+ "play:container_close": this.handleCloseContainer,
69
+ "play:container_set_content": this.handleContainerContent,
70
+ "play:container_set_data": this.handleContainerProperty,
71
+
72
+ "play:tab_list": this.handleSetTablist
62
73
  };
63
74
  }
64
75
 
@@ -73,7 +84,11 @@ export class Listener {
73
84
  // Login
74
85
 
75
86
  private handleLoginDisconnect(data: object) {
76
- const { reason } = zodParse(data, zod.object({ reason: zod.record(zod.string(), zod.any()) }));
87
+ const { reason } = zodParse(data, zod.object({ reason: zod.record(zod.string(), zod.any()).or(zod.string()) }));
88
+
89
+ if (typeof reason === "string")
90
+ return this.disconnect(reason);
91
+
77
92
  const text = getTextFromTextComponent(reason);
78
93
 
79
94
  this.disconnect(text, reason);
@@ -143,7 +158,9 @@ export class Listener {
143
158
  yaw: 0,
144
159
  pitch: 0,
145
160
  },
146
- effects: {}
161
+ effects: {},
162
+ inventory: new Container(0, -1, {}),
163
+ carryingItem: undefined
147
164
  } satisfies ClientPlayer as any;
148
165
  this.sender.sendLoginAck();
149
166
  }
@@ -690,12 +707,16 @@ export class Listener {
690
707
  filter_type: zod.int(),
691
708
  filter_type_bits: zod.array(zod.bigint()).nullable(),
692
709
  chat_type: zod.int().or(zod.record(zod.string(), zod.any())),
693
- sender_name: zod.record(zod.string(), zod.any()),
694
- target_name: zod.record(zod.string(), zod.any()).nullable()
710
+ sender_name: zod.record(zod.string(), zod.any()).or(zod.string()),
711
+ target_name: zod.record(zod.string(), zod.any()).or(zod.string()).nullable()
695
712
  }));
696
713
 
697
- const senderNameText = getTextFromTextComponent(sender_name).toString(),
698
- targetNameText = target_name ? getTextFromTextComponent(target_name).toString() : undefined;
714
+ const senderNameText = typeof sender_name === "string" ? sender_name : getTextFromTextComponent(sender_name).toString(),
715
+ targetNameText = target_name
716
+ ? typeof target_name === "string"
717
+ ? target_name
718
+ : getTextFromTextComponent(target_name).toString()
719
+ : undefined;
699
720
 
700
721
  const emitObject = {
701
722
  sender: senderNameText,
@@ -831,4 +852,83 @@ export class Listener {
831
852
  }
832
853
  });
833
854
  }
855
+
856
+ // Container
857
+ private handleOpenScreen(data: object) {
858
+ const {
859
+ window_id,
860
+ window_type,
861
+ window_title
862
+ } = zodParse(data, zod.object({
863
+ window_id: zod.int(),
864
+ window_type: zod.int(),
865
+ window_title: zod.record(zod.string(), zod.any())
866
+ }));
867
+ this.state.enqueueMutation(state => {
868
+ state.openingContainer = new Container(window_id, window_type, window_title);
869
+ this.emit("openContainer", this.state.openingContainer!);
870
+ });
871
+ }
872
+
873
+ private handleCloseContainer(data: object) {
874
+ // Not used by vanilla client
875
+ const { window_id } = zodParse(data, zod.object({ window_id: zod.int() }));
876
+
877
+ this.state.enqueueMutation(state => {
878
+ state.openingContainer = undefined;
879
+ this.emit("closeContainer");
880
+ });
881
+ }
882
+
883
+ private handleContainerContent(data: object) {
884
+ const {
885
+ window_id,
886
+ state_id,
887
+ slot_data,
888
+ carried_item
889
+ } = zodParse(data, zod.object({
890
+ window_id: zod.int(),
891
+ state_id: zod.int(),
892
+ slot_data: zod.array(zod.any() as zod.ZodType<Slot>),
893
+ carried_item: zod.any() as zod.ZodType<Slot>,
894
+ }));
895
+ this.state.enqueueMutation(state => {
896
+ if (!state.openingContainer) return;
897
+ state.openingContainer.stateId = state_id;
898
+ state.openingContainer.slots = slot_data;
899
+ state.player!.carryingItem = carried_item;
900
+ this.emit("containerContent", state.openingContainer, state.player!.carryingItem);
901
+ });
902
+ }
903
+
904
+ private handleContainerProperty(data: object) {
905
+ const {
906
+ window_id,
907
+ property,
908
+ value
909
+ } = zodParse(data, zod.object({
910
+ window_id: zod.number(),
911
+ property: zod.number(),
912
+ value: zod.number(),
913
+ }));
914
+ this.state.enqueueMutation(state => {
915
+ if (!state.openingContainer) return;
916
+ state.openingContainer.setProperty(property, value);
917
+ this.emit("containerProperty", state.openingContainer);
918
+ });
919
+ }
920
+
921
+ private handleSetTablist(data: object) {
922
+ const { header, footer } = zodParse(data, zod.object({
923
+ header: zod.record(zod.string(), zod.any()),
924
+ footer: zod.record(zod.string(), zod.any()),
925
+ }));
926
+ this.emit(
927
+ "tablist",
928
+ {
929
+ header: { text: getTextFromTextComponent(header), raw: header },
930
+ footer: { text: getTextFromTextComponent(footer), raw: footer },
931
+ }
932
+ );
933
+ }
834
934
  }
@@ -1,15 +1,19 @@
1
- import { createPublicKey, publicEncrypt, constants as crypto_constants, randomBytes } from "node:crypto";
1
+ import { createPublicKey, publicEncrypt, constants as crypto_constants, randomBytes, randomInt } from "node:crypto";
2
2
  import { TypedEmmiter } from "../base/event";
3
3
  import { ClientEvents } from "../client/client";
4
4
  import { ProtocolVersionMapping } from "../version/registry";
5
5
  import { ClientStatus, ConnectionState, ServerKnownPack, SharedState } from "../world/state";
6
6
  import { sliceBuffer } from "../base/buffer";
7
7
  import { randomUUIDBytes } from "../base/math";
8
- import { Angle, BaseVec3 } from "../physics/direction";
8
+ import { Angle, BaseVec3, Direction } from "../physics/direction";
9
9
  import { MessageTooLong, NotImplemented } from "../base/error";
10
10
  import BitSet from "../base/bitset";
11
11
  import { makeMovementFlag } from "../binary/encoder";
12
+ import { BlockFace } from "../world/block";
12
13
 
14
+ /**
15
+ * Packet sender
16
+ */
13
17
  export class Sender {
14
18
  constructor(
15
19
  private state: SharedState,
@@ -173,4 +177,27 @@ export class Sender {
173
177
  });
174
178
  this.state.messageCount = 0;
175
179
  }
180
+
181
+ public sendUseItemOn(
182
+ hand: "main" | "off",
183
+ position: BaseVec3,
184
+ face: BlockFace | Direction,
185
+ cursorPositionOnBlock: BaseVec3,
186
+ isInsideBlock: boolean,
187
+ worldBorderHit: boolean,
188
+ sequence: number = randomInt(1024),
189
+ ) {
190
+ const face_ = (face instanceof Direction ? face.index3D : face) as number;
191
+ this.sendPacket("use_item_on", {
192
+ hand: hand === "off" ? 1 : 0,
193
+ location: position,
194
+ face: face_,
195
+ cursor_position_x: cursorPositionOnBlock.x,
196
+ cursor_position_y: cursorPositionOnBlock.y,
197
+ cursor_position_z: cursorPositionOnBlock.z,
198
+ inside_block: isInsideBlock,
199
+ world_border_hit: worldBorderHit,
200
+ sequence
201
+ });
202
+ }
176
203
  }
@@ -14,6 +14,9 @@ export interface BaseAABB {
14
14
  maxZ: number;
15
15
  }
16
16
 
17
+ /**
18
+ * Represent an axis-aligned bounding box
19
+ */
17
20
  export class AABB implements BaseAABB {
18
21
  public minX: number;
19
22
  public minY: number;
@@ -206,7 +209,7 @@ export class AABB implements BaseAABB {
206
209
  * Cliping math. Find smallest distance to reach a face of this AABB.
207
210
  */
208
211
  public static getDirection(aabb: BaseAABB, from: BaseVec3, delta: BaseVec3, direction_: Direction | null, scaleRef: [number]) {
209
- let direction = structuredClone(direction_);
212
+ let direction = direction_;
210
213
  const { minX, maxX, minY, maxY, minZ, maxZ } = aabb;
211
214
  if (delta.x > Epsilon)
212
215
  direction = this.clipPoint(
@@ -214,7 +217,7 @@ export class AABB implements BaseAABB {
214
217
  scaleRef,
215
218
  { a: delta.x, b: delta.y, c: delta.z },
216
219
  minX,
217
- minY, maxX,
220
+ minY, maxY,
218
221
  minZ, maxZ,
219
222
  from.x, from.y, from.z
220
223
  );
@@ -224,7 +227,7 @@ export class AABB implements BaseAABB {
224
227
  scaleRef,
225
228
  { a: delta.x, b: delta.y, c: delta.z },
226
229
  maxX,
227
- minY, maxX,
230
+ minY, maxY,
228
231
  minZ, maxZ,
229
232
  from.x, from.y, from.z
230
233
  );
@@ -289,7 +292,7 @@ export class AABB implements BaseAABB {
289
292
  /**
290
293
  * `s` come from this equation:
291
294
  * ```
292
- * fromA + s * deltaA = minA (or point here)
295
+ * fromA + s * deltaA = minA (or `point` here)
293
296
  * ```
294
297
  * Which find "how far the ray travels to reach a face of this bounding box".
295
298
  *
@@ -302,7 +305,7 @@ export class AABB implements BaseAABB {
302
305
  pb = fromB + s * delta.b,
303
306
  pc = fromC + s * delta.c;
304
307
  if (
305
- 0 < s && s < scaleRef[0] &&
308
+ 0 < s && s < scaleRef[0] + Epsilon &&
306
309
  minB - Epsilon < pb && pb < maxB + Epsilon &&
307
310
  minC - Epsilon < pc && pc < maxC + Epsilon
308
311
  ) {
@@ -596,9 +599,9 @@ export class VoxelShape {
596
599
  minZ: z,
597
600
  maxZ: z + 1
598
601
  });
599
- else if (lastStartZ !== -1)
602
+ else if (lastStartZ === -1)
600
603
  lastStartZ = z;
601
- } else if (lastStartZ === -1) {
604
+ } else if (lastStartZ !== -1) {
602
605
  let endX = x,
603
606
  endY = y;
604
607
  shape.clearZStrip(lastStartZ, z, endX, endY);
@@ -4,6 +4,9 @@ export interface BaseVec3 {
4
4
  z: number
5
5
  }
6
6
 
7
+ /**
8
+ * Represent a vector which has 3 coords
9
+ */
7
10
  export class Vec3 implements BaseVec3 {
8
11
  public static readonly Zero = new Vec3(0, 0, 0);
9
12
  public static readonly XAxis = new Vec3(1, 0, 0);
@@ -41,6 +41,9 @@ export enum MoverType {
41
41
  SHULKER
42
42
  }
43
43
 
44
+ /**
45
+ * Represent a player for calculating physics
46
+ */
44
47
  export class Player {
45
48
  private fallDistance: number = 0;
46
49
  private deltaMovement: Vec3 = Vec3.Zero;
@@ -470,11 +473,11 @@ export class Player {
470
473
  if (this.fallDistance != 0 && movement.length() >= 1) {
471
474
  const checkDistance = Math.min(movement.length(), 8);
472
475
  const checkTo = this.getPos().add(movement.normalize().scale(checkDistance));
473
- const hitResult = BlockGetter.clip(this.getPos(), checkTo, {
474
- from: this.getPos(),
475
- to: checkTo,
476
- getBlockState: (position) => this.blocks.at(position)!.shape
477
- });
476
+ const hitResult = BlockGetter.clip(
477
+ this.getPos(),
478
+ checkTo,
479
+ (position) => this.blocks.at(position)!.shape
480
+ );
478
481
  if (hitResult?.miss !== false)
479
482
  this.fallDistance = 0;
480
483
  }
@@ -6,6 +6,7 @@ import { FieldNode, PacketObject, PacketRegistry, VersionDefinitions } from "./r
6
6
  import { BinaryEncoder } from "../binary/encoder";
7
7
  import { BitSet } from "../base/bitset";
8
8
 
9
+ /** @hidden */
9
10
  const TCPStateMapping: Record<ConnectionState, keyof z.infer<typeof VersionDefinitions> | null> = {
10
11
  [ConnectionState.Disconnected]: null,
11
12
  [ConnectionState.Handshake]: "handshaking",
@@ -14,11 +15,15 @@ const TCPStateMapping: Record<ConnectionState, keyof z.infer<typeof VersionDefin
14
15
  [ConnectionState.Play]: "play",
15
16
  };
16
17
 
18
+ /** @hidden */
17
19
  export interface EncodeResult {
18
20
  buffer: Buffer,
19
21
  packet: z.infer<typeof PacketObject>,
20
22
  }
21
23
 
24
+ /**
25
+ * A codec that help encode or decode packets
26
+ */
22
27
  export class VersionCodec {
23
28
  constructor(private readonly state: SharedState) { }
24
29
 
@@ -44,11 +49,14 @@ export class VersionCodec {
44
49
  if (err instanceof NotImplemented) return;
45
50
  throw err;
46
51
  }
47
- if (this.state.clientOptions.debug?.packetLogger)
52
+ if (this.state.clientOptions.debug &&
53
+ this.state.clientOptions.debug.packetLogger &&
54
+ (this.state.clientOptions.debug.ignorePackets &&
55
+ !this.state.clientOptions.debug.ignorePackets.includes(packet.name)))
48
56
  console.dir({
49
57
  dir: "to client",
50
58
  state: mappedState,
51
- name,
59
+ name: packet.name,
52
60
  data: resolvedObject,
53
61
  });
54
62
 
@@ -61,11 +69,15 @@ export class VersionCodec {
61
69
  const packet = PacketRegistry.getPacket(mappedState, "serverbound", packetId);
62
70
  if (!packet) throw new UnexpectedValue("valid serverbound packet id", packetId.toString());
63
71
 
64
- if (this.state.clientOptions.debug?.packetLogger)
72
+ if (this.state.clientOptions.debug &&
73
+ this.state.clientOptions.debug.packetLogger &&
74
+ (this.state.clientOptions.debug.ignorePackets &&
75
+ !this.state.clientOptions.debug.ignorePackets.includes(packet.name)))
65
76
  console.dir({
66
77
  dir: "to server",
67
78
  state: mappedState,
68
- id: packetId,
79
+ id: packet.id,
80
+ name: packet.name,
69
81
  data,
70
82
  });
71
83
 
@@ -139,7 +151,17 @@ export class VersionCodec {
139
151
  case "chat_type_decoration": return decoder.readChatTypeDecoration();
140
152
  case "chat_type": return decoder.readChatType();
141
153
  case "enum": return this.readField(packetId, readObject, fieldName, field.subType, decoder);
142
- case "json_text": return JSON.parse(decoder.readString());
154
+ case "json_text": {
155
+ const str = decoder.readString();
156
+ try {
157
+ return JSON.parse(str);
158
+ } catch (err) {
159
+ if (err instanceof SyntaxError)
160
+ return str;
161
+ else
162
+ throw err;
163
+ }
164
+ }
143
165
  case "object": {
144
166
  const obj: Record<string, any> = {};
145
167
  for (const [key, type] of Object.entries(field.fields))
@@ -147,15 +169,7 @@ export class VersionCodec {
147
169
  return obj;
148
170
  }
149
171
  case "game_profile": {
150
- return {
151
- uuid: decoder.readUUID(),
152
- username: decoder.readString(),
153
- pproperties: decoder.readPrefixedArray((decoder) => ({
154
- name: decoder.readString(),
155
- value: decoder.readString(),
156
- signature: decoder.readPrefixedOptional((decoder) => decoder.readString())
157
- }))
158
- };
172
+ return decoder.readProfile();
159
173
  }
160
174
  case "heightmap": {
161
175
  return {
@@ -165,6 +179,7 @@ export class VersionCodec {
165
179
  }
166
180
  case "bitset": return decoder.readPrefixedArray((decoder) => decoder.readLong());
167
181
  case "teleport_flags": return decoder.readTeleportFlag();
182
+ case "slot": return decoder.readSlot();
168
183
  case "null": return null;
169
184
  case "not_implemented": {
170
185
  if (field.skip_able === true)