@vaitosoi/minejs 0.5.1 → 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/binary/decoder.d.ts +35 -0
- package/dist/binary/decoder.d.ts.map +1 -1
- package/dist/binary/decoder.js +665 -2
- package/dist/binary/decoder.js.map +1 -1
- package/dist/binary/encoder.d.ts +3 -3
- package/dist/binary/encoder.d.ts.map +1 -1
- 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/net/tcp.d.ts +6 -2
- package/dist/net/tcp.d.ts.map +1 -1
- package/dist/net/tcp.js +5 -2
- package/dist/net/tcp.js.map +1 -1
- package/dist/packet/listener.d.ts +5 -0
- package/dist/packet/listener.d.ts.map +1 -1
- package/dist/packet/listener.js +78 -5
- package/dist/packet/listener.js.map +1 -1
- package/dist/packet/sender.d.ts +3 -1
- package/dist/packet/sender.d.ts.map +1 -1
- package/dist/packet/sender.js +15 -0
- package/dist/packet/sender.js.map +1 -1
- package/dist/physics/aabb.js +7 -7
- package/dist/physics/aabb.js.map +1 -1
- package/dist/physics/player.js +1 -5
- package/dist/physics/player.js.map +1 -1
- package/dist/version/codec.d.ts.map +1 -1
- package/dist/version/codec.js +25 -14
- package/dist/version/codec.js.map +1 -1
- package/dist/version/registry.d.ts +21 -5
- package/dist/version/registry.d.ts.map +1 -1
- package/dist/version/registry.js +55 -18
- 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 +8 -5
- package/dist/world/state.d.ts.map +1 -1
- package/dist/world/state.js +2 -0
- package/dist/world/state.js.map +1 -1
- package/package.json +2 -2
- package/src/binary/decoder.ts +691 -2
- package/src/binary/encoder.ts +3 -3
- package/src/client/client.ts +81 -4
- package/src/net/tcp.ts +16 -5
- package/src/packet/listener.ts +103 -6
- package/src/packet/sender.ts +26 -2
- package/src/physics/aabb.ts +7 -7
- package/src/physics/player.ts +5 -5
- package/src/version/codec.ts +24 -14
- package/src/version/registry.ts +61 -17
- package/src/world/block.ts +19 -17
- package/src/world/container.ts +180 -0
- package/src/world/state.ts +11 -6
- package/test/client/index.ts +49 -21
- package/test/client/registry.json +1735 -0
package/src/version/registry.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { RegistryItemNotFound, UnexpectedValue, VersionNotSupport } from "../base/error";
|
|
4
4
|
import { Block, BlockState } from "../world/block";
|
|
@@ -7,14 +7,27 @@ import z from "zod";
|
|
|
7
7
|
const BASE_REGISTRY_PATH = join(__dirname, "..", "..", "assets", "minecraft");
|
|
8
8
|
/** @hidden */
|
|
9
9
|
export const SupportVersions = [
|
|
10
|
-
"26.2"
|
|
10
|
+
"26.2",
|
|
11
|
+
"26.3"
|
|
11
12
|
];
|
|
12
13
|
|
|
13
14
|
/** @hidden */
|
|
14
15
|
export const ProtocolVersionMapping: Record<string, number> = {
|
|
15
|
-
"26.2": 776
|
|
16
|
+
"26.2": 776,
|
|
17
|
+
"26.3": 777
|
|
16
18
|
};
|
|
17
19
|
|
|
20
|
+
function getFile(version: string, module: string) {
|
|
21
|
+
const map = JSON.parse(readFileSync(join(BASE_REGISTRY_PATH, "versionMapping.json"), "utf-8"));
|
|
22
|
+
if (!(version in map))
|
|
23
|
+
throw new VersionNotSupport(version);
|
|
24
|
+
const versionMap = map[version];
|
|
25
|
+
if (!(module in versionMap))
|
|
26
|
+
throw new VersionNotSupport(version);
|
|
27
|
+
const fileContent = JSON.parse(readFileSync(join(BASE_REGISTRY_PATH, versionMap[module], `${module}.json`), "utf-8"));
|
|
28
|
+
return fileContent;
|
|
29
|
+
}
|
|
30
|
+
|
|
18
31
|
export class EntityRegistry {
|
|
19
32
|
private static loaded: boolean = false;
|
|
20
33
|
public static readonly data: Record<string, { height: number, width: number, type: number }> = {};
|
|
@@ -25,14 +38,13 @@ export class EntityRegistry {
|
|
|
25
38
|
*
|
|
26
39
|
* Should be called once time
|
|
27
40
|
*/
|
|
28
|
-
public static
|
|
41
|
+
public static load(version: string) {
|
|
29
42
|
if (!SupportVersions.includes(version))
|
|
30
43
|
throw new VersionNotSupport(version);
|
|
31
44
|
if (this.loaded) return;
|
|
32
45
|
this.loaded = true;
|
|
33
46
|
|
|
34
|
-
const
|
|
35
|
-
const json = JSON.parse(file);
|
|
47
|
+
const json = getFile(version, "entities");
|
|
36
48
|
for (const entity in json) {
|
|
37
49
|
this.data[entity] = json[entity];
|
|
38
50
|
this.mapTypeToData[json[entity]["id"]] = entity;
|
|
@@ -68,14 +80,13 @@ export class BlockRegistry {
|
|
|
68
80
|
*
|
|
69
81
|
* Should be called once time
|
|
70
82
|
*/
|
|
71
|
-
public static
|
|
83
|
+
public static load(version: string) {
|
|
72
84
|
if (!SupportVersions.includes(version))
|
|
73
85
|
throw new VersionNotSupport(version);
|
|
74
86
|
if (this.loaded) return;
|
|
75
87
|
this.loaded = true;
|
|
76
88
|
|
|
77
|
-
const
|
|
78
|
-
const json = JSON.parse(file) as Record<string, any>;
|
|
89
|
+
const json = getFile(version, "blocks") as Record<string, any>;
|
|
79
90
|
for (const [type, blockRaw] of Object.entries(json)) {
|
|
80
91
|
const block = new Block(type, blockRaw['definition'], blockRaw['properties'], blockRaw['states']);
|
|
81
92
|
this.blocks[type] = block;
|
|
@@ -113,20 +124,44 @@ export class EffectRegistry {
|
|
|
113
124
|
*
|
|
114
125
|
* Should be called once time
|
|
115
126
|
*/
|
|
116
|
-
public static
|
|
127
|
+
public static load(version: string) {
|
|
117
128
|
if (!SupportVersions.includes(version))
|
|
118
129
|
throw new VersionNotSupport(version);
|
|
119
130
|
if (this.loaded) return;
|
|
120
131
|
this.loaded = true;
|
|
121
132
|
|
|
122
|
-
const
|
|
123
|
-
const json = JSON.parse(file) as Record<string, any>;
|
|
133
|
+
const json = getFile(version, "effects") as Record<string, any>;
|
|
124
134
|
for (const [name, id] of Object.entries(json)) {
|
|
125
135
|
this.effects[name] = id;
|
|
126
136
|
}
|
|
127
137
|
}
|
|
128
138
|
}
|
|
129
139
|
|
|
140
|
+
export class ComponentTypeRegistry {
|
|
141
|
+
private static loaded: boolean = false;
|
|
142
|
+
|
|
143
|
+
public static readonly components: Record<string, number> = {};
|
|
144
|
+
public static readonly idToName: Record<number, string> = {};
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Load effects registry from JSON file.
|
|
148
|
+
*
|
|
149
|
+
* Should be called once time
|
|
150
|
+
*/
|
|
151
|
+
public static load(version: string) {
|
|
152
|
+
if (!SupportVersions.includes(version))
|
|
153
|
+
throw new VersionNotSupport(version);
|
|
154
|
+
if (this.loaded) return;
|
|
155
|
+
this.loaded = true;
|
|
156
|
+
|
|
157
|
+
const json = getFile(version, "components") as Record<string, number>;
|
|
158
|
+
for (const [name, id] of Object.entries(json)) {
|
|
159
|
+
this.components[name] = id;
|
|
160
|
+
this.idToName[id] = name;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
130
165
|
/** @hidden */
|
|
131
166
|
export const SupportTypes = z.enum([
|
|
132
167
|
"byte",
|
|
@@ -166,7 +201,9 @@ export const SupportTypes = z.enum([
|
|
|
166
201
|
"bitset",
|
|
167
202
|
"fixed_bitset",
|
|
168
203
|
"byte_array",
|
|
204
|
+
"slot",
|
|
169
205
|
"null", // a constant
|
|
206
|
+
"special",
|
|
170
207
|
|
|
171
208
|
/**
|
|
172
209
|
* This is used for handle field that its type depends on previous field type, or value
|
|
@@ -188,6 +225,7 @@ export type FieldNode = ({
|
|
|
188
225
|
| "not_implemented"
|
|
189
226
|
| "fixed_bitset"
|
|
190
227
|
| "byte_array"
|
|
228
|
+
| "special"
|
|
191
229
|
>,
|
|
192
230
|
} | {
|
|
193
231
|
type: "not_implemented",
|
|
@@ -221,13 +259,16 @@ export type FieldNode = ({
|
|
|
221
259
|
} | {
|
|
222
260
|
type: "fixed_bitset" | "byte_array",
|
|
223
261
|
length: number,
|
|
262
|
+
} | {
|
|
263
|
+
type: "special",
|
|
264
|
+
comment: string,
|
|
224
265
|
}) & {
|
|
225
266
|
skip_able?: boolean | undefined
|
|
226
267
|
}
|
|
227
268
|
/** @hidden */
|
|
228
269
|
export const Field: z.ZodType<FieldNode> = z.union([
|
|
229
270
|
z.object({
|
|
230
|
-
type: SupportTypes.exclude(["prefixed_array", "prefixed_optional", "string", "id_or_x", "array", "switch", "fixed_point", "enum", "not_implemented", "object", "fixed_bitset", "byte_array"]),
|
|
271
|
+
type: SupportTypes.exclude(["prefixed_array", "prefixed_optional", "string", "id_or_x", "array", "switch", "fixed_point", "enum", "not_implemented", "object", "fixed_bitset", "byte_array", "special"]),
|
|
231
272
|
}),
|
|
232
273
|
z.object({
|
|
233
274
|
type: z.literal(["not_implemented"]),
|
|
@@ -301,7 +342,11 @@ export const Field: z.ZodType<FieldNode> = z.union([
|
|
|
301
342
|
// These type require predefined-length
|
|
302
343
|
type: z.union([z.literal("fixed_bitset"), z.literal("byte_array")]),
|
|
303
344
|
length: z.int()
|
|
304
|
-
})
|
|
345
|
+
}),
|
|
346
|
+
z.object({
|
|
347
|
+
type: z.literal(["special"]),
|
|
348
|
+
comment: z.string()
|
|
349
|
+
}),
|
|
305
350
|
])
|
|
306
351
|
.and(
|
|
307
352
|
z.object({
|
|
@@ -347,14 +392,13 @@ export class PacketRegistry {
|
|
|
347
392
|
*
|
|
348
393
|
* Should be called once time
|
|
349
394
|
*/
|
|
350
|
-
public static
|
|
395
|
+
public static load(version: string) {
|
|
351
396
|
if (!SupportVersions.includes(version))
|
|
352
397
|
throw new VersionNotSupport(version);
|
|
353
398
|
if (this.loaded) return;
|
|
354
399
|
this.loaded = true;
|
|
355
400
|
|
|
356
|
-
const
|
|
357
|
-
const json = JSON.parse(file) as Record<string, any>;
|
|
401
|
+
const json = getFile(version, "packets") as Record<string, any>;
|
|
358
402
|
|
|
359
403
|
const definition = VersionDefinitions.parse(json);
|
|
360
404
|
for (const [stateKey, state] of Object.entries(definition)) {
|
package/src/world/block.ts
CHANGED
|
@@ -5,6 +5,15 @@ import { BaseVec3, Direction, Vec3 } from "../physics/direction";
|
|
|
5
5
|
import { SharedState } from "./state";
|
|
6
6
|
import { NotImplemented } from "../base/error";
|
|
7
7
|
|
|
8
|
+
export enum BlockFace {
|
|
9
|
+
Bottom = 0,
|
|
10
|
+
Top = 1,
|
|
11
|
+
North = 2,
|
|
12
|
+
South = 3,
|
|
13
|
+
West = 4,
|
|
14
|
+
East = 5,
|
|
15
|
+
}
|
|
16
|
+
|
|
8
17
|
/**
|
|
9
18
|
* Represent a state of a block
|
|
10
19
|
*/
|
|
@@ -129,13 +138,10 @@ export class BlockManager {
|
|
|
129
138
|
const sectionCache = this.state.cache.get(sectionKey);
|
|
130
139
|
if (sectionCache) {
|
|
131
140
|
const cachedState = sectionCache.at(this.getLocalPosKey(localPos));
|
|
132
|
-
if (cachedState)
|
|
133
|
-
console.log("cache hit", sectionKey, localPos);
|
|
141
|
+
if (cachedState)
|
|
134
142
|
return this.getBlock(cachedState);
|
|
135
|
-
}
|
|
136
143
|
}
|
|
137
144
|
|
|
138
|
-
console.log("cache miss", sectionKey, localPos);
|
|
139
145
|
const section = this.state.world!.chunks[`${this.state.player!.dimension}:${sx}:${sz}`]?.sections[sy]?.block;
|
|
140
146
|
if (!section) return null;
|
|
141
147
|
if (section.data === null) return BlockRegistry.getState(section.palette[0]!.toString())!;
|
|
@@ -219,11 +225,6 @@ export class BlockManager {
|
|
|
219
225
|
}
|
|
220
226
|
}
|
|
221
227
|
|
|
222
|
-
interface Context {
|
|
223
|
-
getBlockState: (position: BaseVec3) => VoxelShape,
|
|
224
|
-
from: Vec3,
|
|
225
|
-
to: Vec3
|
|
226
|
-
}
|
|
227
228
|
|
|
228
229
|
/**
|
|
229
230
|
* For clipping math
|
|
@@ -231,9 +232,9 @@ interface Context {
|
|
|
231
232
|
* @hidden
|
|
232
233
|
*/
|
|
233
234
|
export class BlockGetter {
|
|
234
|
-
public static clip(from: Vec3, to: Vec3,
|
|
235
|
+
public static clip(from: Vec3, to: Vec3, getBlockState: (position: BaseVec3) => VoxelShape) {
|
|
235
236
|
return this.traverseBlocks(
|
|
236
|
-
from, to,
|
|
237
|
+
from, to, { getBlockState, from, to },
|
|
237
238
|
(context, pos) => {
|
|
238
239
|
const blockState = context.getBlockState(pos);
|
|
239
240
|
const { from, to } = context;
|
|
@@ -247,12 +248,13 @@ export class BlockGetter {
|
|
|
247
248
|
);
|
|
248
249
|
}
|
|
249
250
|
|
|
250
|
-
public static traverseBlocks<T, C
|
|
251
|
+
public static traverseBlocks<T, C>(
|
|
251
252
|
from: Vec3,
|
|
252
253
|
to: Vec3,
|
|
253
254
|
context: C,
|
|
254
255
|
consumer: (context: C, position: BaseVec3) => T,
|
|
255
|
-
missFactory: (context: C) => T
|
|
256
|
+
missFactory: (context: C) => T
|
|
257
|
+
): T {
|
|
256
258
|
let result: T;
|
|
257
259
|
|
|
258
260
|
if (from.equal(to)) return missFactory(context);
|
|
@@ -276,9 +278,9 @@ export class BlockGetter {
|
|
|
276
278
|
const tDeltaX = signX == 0 ? Infinity : signX / dx;
|
|
277
279
|
const tDeltaY = signY == 0 ? Infinity : signY / dy;
|
|
278
280
|
const tDeltaZ = signZ == 0 ? Infinity : signZ / dz;
|
|
279
|
-
let tX = tDeltaX * (signX > 0 ? 1 - getFrac(fromX) : getFrac(fromX));
|
|
280
|
-
let tY = tDeltaY * (signY > 0 ? 1 - getFrac(fromY) : getFrac(fromY));
|
|
281
|
-
let tZ = tDeltaZ * (signZ > 0 ? 1 - getFrac(fromZ) : getFrac(fromZ));
|
|
281
|
+
let tX = signX === 0 ? Infinity : tDeltaX * (signX > 0 ? 1 - getFrac(fromX) : getFrac(fromX));
|
|
282
|
+
let tY = signY === 0 ? Infinity : tDeltaY * (signY > 0 ? 1 - getFrac(fromY) : getFrac(fromY));
|
|
283
|
+
let tZ = signZ === 0 ? Infinity : tDeltaZ * (signZ > 0 ? 1 - getFrac(fromZ) : getFrac(fromZ));
|
|
282
284
|
do {
|
|
283
285
|
if (tX <= 1.0 || tY <= 1.0 || tZ <= 1.0) {
|
|
284
286
|
if (tX < tY) {
|
|
@@ -319,7 +321,7 @@ export class BlockHitResult {
|
|
|
319
321
|
) { }
|
|
320
322
|
|
|
321
323
|
public static hit(location: Vec3, direction: Direction, pos: BaseVec3) {
|
|
322
|
-
return new BlockHitResult(
|
|
324
|
+
return new BlockHitResult(false, location, direction, pos);
|
|
323
325
|
}
|
|
324
326
|
|
|
325
327
|
public static miss(location: Vec3, direction: Direction, pos: BaseVec3) {
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { TextComponent } from "../base/typing";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Represent the data of a slot
|
|
5
|
+
*
|
|
6
|
+
* @see https://minecraft.wiki/w/Java_Edition_protocol/Slot_data#Format
|
|
7
|
+
*/
|
|
8
|
+
export interface Slot {
|
|
9
|
+
counts: number,
|
|
10
|
+
itemId: number | undefined,
|
|
11
|
+
addComponents: Record<string, any> | undefined,
|
|
12
|
+
removeComponents: number[] | undefined
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Represent a container (such as inventory, chest, anvil, ...)
|
|
17
|
+
*/
|
|
18
|
+
export class Container {
|
|
19
|
+
/**
|
|
20
|
+
* State ID
|
|
21
|
+
*
|
|
22
|
+
* You SHOULD NOT edit this field since this field is for syncing state from client and server
|
|
23
|
+
*/
|
|
24
|
+
public stateId: number = 0;
|
|
25
|
+
/**
|
|
26
|
+
* Slots data
|
|
27
|
+
*
|
|
28
|
+
* @see https://minecraft.wiki/w/Java_Edition_protocol/Inventory For more info how how slot are organized
|
|
29
|
+
*/
|
|
30
|
+
public slots: Slot[] = [];
|
|
31
|
+
/**
|
|
32
|
+
* Data from Set Container Property packet
|
|
33
|
+
*
|
|
34
|
+
* @see https://minecraft.wiki/w/Java_Edition_protocol/Packets#Set_Container_Property
|
|
35
|
+
*
|
|
36
|
+
* | Field | Meaning |
|
|
37
|
+
* |------------------------------------------|---------|
|
|
38
|
+
* | # **Furnace / Blast furnace / Smoker** | ------- |
|
|
39
|
+
* | fuel_left | counting from fuel burn time down to 0 |
|
|
40
|
+
* | max_burn_time | fuel burn time or 0 (in-game ticks) |
|
|
41
|
+
* | progress_arrow | counting from 0 to maximum progress (in-game ticks) |
|
|
42
|
+
* | max_progress | always 200 on the vanilla server |
|
|
43
|
+
* | # **Enchantment table** | ------- |
|
|
44
|
+
* | level_requirement_for_top_enchantment | |
|
|
45
|
+
* | level_requirement_for_middle_enchantment | The enchantment's xp level requirement |
|
|
46
|
+
* | level_requirement_for_bottom_enchantment | |
|
|
47
|
+
* | enchantment_seed | Used for drawing the enchantment names (in SGA) clientside. The same seed is used to calculate enchantments, but some of the data isn't sent to the client to prevent easily guessing the entire list (the seed value here is the regular seed bitwise and 0xFFFFFFF0). |
|
|
48
|
+
* | enchant_id_of_top_enchantment | |
|
|
49
|
+
* | enchant_id_of_middle_enchantment | The enchantment ID (set to -1 to hide it), see below for values |
|
|
50
|
+
* | enchant_id_of_bottom_enchantment | |
|
|
51
|
+
* | enchant_level_of_top_enchantment | |
|
|
52
|
+
* | enchant_level_of_middle_enchantment | The enchantment level (1 = I, 2 = II, 6 = VI, etc.), or -1 if no enchant |
|
|
53
|
+
* | enchant_level_of_bottom_enchantment | |
|
|
54
|
+
* | # **Beacon** | ------- |
|
|
55
|
+
* | power_level | 0-4, controls what effect buttons are enabled |
|
|
56
|
+
* | first_effect | Potion effect ID for the first effect, or -1 if no effect |
|
|
57
|
+
* | second_level | Potion effect ID for the second effect, or -1 if no effect |
|
|
58
|
+
* | # **Anvil** | ------- |
|
|
59
|
+
* | repair_cost | The repair's cost in XP levels |
|
|
60
|
+
* | # **Brewing stand** | ------- |
|
|
61
|
+
* | brew_time | 0 – 400, with 400 making the arrow empty, and 0 making the arrow full |
|
|
62
|
+
* | fuel_time | 0 - 20, with 0 making the arrow empty, and 20 making the arrow full |
|
|
63
|
+
* | # **Stonecutter** | ------- |
|
|
64
|
+
* | selected_recipe | The index of the selected recipe. -1 means none is selected |
|
|
65
|
+
* | # **Loom** | ------- |
|
|
66
|
+
* | selected_pattern | The index of the selected pattern. 0 means none is selected, 0 is also the internal ID of the "base" pattern |
|
|
67
|
+
* | # **Lectern** | ------- |
|
|
68
|
+
* | page_number | The current page number, starting from 0. |
|
|
69
|
+
* | # **Smithing table** | ------- |
|
|
70
|
+
* | has_recipe_error | True if greater than zero |
|
|
71
|
+
* | # **Crafter** | ------- |
|
|
72
|
+
* | slot_0_disabled | |
|
|
73
|
+
* | slot_1_disabled | |
|
|
74
|
+
* | slot_2_disabled | |
|
|
75
|
+
* | slot_3_disabled | |
|
|
76
|
+
* | slot_4_disabled | 0 If the slot is enabled, 1 if the slot is disabled |
|
|
77
|
+
* | slot_5_disabled | |
|
|
78
|
+
* | slot_6_disabled | |
|
|
79
|
+
* | slot_7_disabled | |
|
|
80
|
+
* | slot_8_disabled | |
|
|
81
|
+
*
|
|
82
|
+
* @see https://minecraft.wiki/w/Java_Edition_data_values#Effects For effect ID
|
|
83
|
+
*/
|
|
84
|
+
public properties: Record<string, number> = {};
|
|
85
|
+
|
|
86
|
+
constructor(
|
|
87
|
+
/**
|
|
88
|
+
* This is not used at the moment
|
|
89
|
+
*/
|
|
90
|
+
public id: number,
|
|
91
|
+
/**
|
|
92
|
+
* @see https://minecraft.wiki/w/Java_Edition_protocol/Inventory#Types
|
|
93
|
+
*/
|
|
94
|
+
public type: number,
|
|
95
|
+
public rawTitle: TextComponent
|
|
96
|
+
) { }
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* @see https://minecraft.wiki/w/Java_Edition_protocol/Packets#Set_Container_Property
|
|
100
|
+
*/
|
|
101
|
+
public setProperty(property: number, data: number) {
|
|
102
|
+
switch (this.type) {
|
|
103
|
+
case 14: // Furnace
|
|
104
|
+
case 10: // Blast furnace
|
|
105
|
+
case 22: // Smoker
|
|
106
|
+
switch (property) {
|
|
107
|
+
case 0: this.properties["fuel_left"] = data; break;
|
|
108
|
+
case 1: this.properties["max_burn_time"] = data; break;
|
|
109
|
+
case 2: this.properties["progress_arrow"] = data; break;
|
|
110
|
+
case 3: this.properties["max_progress"] = data; break;
|
|
111
|
+
}
|
|
112
|
+
break;
|
|
113
|
+
case 13: // Enchantment table
|
|
114
|
+
switch (property) {
|
|
115
|
+
case 0: this.properties["level_requirement_for_top_enchantment"] = data; break;
|
|
116
|
+
case 1: this.properties["level_requirement_for_middle_enchantment"] = data; break;
|
|
117
|
+
case 2: this.properties["level_requirement_for_bottom_enchantment"] = data; break;
|
|
118
|
+
case 3: this.properties["enchantment_seed"] = data; break;
|
|
119
|
+
case 4: this.properties["enchant_id_of_top_enchantment"] = data; break;
|
|
120
|
+
case 5: this.properties["enchant_id_of_middle_enchantment"] = data; break;
|
|
121
|
+
case 6: this.properties["enchant_id_of_bottom_enchantment"] = data; break;
|
|
122
|
+
case 7: this.properties["enchant_level_of_top_enchantment"] = data; break;
|
|
123
|
+
case 8: this.properties["enchant_level_of_middle_enchantment"] = data; break;
|
|
124
|
+
case 9: this.properties["enchant_level_of_bottom_enchantment"] = data; break;
|
|
125
|
+
}
|
|
126
|
+
break;
|
|
127
|
+
case 9: // Beacon
|
|
128
|
+
switch (property) {
|
|
129
|
+
case 0: this.properties["power_level"] = data; break;
|
|
130
|
+
case 1: this.properties["first_effect"] = data; break;
|
|
131
|
+
case 2: this.properties["second_level"] = data; break;
|
|
132
|
+
}
|
|
133
|
+
break;
|
|
134
|
+
case 8: // Anvil
|
|
135
|
+
switch (property) {
|
|
136
|
+
case 0: this.properties["repair_cost"] = data; break;
|
|
137
|
+
}
|
|
138
|
+
break;
|
|
139
|
+
case 11: // Brewing stand
|
|
140
|
+
switch (property) {
|
|
141
|
+
case 0: this.properties["brew_time"] = data; break;
|
|
142
|
+
case 1: this.properties["fuel_time"] = data; break;
|
|
143
|
+
}
|
|
144
|
+
break;
|
|
145
|
+
case 24: // Stonecutter
|
|
146
|
+
switch (property) {
|
|
147
|
+
case 0: this.properties["selected_recipe"] = data; break;
|
|
148
|
+
}
|
|
149
|
+
break;
|
|
150
|
+
case 18: // Loom
|
|
151
|
+
switch (property) {
|
|
152
|
+
case 0: this.properties["selected_pattern"] = data; break;
|
|
153
|
+
}
|
|
154
|
+
break;
|
|
155
|
+
case 17: // Lectern
|
|
156
|
+
switch (property) {
|
|
157
|
+
case 0: this.properties["page_number"] = data; break;
|
|
158
|
+
}
|
|
159
|
+
break;
|
|
160
|
+
case 21: // Smithing table
|
|
161
|
+
switch (property) {
|
|
162
|
+
case 0: this.properties["has_recipe_error"] = data; break;
|
|
163
|
+
}
|
|
164
|
+
break;
|
|
165
|
+
case 7: // Crafter
|
|
166
|
+
switch (property) {
|
|
167
|
+
case 0: this.properties["slot_0_disabled"] = data; break;
|
|
168
|
+
case 1: this.properties["slot_1_disabled"] = data; break;
|
|
169
|
+
case 2: this.properties["slot_2_disabled"] = data; break;
|
|
170
|
+
case 3: this.properties["slot_3_disabled"] = data; break;
|
|
171
|
+
case 4: this.properties["slot_4_disabled"] = data; break;
|
|
172
|
+
case 5: this.properties["slot_5_disabled"] = data; break;
|
|
173
|
+
case 6: this.properties["slot_6_disabled"] = data; break;
|
|
174
|
+
case 7: this.properties["slot_7_disabled"] = data; break;
|
|
175
|
+
case 8: this.properties["slot_8_disabled"] = data; break;
|
|
176
|
+
}
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
package/src/world/state.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Cipheriv, Decipheriv } from "node:crypto";
|
|
2
|
-
import { If } from "../base/typing";
|
|
3
2
|
import { Angle, BaseVec3 } from "../physics/direction";
|
|
4
3
|
import { AuthRelatedNotFound, ClientNotReady } from "../base/error";
|
|
5
4
|
import { ClientEvents, ClientOption } from "../client/client";
|
|
@@ -7,6 +6,7 @@ import { AuthClient } from "../client/auth";
|
|
|
7
6
|
import { MessageLink } from "../message/link";
|
|
8
7
|
import { SignatureCache } from "../message/sigCache";
|
|
9
8
|
import { CacheImplementation, LRUCache } from "../base/cache";
|
|
9
|
+
import { Container, Slot } from "./container";
|
|
10
10
|
|
|
11
11
|
export interface Server {
|
|
12
12
|
knownPacks?: ServerKnownPack
|
|
@@ -125,7 +125,10 @@ export interface ClientPlayer {
|
|
|
125
125
|
/** Current dimension */
|
|
126
126
|
dimension: string,
|
|
127
127
|
|
|
128
|
-
effects: Record<number, Effect
|
|
128
|
+
effects: Record<number, Effect>,
|
|
129
|
+
|
|
130
|
+
inventory: Container,
|
|
131
|
+
carryingItem: Slot | undefined,
|
|
129
132
|
}
|
|
130
133
|
|
|
131
134
|
export enum ClientStatus {
|
|
@@ -160,7 +163,7 @@ export type SendingPacket = {
|
|
|
160
163
|
/**
|
|
161
164
|
* For managing shared data between classes
|
|
162
165
|
*/
|
|
163
|
-
export class SharedState
|
|
166
|
+
export class SharedState {
|
|
164
167
|
private mutationQueue: MutateState[] = [];
|
|
165
168
|
private eventQueue: EmittedEvent[] = [];
|
|
166
169
|
private packetQueue: SendingPacket[] = [];
|
|
@@ -186,9 +189,10 @@ export class SharedState<IsReady extends boolean = boolean> {
|
|
|
186
189
|
|
|
187
190
|
public server: Server | undefined = undefined;
|
|
188
191
|
public registry: Record<string, ServerRegistryEntry[]> | undefined = undefined;
|
|
189
|
-
public world:
|
|
190
|
-
public player:
|
|
192
|
+
public world: ServerWorld | null = null;
|
|
193
|
+
public player: ClientPlayer | null = null;
|
|
191
194
|
public messageCount: number = 0;
|
|
195
|
+
public openingContainer: Container | undefined = undefined;
|
|
192
196
|
|
|
193
197
|
public cache: CacheImplementation<Uint16Array> | undefined = undefined;
|
|
194
198
|
|
|
@@ -211,7 +215,7 @@ export class SharedState<IsReady extends boolean = boolean> {
|
|
|
211
215
|
}
|
|
212
216
|
}
|
|
213
217
|
|
|
214
|
-
public isReady()
|
|
218
|
+
public isReady() {
|
|
215
219
|
return this.status === ClientStatus.Ready;
|
|
216
220
|
}
|
|
217
221
|
|
|
@@ -246,6 +250,7 @@ export class SharedState<IsReady extends boolean = boolean> {
|
|
|
246
250
|
this.world = undefined as any;
|
|
247
251
|
this.player = undefined as any;
|
|
248
252
|
this.messageCount = 0;
|
|
253
|
+
this.openingContainer = undefined as any;
|
|
249
254
|
|
|
250
255
|
if (this.clientOptions.shouldVerifyMessageSignature)
|
|
251
256
|
this.messageSignatureCache = SignatureCache.default();
|
package/test/client/index.ts
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
|
-
import { Input, Client } from "../../src/index";
|
|
1
|
+
import { Input, Client, ClientEvents } from "../../src/index";
|
|
2
2
|
|
|
3
3
|
const client = new Client({
|
|
4
4
|
playerName: "bot",
|
|
5
5
|
host: "localhost",
|
|
6
6
|
port: 25565,
|
|
7
7
|
version: "26.2",
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
debug: {
|
|
9
|
+
packetLogger: true,
|
|
10
|
+
ignorePackets: [
|
|
11
|
+
"move_entity_pos", "move_entity_pos_rot", "move_entity_rot",
|
|
12
|
+
"set_entity_motion", "rotate_head", "set_time",
|
|
13
|
+
"add_entity", "remove_entities", "bundle_delimiter",
|
|
14
|
+
"entity_position_sync", "update_attributes",
|
|
15
|
+
"move_player_pos", "move_player_pos_rot", "move_player_rot",
|
|
16
|
+
"player_chat", "keep_alive", "entity_event", "tab_list"
|
|
17
|
+
],
|
|
18
|
+
ignoreTCPPacketLogs: [0, 83, 54, 101, 1, 99, 53, 35, 131, 113, 102, 44, 56, 65, 34, 77, 122]
|
|
19
|
+
},
|
|
11
20
|
loadRegistry: false,
|
|
12
21
|
// auth: {
|
|
13
22
|
// client_id: "d86254d8-edf7-4640-90eb-643c99af188e",
|
|
@@ -17,23 +26,30 @@ const client = new Client({
|
|
|
17
26
|
// },
|
|
18
27
|
// shouldVerifyMessageOrder: true,
|
|
19
28
|
// shouldVerifyMessageSignature: true
|
|
20
|
-
loadAndCacheChunk:
|
|
29
|
+
loadAndCacheChunk: false
|
|
21
30
|
});
|
|
31
|
+
const forwardEventToConsole = <K extends keyof ClientEvents>(name: K) => client.on(name, ((...args: any[]) => console.dir({ name, args }, { depth: null })) as any);
|
|
22
32
|
client.connect();
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
forwardEventToConsole("disconnect");
|
|
34
|
+
forwardEventToConsole("disconnectRaw");
|
|
35
|
+
// forwardEventToConsole("loadChunk");
|
|
36
|
+
// forwardEventToConsole("unloadChunk");
|
|
37
|
+
// forwardEventToConsole("spawnEntity");
|
|
38
|
+
// forwardEventToConsole("updateEntity");
|
|
39
|
+
// forwardEventToConsole("removeEntity");
|
|
40
|
+
// forwardEventToConsole("playerPosition");
|
|
41
|
+
// forwardEventToConsole("message");
|
|
42
|
+
forwardEventToConsole("systemMessage");
|
|
43
|
+
forwardEventToConsole("systemMessageRaw");
|
|
44
|
+
forwardEventToConsole("actionBar");
|
|
45
|
+
forwardEventToConsole("actionBarRaw");
|
|
46
|
+
forwardEventToConsole("openContainer");
|
|
47
|
+
forwardEventToConsole("closeContainer");
|
|
48
|
+
forwardEventToConsole("containerContent");
|
|
49
|
+
forwardEventToConsole("containerProperty");
|
|
50
|
+
forwardEventToConsole("tablist");
|
|
51
|
+
|
|
52
|
+
client.on("message", async (message) => {
|
|
37
53
|
if (!message.content.startsWith("_")) return;
|
|
38
54
|
const args = message.content.slice(1).split(" ");
|
|
39
55
|
switch (args[0]) {
|
|
@@ -59,8 +75,20 @@ client.on("message", (message) => {
|
|
|
59
75
|
const [, x, y, z] = args.map(val => parseInt(val)) as [any, number, number, number];
|
|
60
76
|
console.time("get_block");
|
|
61
77
|
const state = client.at(x, y, z);
|
|
62
|
-
console.timeEnd("get_block");
|
|
63
|
-
|
|
78
|
+
console.timeEnd("get_block");
|
|
79
|
+
if (state) {
|
|
80
|
+
console.dir(state);
|
|
81
|
+
client.chat(state!.owner.type);
|
|
82
|
+
} else
|
|
83
|
+
client.chat("no block");
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
case "open": {
|
|
88
|
+
const [, x, y, z] = args.map(val => parseInt(val)) as [any, number, number, number];
|
|
89
|
+
const success = client.openBlock(x, y, z);
|
|
90
|
+
if (success) client.chat("opening");
|
|
91
|
+
else client.chat("failed");
|
|
64
92
|
break;
|
|
65
93
|
}
|
|
66
94
|
|