@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/version/registry.ts
CHANGED
|
@@ -1,18 +1,33 @@
|
|
|
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";
|
|
5
5
|
import z from "zod";
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
const BASE_REGISTRY_PATH = join(__dirname, "..", "..", "assets", "minecraft");
|
|
8
|
+
/** @hidden */
|
|
8
9
|
export const SupportVersions = [
|
|
9
|
-
"26.2"
|
|
10
|
+
"26.2",
|
|
11
|
+
"26.3"
|
|
10
12
|
];
|
|
11
13
|
|
|
14
|
+
/** @hidden */
|
|
12
15
|
export const ProtocolVersionMapping: Record<string, number> = {
|
|
13
|
-
"26.2": 776
|
|
16
|
+
"26.2": 776,
|
|
17
|
+
"26.3": 777
|
|
14
18
|
};
|
|
15
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
|
+
|
|
16
31
|
export class EntityRegistry {
|
|
17
32
|
private static loaded: boolean = false;
|
|
18
33
|
public static readonly data: Record<string, { height: number, width: number, type: number }> = {};
|
|
@@ -23,14 +38,13 @@ export class EntityRegistry {
|
|
|
23
38
|
*
|
|
24
39
|
* Should be called once time
|
|
25
40
|
*/
|
|
26
|
-
public static
|
|
41
|
+
public static load(version: string) {
|
|
27
42
|
if (!SupportVersions.includes(version))
|
|
28
43
|
throw new VersionNotSupport(version);
|
|
29
44
|
if (this.loaded) return;
|
|
30
45
|
this.loaded = true;
|
|
31
46
|
|
|
32
|
-
const
|
|
33
|
-
const json = JSON.parse(file);
|
|
47
|
+
const json = getFile(version, "entities");
|
|
34
48
|
for (const entity in json) {
|
|
35
49
|
this.data[entity] = json[entity];
|
|
36
50
|
this.mapTypeToData[json[entity]["id"]] = entity;
|
|
@@ -66,14 +80,13 @@ export class BlockRegistry {
|
|
|
66
80
|
*
|
|
67
81
|
* Should be called once time
|
|
68
82
|
*/
|
|
69
|
-
public static
|
|
83
|
+
public static load(version: string) {
|
|
70
84
|
if (!SupportVersions.includes(version))
|
|
71
85
|
throw new VersionNotSupport(version);
|
|
72
86
|
if (this.loaded) return;
|
|
73
87
|
this.loaded = true;
|
|
74
88
|
|
|
75
|
-
const
|
|
76
|
-
const json = JSON.parse(file) as Record<string, any>;
|
|
89
|
+
const json = getFile(version, "blocks") as Record<string, any>;
|
|
77
90
|
for (const [type, blockRaw] of Object.entries(json)) {
|
|
78
91
|
const block = new Block(type, blockRaw['definition'], blockRaw['properties'], blockRaw['states']);
|
|
79
92
|
this.blocks[type] = block;
|
|
@@ -111,20 +124,45 @@ export class EffectRegistry {
|
|
|
111
124
|
*
|
|
112
125
|
* Should be called once time
|
|
113
126
|
*/
|
|
114
|
-
public static
|
|
127
|
+
public static load(version: string) {
|
|
115
128
|
if (!SupportVersions.includes(version))
|
|
116
129
|
throw new VersionNotSupport(version);
|
|
117
130
|
if (this.loaded) return;
|
|
118
131
|
this.loaded = true;
|
|
119
132
|
|
|
120
|
-
const
|
|
121
|
-
const json = JSON.parse(file) as Record<string, any>;
|
|
133
|
+
const json = getFile(version, "effects") as Record<string, any>;
|
|
122
134
|
for (const [name, id] of Object.entries(json)) {
|
|
123
135
|
this.effects[name] = id;
|
|
124
136
|
}
|
|
125
137
|
}
|
|
126
138
|
}
|
|
127
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
|
+
|
|
165
|
+
/** @hidden */
|
|
128
166
|
export const SupportTypes = z.enum([
|
|
129
167
|
"byte",
|
|
130
168
|
"unsigned_byte",
|
|
@@ -163,13 +201,16 @@ export const SupportTypes = z.enum([
|
|
|
163
201
|
"bitset",
|
|
164
202
|
"fixed_bitset",
|
|
165
203
|
"byte_array",
|
|
204
|
+
"slot",
|
|
166
205
|
"null", // a constant
|
|
206
|
+
"special",
|
|
167
207
|
|
|
168
208
|
/**
|
|
169
209
|
* This is used for handle field that its type depends on previous field type, or value
|
|
170
210
|
*/
|
|
171
211
|
"switch"
|
|
172
212
|
]);
|
|
213
|
+
/** @hidden */
|
|
173
214
|
export type FieldNode = ({
|
|
174
215
|
type: Exclude<z.infer<typeof SupportTypes>,
|
|
175
216
|
| "prefixed_array"
|
|
@@ -184,6 +225,7 @@ export type FieldNode = ({
|
|
|
184
225
|
| "not_implemented"
|
|
185
226
|
| "fixed_bitset"
|
|
186
227
|
| "byte_array"
|
|
228
|
+
| "special"
|
|
187
229
|
>,
|
|
188
230
|
} | {
|
|
189
231
|
type: "not_implemented",
|
|
@@ -217,12 +259,16 @@ export type FieldNode = ({
|
|
|
217
259
|
} | {
|
|
218
260
|
type: "fixed_bitset" | "byte_array",
|
|
219
261
|
length: number,
|
|
262
|
+
} | {
|
|
263
|
+
type: "special",
|
|
264
|
+
comment: string,
|
|
220
265
|
}) & {
|
|
221
266
|
skip_able?: boolean | undefined
|
|
222
267
|
}
|
|
268
|
+
/** @hidden */
|
|
223
269
|
export const Field: z.ZodType<FieldNode> = z.union([
|
|
224
270
|
z.object({
|
|
225
|
-
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"]),
|
|
226
272
|
}),
|
|
227
273
|
z.object({
|
|
228
274
|
type: z.literal(["not_implemented"]),
|
|
@@ -296,24 +342,32 @@ export const Field: z.ZodType<FieldNode> = z.union([
|
|
|
296
342
|
// These type require predefined-length
|
|
297
343
|
type: z.union([z.literal("fixed_bitset"), z.literal("byte_array")]),
|
|
298
344
|
length: z.int()
|
|
299
|
-
})
|
|
345
|
+
}),
|
|
346
|
+
z.object({
|
|
347
|
+
type: z.literal(["special"]),
|
|
348
|
+
comment: z.string()
|
|
349
|
+
}),
|
|
300
350
|
])
|
|
301
351
|
.and(
|
|
302
352
|
z.object({
|
|
303
353
|
skip_able: z.boolean().optional()
|
|
304
354
|
})
|
|
305
355
|
);
|
|
356
|
+
/** @hidden */
|
|
306
357
|
export const PacketObject = z.object({
|
|
307
358
|
name: z.string(),
|
|
308
359
|
id: z.number(),
|
|
309
360
|
structure: z.record(z.string(), Field),
|
|
310
361
|
skipForNow: z.boolean().default(false)
|
|
311
362
|
});
|
|
363
|
+
/** @hidden */
|
|
312
364
|
export const PacketsDefinition = z.record(z.string().or(z.number()), PacketObject);
|
|
365
|
+
/** @hidden */
|
|
313
366
|
export const StatesDefinition = z.object({
|
|
314
367
|
clientbound: PacketsDefinition,
|
|
315
368
|
serverbound: PacketsDefinition,
|
|
316
369
|
});
|
|
370
|
+
/** @hidden */
|
|
317
371
|
export const VersionDefinitions = z.object({
|
|
318
372
|
handshaking: StatesDefinition,
|
|
319
373
|
status: StatesDefinition,
|
|
@@ -338,14 +392,13 @@ export class PacketRegistry {
|
|
|
338
392
|
*
|
|
339
393
|
* Should be called once time
|
|
340
394
|
*/
|
|
341
|
-
public static
|
|
395
|
+
public static load(version: string) {
|
|
342
396
|
if (!SupportVersions.includes(version))
|
|
343
397
|
throw new VersionNotSupport(version);
|
|
344
398
|
if (this.loaded) return;
|
|
345
399
|
this.loaded = true;
|
|
346
400
|
|
|
347
|
-
const
|
|
348
|
-
const json = JSON.parse(file) as Record<string, any>;
|
|
401
|
+
const json = getFile(version, "packets") as Record<string, any>;
|
|
349
402
|
|
|
350
403
|
const definition = VersionDefinitions.parse(json);
|
|
351
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 {
|
|
@@ -157,7 +160,10 @@ export type SendingPacket = {
|
|
|
157
160
|
data: Record<string, any>
|
|
158
161
|
}
|
|
159
162
|
|
|
160
|
-
|
|
163
|
+
/**
|
|
164
|
+
* For managing shared data between classes
|
|
165
|
+
*/
|
|
166
|
+
export class SharedState {
|
|
161
167
|
private mutationQueue: MutateState[] = [];
|
|
162
168
|
private eventQueue: EmittedEvent[] = [];
|
|
163
169
|
private packetQueue: SendingPacket[] = [];
|
|
@@ -183,9 +189,10 @@ export class SharedState<IsReady extends boolean = boolean> {
|
|
|
183
189
|
|
|
184
190
|
public server: Server | undefined = undefined;
|
|
185
191
|
public registry: Record<string, ServerRegistryEntry[]> | undefined = undefined;
|
|
186
|
-
public world:
|
|
187
|
-
public player:
|
|
192
|
+
public world: ServerWorld | null = null;
|
|
193
|
+
public player: ClientPlayer | null = null;
|
|
188
194
|
public messageCount: number = 0;
|
|
195
|
+
public openingContainer: Container | undefined = undefined;
|
|
189
196
|
|
|
190
197
|
public cache: CacheImplementation<Uint16Array> | undefined = undefined;
|
|
191
198
|
|
|
@@ -208,7 +215,7 @@ export class SharedState<IsReady extends boolean = boolean> {
|
|
|
208
215
|
}
|
|
209
216
|
}
|
|
210
217
|
|
|
211
|
-
public isReady()
|
|
218
|
+
public isReady() {
|
|
212
219
|
return this.status === ClientStatus.Ready;
|
|
213
220
|
}
|
|
214
221
|
|
|
@@ -243,6 +250,7 @@ export class SharedState<IsReady extends boolean = boolean> {
|
|
|
243
250
|
this.world = undefined as any;
|
|
244
251
|
this.player = undefined as any;
|
|
245
252
|
this.messageCount = 0;
|
|
253
|
+
this.openingContainer = undefined as any;
|
|
246
254
|
|
|
247
255
|
if (this.clientOptions.shouldVerifyMessageSignature)
|
|
248
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
|
|