@vaitosoi/minejs 0.4.2 → 0.5.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 (106) hide show
  1. package/README.md +53 -5
  2. package/assets/doc/rejected.png +0 -0
  3. package/assets/minecraft/26.2/effects.json +42 -0
  4. package/assets/minecraft/26.2/packets.json +3 -2
  5. package/dist/base/bitset.d.ts +43 -4
  6. package/dist/base/bitset.d.ts.map +1 -1
  7. package/dist/base/bitset.js +251 -30
  8. package/dist/base/bitset.js.map +1 -1
  9. package/dist/base/cache.d.ts +24 -0
  10. package/dist/base/cache.d.ts.map +1 -0
  11. package/dist/base/cache.js +58 -0
  12. package/dist/base/cache.js.map +1 -0
  13. package/dist/base/error.d.ts +13 -0
  14. package/dist/base/error.d.ts.map +1 -1
  15. package/dist/base/error.js +19 -1
  16. package/dist/base/error.js.map +1 -1
  17. package/dist/base/math.d.ts +12 -0
  18. package/dist/base/math.d.ts.map +1 -1
  19. package/dist/base/math.js +39 -0
  20. package/dist/base/math.js.map +1 -1
  21. package/dist/base/typing.d.ts +7 -0
  22. package/dist/base/typing.d.ts.map +1 -1
  23. package/dist/{packet → binary}/decoder.d.ts.map +1 -1
  24. package/dist/{packet → binary}/decoder.js.map +1 -1
  25. package/dist/{packet → binary}/encoder.d.ts +2 -2
  26. package/dist/binary/encoder.d.ts.map +1 -0
  27. package/dist/{packet → binary}/encoder.js +6 -2
  28. package/dist/binary/encoder.js.map +1 -0
  29. package/dist/{packet → binary}/static.d.ts.map +1 -1
  30. package/dist/{packet → binary}/static.js.map +1 -1
  31. package/dist/client/client.d.ts +23 -2
  32. package/dist/client/client.d.ts.map +1 -1
  33. package/dist/client/client.js +34 -19
  34. package/dist/client/client.js.map +1 -1
  35. package/dist/index.d.ts +12 -4
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +12 -4
  38. package/dist/index.js.map +1 -1
  39. package/dist/{client → net}/tcp.d.ts +2 -2
  40. package/dist/net/tcp.d.ts.map +1 -0
  41. package/dist/{client → net}/tcp.js +4 -2
  42. package/dist/net/tcp.js.map +1 -0
  43. package/dist/packet/{dispatcher.d.ts → listener.d.ts} +7 -18
  44. package/dist/packet/listener.d.ts.map +1 -0
  45. package/dist/packet/{dispatcher.js → listener.js} +73 -139
  46. package/dist/packet/listener.js.map +1 -0
  47. package/dist/packet/sender.d.ts +25 -0
  48. package/dist/packet/sender.d.ts.map +1 -0
  49. package/dist/packet/sender.js +144 -0
  50. package/dist/packet/sender.js.map +1 -0
  51. package/dist/physics/aabb.d.ts.map +1 -1
  52. package/dist/physics/aabb.js +9 -8
  53. package/dist/physics/aabb.js.map +1 -1
  54. package/dist/physics/player.d.ts +1 -0
  55. package/dist/physics/player.d.ts.map +1 -1
  56. package/dist/physics/player.js +22 -4
  57. package/dist/physics/player.js.map +1 -1
  58. package/dist/version/codec.d.ts +1 -1
  59. package/dist/version/codec.d.ts.map +1 -1
  60. package/dist/version/codec.js +28 -6
  61. package/dist/version/codec.js.map +1 -1
  62. package/dist/version/registry.d.ts +19 -2
  63. package/dist/version/registry.d.ts.map +1 -1
  64. package/dist/version/registry.js +35 -3
  65. package/dist/version/registry.js.map +1 -1
  66. package/dist/world/block.d.ts +5 -0
  67. package/dist/world/block.d.ts.map +1 -1
  68. package/dist/world/block.js +56 -0
  69. package/dist/world/block.js.map +1 -1
  70. package/dist/world/state.d.ts +22 -3
  71. package/dist/world/state.d.ts.map +1 -1
  72. package/dist/world/state.js +36 -12
  73. package/dist/world/state.js.map +1 -1
  74. package/package.json +8 -10
  75. package/src/base/bitset.ts +312 -31
  76. package/src/base/cache.ts +62 -0
  77. package/src/base/error.ts +16 -0
  78. package/src/base/math.ts +43 -1
  79. package/src/base/typing.ts +5 -0
  80. package/src/{packet → binary}/encoder.ts +6 -2
  81. package/src/client/client.ts +56 -23
  82. package/src/index.ts +12 -4
  83. package/src/{client → net}/tcp.ts +5 -3
  84. package/src/packet/{dispatcher.ts → listener.ts} +92 -177
  85. package/src/packet/sender.ts +176 -0
  86. package/src/physics/aabb.ts +9 -8
  87. package/src/physics/player.ts +20 -5
  88. package/src/version/codec.ts +29 -8
  89. package/src/version/registry.ts +58 -5
  90. package/src/world/block.ts +72 -2
  91. package/src/world/state.ts +59 -14
  92. package/test/bitset/index.ts +3 -8
  93. package/test/client/index.ts +23 -13
  94. package/test/state/index.ts +7 -0
  95. package/dist/client/tcp.d.ts.map +0 -1
  96. package/dist/client/tcp.js.map +0 -1
  97. package/dist/packet/dispatcher.d.ts.map +0 -1
  98. package/dist/packet/dispatcher.js.map +0 -1
  99. package/dist/packet/encoder.d.ts.map +0 -1
  100. package/dist/packet/encoder.js.map +0 -1
  101. /package/dist/{packet → binary}/decoder.d.ts +0 -0
  102. /package/dist/{packet → binary}/decoder.js +0 -0
  103. /package/dist/{packet → binary}/static.d.ts +0 -0
  104. /package/dist/{packet → binary}/static.js +0 -0
  105. /package/src/{packet → binary}/decoder.ts +0 -0
  106. /package/src/{packet → binary}/static.ts +0 -0
