@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.
- package/README.md +4 -0
- package/assets/minecraft/26.2/components.json +113 -0
- package/assets/minecraft/26.2/packets.json +8 -16
- package/assets/minecraft/26.3/packets.json +4767 -0
- package/assets/minecraft/versionMapping.json +16 -0
- package/dist/base/bitset.d.ts.map +1 -1
- package/dist/base/bitset.js.map +1 -1
- package/dist/base/buffer.d.ts +1 -0
- package/dist/base/buffer.d.ts.map +1 -1
- package/dist/base/buffer.js +1 -0
- package/dist/base/buffer.js.map +1 -1
- package/dist/base/http.d.ts +1 -0
- package/dist/base/http.d.ts.map +1 -1
- package/dist/base/http.js +1 -0
- package/dist/base/http.js.map +1 -1
- package/dist/base/math.d.ts +2 -0
- package/dist/base/math.d.ts.map +1 -1
- package/dist/base/math.js +2 -0
- package/dist/base/math.js.map +1 -1
- package/dist/base/typing.d.ts +5 -0
- package/dist/base/typing.d.ts.map +1 -1
- package/dist/binary/decoder.d.ts +39 -0
- package/dist/binary/decoder.d.ts.map +1 -1
- package/dist/binary/decoder.js +669 -2
- package/dist/binary/decoder.js.map +1 -1
- package/dist/binary/encoder.d.ts +7 -3
- package/dist/binary/encoder.d.ts.map +1 -1
- package/dist/binary/encoder.js +4 -0
- package/dist/binary/encoder.js.map +1 -1
- package/dist/client/client.d.ts +58 -1
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js +38 -0
- package/dist/client/client.js.map +1 -1
- package/dist/message/link.d.ts +3 -0
- package/dist/message/link.d.ts.map +1 -1
- package/dist/message/link.js +3 -0
- package/dist/message/link.js.map +1 -1
- package/dist/message/sigCache.d.ts +5 -0
- package/dist/message/sigCache.d.ts.map +1 -1
- package/dist/message/sigCache.js +5 -0
- package/dist/message/sigCache.js.map +1 -1
- package/dist/net/tcp.d.ts +7 -3
- package/dist/net/tcp.d.ts.map +1 -1
- package/dist/net/tcp.js +6 -3
- package/dist/net/tcp.js.map +1 -1
- package/dist/packet/listener.d.ts +8 -0
- package/dist/packet/listener.d.ts.map +1 -1
- package/dist/packet/listener.js +81 -5
- package/dist/packet/listener.js.map +1 -1
- package/dist/packet/sender.d.ts +6 -1
- package/dist/packet/sender.d.ts.map +1 -1
- package/dist/packet/sender.js +18 -0
- package/dist/packet/sender.js.map +1 -1
- package/dist/physics/aabb.d.ts +3 -0
- package/dist/physics/aabb.d.ts.map +1 -1
- package/dist/physics/aabb.js +10 -7
- package/dist/physics/aabb.js.map +1 -1
- package/dist/physics/direction.d.ts +3 -0
- package/dist/physics/direction.d.ts.map +1 -1
- package/dist/physics/direction.js +3 -0
- package/dist/physics/direction.js.map +1 -1
- package/dist/physics/player.d.ts +3 -0
- package/dist/physics/player.d.ts.map +1 -1
- package/dist/physics/player.js +4 -5
- package/dist/physics/player.js.map +1 -1
- package/dist/version/codec.d.ts +4 -0
- package/dist/version/codec.d.ts.map +1 -1
- package/dist/version/codec.js +29 -14
- package/dist/version/codec.js.map +1 -1
- package/dist/version/registry.d.ts +30 -6
- package/dist/version/registry.d.ts.map +1 -1
- package/dist/version/registry.js +64 -19
- package/dist/version/registry.js.map +1 -1
- package/dist/world/block.d.ts +10 -8
- package/dist/world/block.d.ts.map +1 -1
- package/dist/world/block.js +17 -11
- package/dist/world/block.js.map +1 -1
- package/dist/world/container.d.ts +106 -0
- package/dist/world/container.d.ts.map +1 -0
- package/dist/world/container.js +240 -0
- package/dist/world/container.js.map +1 -0
- package/dist/world/state.d.ts +11 -5
- package/dist/world/state.d.ts.map +1 -1
- package/dist/world/state.js +5 -0
- package/dist/world/state.js.map +1 -1
- package/package.json +2 -2
- package/src/base/bitset.ts +0 -1
- package/src/base/buffer.ts +1 -0
- package/src/base/http.ts +1 -0
- package/src/base/math.ts +2 -0
- package/src/base/typing.ts +6 -0
- package/src/binary/decoder.ts +695 -2
- package/src/binary/encoder.ts +7 -3
- package/src/client/client.ts +81 -4
- package/src/message/link.ts +3 -0
- package/src/message/sigCache.ts +5 -0
- package/src/net/tcp.ts +17 -10
- package/src/packet/listener.ts +106 -6
- package/src/packet/sender.ts +29 -2
- package/src/physics/aabb.ts +10 -7
- package/src/physics/direction.ts +3 -0
- package/src/physics/player.ts +8 -5
- package/src/version/codec.ts +29 -14
- package/src/version/registry.ts +71 -18
- package/src/world/block.ts +19 -17
- package/src/world/container.ts +180 -0
- package/src/world/state.ts +14 -6
- package/test/client/index.ts +49 -21
- package/test/client/registry.json +1735 -0
package/src/binary/encoder.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
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:
|
|
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:
|
|
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
|
package/src/client/client.ts
CHANGED
|
@@ -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
|
|
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
|
|
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
|
}
|
package/src/message/link.ts
CHANGED
package/src/message/sigCache.ts
CHANGED
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
|
|
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 =
|
|
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 (
|
|
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
|
|
package/src/packet/listener.ts
CHANGED
|
@@ -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
|
|
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
|
}
|
package/src/packet/sender.ts
CHANGED
|
@@ -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
|
}
|
package/src/physics/aabb.ts
CHANGED
|
@@ -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 =
|
|
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,
|
|
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,
|
|
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
|
|
602
|
+
else if (lastStartZ === -1)
|
|
600
603
|
lastStartZ = z;
|
|
601
|
-
} else if (lastStartZ
|
|
604
|
+
} else if (lastStartZ !== -1) {
|
|
602
605
|
let endX = x,
|
|
603
606
|
endY = y;
|
|
604
607
|
shape.clearZStrip(lastStartZ, z, endX, endY);
|
package/src/physics/direction.ts
CHANGED
package/src/physics/player.ts
CHANGED
|
@@ -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(
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
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
|
}
|
package/src/version/codec.ts
CHANGED
|
@@ -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
|
|
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
|
|
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:
|
|
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":
|
|
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)
|