@@ -3,17 +3,19 @@ import { 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";
6
- import { Message, TCPClient, TCPClientOption } from "./tcp";
6
+ import { Message, TCPClient, TCPClientOption } from "../net/tcp";
7
7
  import { TypedEmmiter as TypedEmitter } from "../base/event";
8
- import { BlockRegistry, EntityRegistry, PacketRegistry, ProtocolVersionMapping } from "../version/registry";
8
+ import { BlockRegistry, EffectRegistry, EntityRegistry, PacketRegistry, ProtocolVersionMapping } from "../version/registry";
9
9
  import { BaseVec3, Vec3 } from "../physics/direction";
10
- import { TextComponent } from "../base/typing";
10
+ import { RegistryData, TextComponent } from "../base/typing";
11
11
  import { ClientStatus, ConnectionState, EmittedEvent as EmittedEvent, Entity, SharedState } from "../world/state";
12
- import { Dispatcher } from "../packet/dispatcher";
12
+ import { Listener } from "../packet/listener";
13
13
  import { VersionCodec } from "../version/codec";
14
14
  import { AuthClient } from "./auth";
15
15
  import { computeUUID } from "../base/math";
16
16
  import { HaveSignatureButNotIndex } from "../base/error";
17
+ import { Sender } from "../packet/sender";
18
+ import { CacheImplementation } from "../base/cache";
17
19
 
18
20
  export type ClientOption = Omit<TCPClientOption, "protocolVersion"> & {
19
21
  version: string,
@@ -23,12 +25,26 @@ export type ClientOption = Omit<TCPClientOption, "protocolVersion"> & {
23
25
  *
24
26
  * When set this option to true, you HAVE TO set `shouldVerifyMessageOrder` to true
25
27
  */
26
- shouldVerifyMessageSignature?: boolean
28
+ shouldVerifyMessageSignature?: boolean,
27
29
 
28
30
  /**
29
31
  * Should verify the message orders to keep the sync with server.
30
32
  */
31
- shouldVerifyMessageOrder?: boolean
33
+ shouldVerifyMessageOrder?: boolean,
34
+
35
+ /**
36
+ * Whether to load the whole chunk section (16x16x16) and keep them in the cache or just load the needed block.
37
+ *
38
+ * Please consider this carefully since this can consume a lot of resource to calculate the whole chunk section and store it while you may not need that much.
39
+ *
40
+ * This option is useful when you have to constantly access to the world chunk, e.g pathfinding.
41
+ */
42
+ loadAndCacheChunk?: boolean,
43
+
44
+ /**
45
+ * Custom cache class. Default to `LRUCache`
46
+ */
47
+ cacheImplementation?: new () => CacheImplementation<Uint16Array>,
32
48
  }
33
49
 
34
50
  export interface ClientEvents {
@@ -71,6 +87,8 @@ export interface ClientEvents {
71
87
  updateEntity: [entityId: number],
72
88
  /** When an entity go out of client view distance or despawned */
73
89
  removeEntity: [entityId: number],
90
+ /** The registry_data packet */
91
+ registryData: [data: RegistryData],
74
92
 
75
93
  // Chat
76
94
  /** When a chat message got broadcasted */
@@ -101,6 +119,8 @@ export class Client<IsReady extends boolean = boolean> extends (EventEmitter as
101
119
 
102
120
  private tcp: TCPClient;
103
121
  private state: SharedState<IsReady>;
122
+ private packetListener: Listener;
123
+ private packetSender: Sender;
104
124
 
105
125
  constructor(private options: ClientOption) {
106
126
  if (options.shouldVerifyMessageSignature === true && options.shouldVerifyMessageOrder !== true)
@@ -111,7 +131,8 @@ export class Client<IsReady extends boolean = boolean> extends (EventEmitter as
111
131
  // For managing shared data
112
132
  this.state = new SharedState(options);
113
133
  // For handling packet
114
- const dispatcher = new Dispatcher(this.state, this.emit.bind(this));
134
+ this.packetSender = new Sender(this.state, this.emit.bind(this));
135
+ this.packetListener = new Listener(this.state, this.emit.bind(this), this.packetSender);
115
136
  // For reading packet
116
137
  const versionCodec = new VersionCodec(this.state);
117
138
  // For throwing packet through internet
@@ -126,12 +147,12 @@ export class Client<IsReady extends boolean = boolean> extends (EventEmitter as
126
147
  this.tcp.forwardPacket = versionCodec.handlePacket.bind(versionCodec);
127
148
  this.tcp.parsePacket = versionCodec.encodePacket.bind(versionCodec);
128
149
  this.tcp.sendInitPacket = () => {
129
- dispatcher.sendHandshake();
130
- dispatcher.sendLoginStart();
150
+ this.packetSender.sendHandshake();
151
+ this.packetSender.sendLoginStart();
131
152
  };
132
- dispatcher.sendPacket = this.tcp.sendPacket.bind(this.tcp);
133
- dispatcher.disconnect = this.disconnect.bind(this);
134
- versionCodec.consumePacket = dispatcher.handlePacket.bind(dispatcher);
153
+ this.packetSender.sendPacket = this.tcp.sendPacket.bind(this.tcp);
154
+ this.packetListener.disconnect = this.disconnect.bind(this);
155
+ versionCodec.consumePacket = this.packetListener.handlePacket.bind(this.packetListener);
135
156
 
136
157
  // For querying data
137
158
  this.blocks = new BlockManager(this.state);
@@ -143,24 +164,27 @@ export class Client<IsReady extends boolean = boolean> extends (EventEmitter as
143
164
  this.entities,
144
165
  this.blocks,
145
166
  );
146
- this.player.sendPlayerPos = dispatcher.sendPlayerPos;
147
- this.player.sendPlayerPosRot = dispatcher.sendPlayerPosRot;
148
- this.player.sendPlayerRot = dispatcher.sendPlayerRot;
149
- this.player.sendPlayerStatus = dispatcher.sendPlayerStatus;
167
+ this.player.sendPlayerPos = this.packetSender.sendPlayerPos;
168
+ this.player.sendPlayerPosRot = this.packetSender.sendPlayerPosRot;
169
+ this.player.sendPlayerRot = this.packetSender.sendPlayerRot;
170
+ this.player.sendPlayerStatus = this.packetSender.sendPlayerStatus;
150
171
 
151
172
  // For tick-tock tick-tock
152
173
  this.tickLoop = new TickLoop(() => {
153
174
  this.state.drainMutation();
154
- this.state.drainEvent()
155
- .forEach(event => this.emitEvent(event));
156
- this.state.drainPacket()
157
- .forEach((packet) => this.tcp.sendPacket(packet.id, packet.data));
175
+ this.state.drainEvent(event => this.emitEvent(event));
176
+ this.state.drainPacket((packet) => this.tcp.sendPacket(packet.id, packet.data));
158
177
  this.player.tick();
159
178
  });
160
179
 
161
180
  // Forward TCP events
162
181
  this.tcp.on("connect", () => this.emit("connect"));
163
182
  this.tcp.on("destroy", () => this.emit("destroy"));
183
+ this.on("loadChunk", (sx, sy, sz) => this.blocks.deleteChunkCache(sx, sy, sz));
184
+ this.on("unloadChunk", (sx, sz) => {
185
+ for (let sy = 0; sy < 16; sy++)
186
+ this.blocks.deleteChunkCache(sx, sy, sz);
187
+ });
164
188
  }
165
189
 
166
190
  private emitEvent(event: EmittedEvent) {
@@ -170,9 +194,11 @@ export class Client<IsReady extends boolean = boolean> extends (EventEmitter as
170
194
  private async loadRegistries() {
171
195
  if (Client.loadRegistry) return;
172
196
  Client.loadRegistry = true;
173
- await BlockRegistry.load(this.options.version);
174
- await EntityRegistry.load(this.options.version);
175
- await PacketRegistry.load(this.options.version);
197
+ const { version } = this.options;
198
+ await BlockRegistry.load(version);
199
+ await EntityRegistry.load(version);
200
+ await PacketRegistry.load(version);
201
+ await EffectRegistry.load(version);
176
202
  }
177
203
 
178
204
  // Start / stop
@@ -241,6 +267,13 @@ export class Client<IsReady extends boolean = boolean> extends (EventEmitter as
241
267
  this.player.releaseAllInputs();
242
268
  }
243
269
 
270
+ /**
271
+ * Send a message
272
+ */
273
+ public chat(message: string) {
274
+ this.packetSender.sendMessage(message);
275
+ }
276
+
244
277
  // World
245
278
  /**
246
279
  * Get the block state at position
package/src/index.ts CHANGED
@@ -1,14 +1,22 @@
1
1
  export * from "./base/bitset";
2
+ export * from "./base/buffer";
3
+ export * from "./base/cache";
2
4
  export * from "./base/error";
3
5
  export * from "./base/event";
6
+ export * from "./base/http";
4
7
  export * from "./base/math";
5
8
  export * from "./base/typing";
9
+ export * from "./binary/decoder";
10
+ export * from "./binary/encoder";
11
+ export * from "./binary/static";
6
12
  export * from "./client/auth";
7
13
  export * from "./client/client";
8
- export * from "./client/tcp";
9
- export * from "./packet/decoder";
10
- export * from "./packet/encoder";
11
- export * from "./packet/dispatcher";
14
+ export * from "./client/static";
15
+ export * from "./message/link";
16
+ export * from "./message/sigCache";
17
+ export * from "./net/tcp";
18
+ export * from "./packet/listener";
19
+ export * from "./packet/sender";
12
20
  export * from "./physics/aabb";
13
21
  export * from "./physics/direction";
14
22
  export * from "./physics/player";
@@ -3,9 +3,9 @@ import { EventEmitter } from "node:events";
3
3
  import { deflateSync, inflateSync } from "node:zlib";
4
4
 
5
5
  import { TypedEmmiter } from "../base/event";
6
- import { BinaryDecoder } from "../packet/decoder";
7
- import { BinaryEncoder } from "../packet/encoder";
8
- import { AuthOption } from "./auth";
6
+ import { BinaryDecoder } from "../binary/decoder";
7
+ import { BinaryEncoder } from "../binary/encoder";
8
+ import { AuthOption } from "../client/auth";
9
9
  import { ClientStatus, SharedState } from "../world/state";
10
10
  import { EncodeResult } from "../version/codec";
11
11
  import { TextComponent } from "../base/typing";
@@ -171,6 +171,8 @@ 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)
175
+ console.log("packet", packetID, "length", decoder.buffer.length);
174
176
  this.forwardPacket(packetID, decoder);
175
177
 
176
178
  this.bufferPool = this.bufferPool.subarray(expectedPacketEnd);
@@ -1,61 +1,67 @@
1
1
  import zod from "zod";
2
- import { createCipheriv, createDecipheriv, createPublicKey, publicEncrypt, randomBytes, constants as crypto_constants, createVerify } from "node:crypto";
3
- import { BlockEntity, ChunkSection, ClientPlayer, ClientStatus, ConnectionState, Entity, GameMode, PaletteContainer, ServerKnownPack, ServerWorld, SharedState } from "../world/state";
4
- import { BinaryDecoder, getTextFromTextComponent } from "./decoder";
2
+ import { createCipheriv, createDecipheriv, randomBytes, createVerify } from "node:crypto";
3
+ import { BlockEntity, ChunkSection, ClientPlayer, ClientStatus, Entity, GameMode, ServerKnownPack, ServerWorld, SharedState } from "../world/state";
4
+ import { BinaryDecoder, getTextFromTextComponent } from "../binary/decoder";
5
5
  import { packBlockPos, SectionsPerChunk } from "../client/static";
6
- import { Angle, BaseVec3, Vec3 } from "../physics/direction";
7
- import { ProtocolVersionMapping } from "../version/registry";
6
+ import { Vec3 } from "../physics/direction";
8
7
  import { TypedEmmiter } from "../base/event";
9
8
  import { ClientEvents } from "../client/client";
10
- import { makeMovementFlag } from "./encoder";
9
+ import { makeMovementFlag } from "../binary/encoder";
11
10
  import { TextComponent } from "../base/typing";
12
- import { AuthRelatedNotFound, HaveSignatureButNotIndex, MessageLinkNotFound, MissingAuthOption, NotImplemented, UnexpectedValue } from "../base/error";
13
- import { randomUUIDBytes, uuidToBuffer } from "../base/math";
11
+ import { AuthRelatedNotFound, HaveSignatureButNotIndex, MessageLinkNotFound, NotImplemented, UnexpectedValue } from "../base/error";
12
+ import { uuidToBuffer } from "../base/math";
14
13
  import { MessageLink } from "../message/link";
15
- import { sliceBuffer } from "../base/buffer";
14
+ import { Sender } from "./sender";
16
15
 
17
16
  function zodParse<Type extends zod.ZodType>(data: object, zod: Type): zod.infer<Type> {
18
17
  return zod.parse(data);
19
18
  }
20
19
 
21
- export class Dispatcher {
20
+ export class Listener {
22
21
  private readonly mapping: Record<string, (data: object) => void>;
23
22
 
24
23
  constructor(
25
24
  private state: SharedState,
26
- private emit: TypedEmmiter<ClientEvents>["emit"]
25
+ private emit: TypedEmmiter<ClientEvents>["emit"],
26
+ private sender: Sender,
27
27
  ) {
28
28
  this.mapping = {
29
29
  "login:login_disconnect": this.handleLoginDisconnect,
30
30
  "login:hello": this.handleEncryption,
31
31
  "login:login_finished": this.handleLoginSucess,
32
32
  "login:login_compression": this.handleSetCompression,
33
+
33
34
  "configuration:disconnect": this.handleConfiguarionPlayDisconnect,
34
35
  "configuration:keep_alive": this.handleKeepAlive,
35
36
  "configuration:registry_data": this.handleRegistryData,
36
37
  "configuration:select_known_packs": this.handleKnownPack,
37
38
  "configuration:finish_configuration": this.handleFinishConfiguration,
38
- "play:add_entity": this.handleSpawnEntity,
39
+
40
+ "play:login": this.handlePlayLogin,
39
41
  "play:disconnect": this.handleConfiguarionPlayDisconnect,
40
- "play:disguised_chat": this.handleDisguisedChatMessage,
41
- "play:entity_position_sync": this.handleTeleportEntity,
42
- "play:forget_level_chunk": this.handleUnloadChunk,
43
42
  "play:keep_alive": this.handleKeepAlive,
43
+
44
+ "play:player_chat": this.handlePlayerChat,
45
+ "play:disguised_chat": this.handleDisguisedChatMessage,
46
+ "play:system_chat": this.handleSystemMessage,
47
+ "play:set_action_bar_text": this.handleSetActionBar,
48
+
44
49
  "play:level_chunk_with_light": this.handleChunkData,
45
- "play:login": this.handlePlayLogin,
50
+ "play:forget_level_chunk": this.handleUnloadChunk,
51
+
52
+ "play:add_entity": this.handleSpawnEntity,
46
53
  "play:move_entity_pos": this.handleUpdateEntityPosition,
47
54
  "play:move_entity_pos_rot": this.handleUpdateEntityPositionRotation,
48
- "play:player_chat": this.handlePlayerChat,
55
+ "play:entity_position_sync": this.handleTeleportEntity,
49
56
  "play:player_position": this.handleSynchronizePlayerPosition,
57
+ "play:update_mob_effect": this.handleUpdateMobEffect,
50
58
  "play:remove_entities": this.handleRemoveEntity,
51
- // "play:rotate_head":
52
- "play:set_action_bar_text": this.handleSetActionBar,
59
+ "play:remove_mob_effect": this.handleRemoveMobEffect,
60
+ // "play:rotate_head": ,
53
61
  "play:set_entity_motion": this.handleSetEntityVelocity,
54
- "play:system_chat": this.handleSystemMessage,
55
62
  };
56
63
  }
57
64
 
58
- public sendPacket: (packetId: string, data: object) => void = () => { throw new NotImplemented(); };
59
65
  public disconnect: (reason: string, reasonRaw?: TextComponent) => void = () => { throw new NotImplemented(); };
60
66
 
61
67
 
@@ -99,9 +105,9 @@ export class Dispatcher {
99
105
  throw new AuthRelatedNotFound("auth client");
100
106
  else
101
107
  this.state.authClient.sendAuth(server_id, sharedSecret, publicKey)
102
- .then(() => this.sendEncryptionResponse(publicKey, verifyToken));
108
+ .then(() => this.sender.sendEncryptionResponse(publicKey, verifyToken));
103
109
  else
104
- this.sendEncryptionResponse(publicKey, verifyToken);
110
+ this.sender.sendEncryptionResponse(publicKey, verifyToken);
105
111
  }
106
112
 
107
113
  private handleSetCompression(data: object) {
@@ -136,9 +142,10 @@ export class Dispatcher {
136
142
  angle: {
137
143
  yaw: 0,
138
144
  pitch: 0,
139
- }
145
+ },
146
+ effects: {}
140
147
  } satisfies ClientPlayer as any;
141
- this.sendLoginAck();
148
+ this.sender.sendLoginAck();
142
149
  }
143
150
 
144
151
  // Configure
@@ -152,8 +159,8 @@ export class Dispatcher {
152
159
  }))
153
160
  }));
154
161
  this.state.server!.knownPacks = known_packs satisfies ServerKnownPack[] as any;
155
- this.sendKnownPack(this.state.clientOptions.loadRegistry === true ? [] : known_packs);
156
- // this.sendKnownPack([]);
162
+ this.sender.sendKnownPack(this.state.clientOptions.loadRegistry === true ? [] : known_packs);
163
+ // this.sender.sendKnownPack([]);
157
164
  }
158
165
 
159
166
  private handleRegistryData(data: object) {
@@ -171,10 +178,11 @@ export class Dispatcher {
171
178
  const registry = this.state.registry as Record<string, any[]>;
172
179
  if (!(registry_id in registry)) registry[registry_id] = [];
173
180
  registry[registry_id]!.push(...entries);
181
+ this.emit("registryData", { registry_id, entries });
174
182
  }
175
183
 
176
184
  private handleFinishConfiguration(data: object) {
177
- this.sendConfigureAck();
185
+ this.sender.sendConfigureAck();
178
186
  }
179
187
 
180
188
  private handleConfiguarionPlayDisconnect(data: object) {
@@ -225,7 +233,7 @@ export class Dispatcher {
225
233
  this.state.status = ClientStatus.Ready;
226
234
 
227
235
  if (this.state.useEncryption)
228
- this.sendPlayerSession();
236
+ this.sender.sendPlayerSession();
229
237
 
230
238
  this.emit("ready");
231
239
  }
@@ -291,8 +299,8 @@ export class Dispatcher {
291
299
  } satisfies ClientPlayer as any
292
300
  );
293
301
  this.state.enqueueEvent("playerPosition", { x: newPosX, y: newPosY, z: newPosZ });
294
- this.sendConfirmTeleportation(teleport_id);
295
- this.sendPacket("move_player_pos_rot", {
302
+ this.sender.sendConfirmTeleportation(teleport_id);
303
+ this.sender.sendPacket("move_player_pos_rot", {
296
304
  x: newPosition.x,
297
305
  feet_y: newPosition.y,
298
306
  z: newPosition.z,
@@ -308,7 +316,7 @@ export class Dispatcher {
308
316
  keep_alive_id: zod.bigint().or(zod.number())
309
317
  })
310
318
  );
311
- this.sendKeepAlive(BigInt(keep_alive_id));
319
+ this.sender.sendKeepAlive(BigInt(keep_alive_id));
312
320
  }
313
321
 
314
322
  private handleChangeGameMode(data: object) {
@@ -337,7 +345,7 @@ export class Dispatcher {
337
345
  type: zod.int(),
338
346
  data: zod.record(zod.string(), zod.any())
339
347
  })),
340
- light: zod.any(),
348
+ light: zod.any().optional(),
341
349
  })
342
350
  );
343
351
 
@@ -602,6 +610,53 @@ export class Dispatcher {
602
610
  });
603
611
  }
604
612
 
613
+ private handleUpdateMobEffect(data: object) {
614
+ const {
615
+ entity_id,
616
+ effect_id,
617
+ amplifier,
618
+ duration,
619
+ flags
620
+ } = zodParse(data, zod.object({
621
+ entity_id: zod.number(),
622
+ effect_id: zod.number(),
623
+ amplifier: zod.number(),
624
+ duration: zod.number(),
625
+ flags: zod.number(),
626
+ }));
627
+ const isAmbient = (flags & 0x01) == 1,
628
+ showParticles = (flags & 0x02) == 1,
629
+ showIcon = (flags & 0x04) == 1,
630
+ blend = (flags & 0x08) == 1;
631
+ this.state.enqueueMutation((state) => {
632
+ if (state.player!.entityId === entity_id)
633
+ state.player!.effects[effect_id] = {
634
+ amplifier,
635
+ duration,
636
+ id: effect_id,
637
+ flag: {
638
+ blend,
639
+ isAmbient,
640
+ showIcon,
641
+ showParticles
642
+ },
643
+ date: Date.now()
644
+ };
645
+ });
646
+ }
647
+
648
+ private handleRemoveMobEffect(data: object) {
649
+ const { entity_id, effect_id } = zodParse(data, zod.object({
650
+ entity_id: zod.number(),
651
+ effect_id: zod.number(),
652
+ }));
653
+
654
+ this.state.enqueueMutation((state) => {
655
+ if (state.player!.entityId === entity_id)
656
+ delete state.player!.effects[effect_id];
657
+ });
658
+ }
659
+
605
660
  // Chat
606
661
  private handlePlayerChat(data: object) {
607
662
  const {
@@ -645,7 +700,7 @@ export class Dispatcher {
645
700
  const emitObject = {
646
701
  sender: senderNameText,
647
702
  target: targetNameText,
648
- content: getTextFromTextComponent(unsigned_content),
703
+ content: message,
649
704
  raw: {
650
705
  sender: sender_name,
651
706
  target: target_name || undefined,
@@ -677,6 +732,8 @@ export class Dispatcher {
677
732
  }
678
733
  }
679
734
 
735
+ if (message_signature_bytes !== null)
736
+ this.state.messageCount += 1;
680
737
  if (this.state.clientOptions.shouldVerifyMessageSignature === true && message_signature_bytes) {
681
738
  if (!this.state.sessionID)
682
739
  throw new AuthRelatedNotFound("session uuid");
@@ -716,7 +773,7 @@ export class Dispatcher {
716
773
  const signature = Buffer.from(message_signature_bytes);
717
774
 
718
775
  if (!verify.verify(publicKey!, signature))
719
- this.emit("failedMessage", emitObject);
776
+ return this.emit("failedMessage", emitObject);
720
777
  }
721
778
 
722
779
  this.emit("message", emitObject);
@@ -774,146 +831,4 @@ export class Dispatcher {
774
831
  }
775
832
  });
776
833
  }
777
-
778
- /*
779
- * Send packet
780
- */
781
-
782
- // Login
783
-
784
- public sendHandshake() {
785
- this.state.state = ConnectionState.Handshake;
786
- this.sendPacket("intention",
787
- {
788
- protocol_version: ProtocolVersionMapping[this.state.clientOptions.version]!,
789
- server_address: this.state.clientOptions.host,
790
- server_port: this.state.clientOptions.port,
791
- intent: 2
792
- }
793
- );
794
- }
795
-
796
- public sendLoginStart() {
797
- this.state.state = ConnectionState.Login;
798
- this.state.status = ClientStatus.Logining;
799
- this.sendPacket("hello", {
800
- name: this.state.clientOptions.playerName,
801
- player_uuid: Buffer.from(this.state.playerUUID!, "hex")
802
- });
803
- }
804
-
805
- private sendEncryptionResponse(publicKey: Buffer, verifyToken: Buffer) {
806
- const encryptedSecret = publicEncrypt({
807
- key: createPublicKey({ key: publicKey, format: 'der', type: 'spki' }),
808
- padding: crypto_constants.RSA_PKCS1_PADDING
809
- }, this.state.sharedSecret!);
810
- const encryptedVerifyToken = publicEncrypt({
811
- key: createPublicKey({ key: publicKey, format: 'der', type: 'spki' }),
812
- padding: crypto_constants.RSA_PKCS1_PADDING
813
- }, verifyToken);
814
- const secretBuf = sliceBuffer(encryptedSecret, 1, (buf) => buf.readInt8());
815
- const token = sliceBuffer(encryptedVerifyToken, 1, (buf) => buf.readInt8());
816
- this.sendPacket("key", {
817
- shared_secret: { value: secretBuf, length: secretBuf.length },
818
- verify_token: { value: token, length: token.length }
819
- });
820
- }
821
-
822
- private sendLoginAck() {
823
- this.sendPacket("login_acknowledged", {});
824
- this.state.state = ConnectionState.Configure;
825
- this.state.server = {};
826
- }
827
-
828
- // Configuring
829
-
830
- private sendKnownPack(knownPacks: ServerKnownPack[]) {
831
- this.sendPacket("select_known_packs", {
832
- known_packs: {
833
- value: knownPacks,
834
- length: knownPacks.length
835
- }
836
- });
837
- }
838
-
839
- private sendConfigureAck() {
840
- this.sendPacket("finish_configuration", {});
841
- this.state.state = ConnectionState.Play;
842
- }
843
-
844
- // Play
845
-
846
- private sendPlayerSession() {
847
- const sessionUUID = randomUUIDBytes();
848
- const expireTime = 15 * 60 * 1000; // 15min in ms
849
- const expiredAt = Date.now() + expireTime;
850
- const { publicKey, signature } = this.state.getSignature();
851
- this.state.sessionID = sessionUUID;
852
- this.sendPacket("chat_session_update", {
853
- session_id: sessionUUID,
854
- expire_at: expiredAt,
855
- public_key: publicKey,
856
- signature
857
- });
858
- }
859
-
860
- private sendConfirmTeleportation(teleportId: number) {
861
- this.sendPacket("accept_teleportation", {
862
- teleport_id: teleportId
863
- });
864
- }
865
-
866
- private sendKeepAlive(id: bigint) {
867
- this.sendPacket("keep_alive", {
868
- keep_alive_id: id
869
- });
870
- }
871
-
872
- public sendPlayerPos(
873
- position: BaseVec3,
874
- onGround: boolean,
875
- horizontalCollision: boolean
876
- ) {
877
- const { x, y, z } = position;
878
- this.state.enqueuePacket("move_player_pos", {
879
- x, feet_y: y, z,
880
- flags: makeMovementFlag(onGround, horizontalCollision)
881
- });
882
- }
883
-
884
- public sendPlayerPosRot(
885
- position: BaseVec3,
886
- angle: Angle,
887
- onGround: boolean,
888
- horizontalCollision: boolean
889
- ) {
890
- const { x, y, z } = position,
891
- { yaw, pitch } = angle;
892
- this.state.enqueuePacket("move_player_pos_rot", {
893
- x, feet_y: y, z,
894
- yaw, pitch,
895
- flags: makeMovementFlag(onGround, horizontalCollision)
896
- });
897
- }
898
-
899
- public sendPlayerRot(
900
- angle: Angle,
901
- onGround: boolean,
902
- horizontalCollision: boolean
903
- ) {
904
- const { yaw, pitch } = angle;
905
- this.state.enqueuePacket("move_player_rot", {
906
- yaw, pitch,
907
- flags: makeMovementFlag(onGround, horizontalCollision)
908
- });
909
- }
910
-
911
- public sendPlayerStatus(
912
- onGround: boolean,
913
- horizontalCollision: boolean
914
- ) {
915
- this.state.enqueuePacket("move_player_status_only", {
916
- flags: makeMovementFlag(onGround, horizontalCollision)
917
- });
918
- }
919
834
  }