@vaitosoi/minejs 0.5.1 → 0.6.1
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 +25 -9
- package/assets/minecraft/26.2/components.json +113 -0
- package/assets/minecraft/26.2/packets.json +8 -28
- package/assets/minecraft/26.3/packets.json +4957 -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 +4 -4
- package/dist/binary/encoder.js.map +1 -1
- package/dist/client/client.d.ts +178 -22
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js +132 -13
- package/dist/client/client.js.map +1 -1
- package/dist/net/tcp.d.ts +6 -9
- package/dist/net/tcp.d.ts.map +1 -1
- package/dist/net/tcp.js +16 -19
- package/dist/net/tcp.js.map +1 -1
- package/dist/packet/listener.d.ts +6 -1
- package/dist/packet/listener.d.ts.map +1 -1
- package/dist/packet/listener.js +220 -60
- package/dist/packet/listener.js.map +1 -1
- package/dist/packet/sender.d.ts +4 -1
- package/dist/packet/sender.d.ts.map +1 -1
- package/dist/packet/sender.js +30 -4
- package/dist/packet/sender.js.map +1 -1
- package/dist/physics/aabb.d.ts.map +1 -1
- package/dist/physics/aabb.js +17 -9
- package/dist/physics/aabb.js.map +1 -1
- package/dist/physics/direction.js +2 -2
- package/dist/physics/direction.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 +63 -14
- package/dist/version/codec.js.map +1 -1
- package/dist/version/registry.d.ts +26 -5
- package/dist/version/registry.d.ts.map +1 -1
- package/dist/version/registry.js +78 -18
- package/dist/version/registry.js.map +1 -1
- package/dist/world/block.d.ts +54 -10
- package/dist/world/block.d.ts.map +1 -1
- package/dist/world/block.js +78 -32
- 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/entity.d.ts +3 -0
- package/dist/world/entity.d.ts.map +1 -1
- package/dist/world/entity.js +3 -0
- package/dist/world/entity.js.map +1 -1
- package/dist/world/state.d.ts +11 -5
- package/dist/world/state.d.ts.map +1 -1
- package/dist/world/state.js +11 -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 +8 -9
- package/src/client/client.ts +234 -34
- package/src/net/tcp.ts +27 -22
- package/src/packet/listener.ts +275 -76
- package/src/packet/sender.ts +42 -7
- package/src/physics/aabb.ts +18 -9
- package/src/physics/direction.ts +2 -2
- package/src/physics/player.ts +5 -5
- package/src/version/codec.ts +56 -14
- package/src/version/registry.ts +92 -18
- package/src/world/block.ts +83 -39
- package/src/world/container.ts +180 -0
- package/src/world/entity.ts +3 -0
- package/src/world/state.ts +21 -6
- package/test/client/index.ts +56 -21
- package/test/client/registry.json +1735 -0
package/src/version/codec.ts
CHANGED
|
@@ -49,11 +49,14 @@ export class VersionCodec {
|
|
|
49
49
|
if (err instanceof NotImplemented) return;
|
|
50
50
|
throw err;
|
|
51
51
|
}
|
|
52
|
-
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)))
|
|
53
56
|
console.dir({
|
|
54
57
|
dir: "to client",
|
|
55
58
|
state: mappedState,
|
|
56
|
-
name,
|
|
59
|
+
name: packet.name,
|
|
57
60
|
data: resolvedObject,
|
|
58
61
|
});
|
|
59
62
|
|
|
@@ -66,11 +69,15 @@ export class VersionCodec {
|
|
|
66
69
|
const packet = PacketRegistry.getPacket(mappedState, "serverbound", packetId);
|
|
67
70
|
if (!packet) throw new UnexpectedValue("valid serverbound packet id", packetId.toString());
|
|
68
71
|
|
|
69
|
-
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)))
|
|
70
76
|
console.dir({
|
|
71
77
|
dir: "to server",
|
|
72
78
|
state: mappedState,
|
|
73
|
-
id:
|
|
79
|
+
id: packet.id,
|
|
80
|
+
name: packet.name,
|
|
74
81
|
data,
|
|
75
82
|
});
|
|
76
83
|
|
|
@@ -105,6 +112,21 @@ export class VersionCodec {
|
|
|
105
112
|
field: FieldNode,
|
|
106
113
|
decoder: BinaryDecoder
|
|
107
114
|
): any {
|
|
115
|
+
// if (
|
|
116
|
+
// this.state.clientOptions.debug &&
|
|
117
|
+
// this.state.clientOptions.debug.packetLogger &&
|
|
118
|
+
// (
|
|
119
|
+
// (Array.isArray(this.state.clientOptions.debug.ignoreTCPPacketLogs) && !this.state.clientOptions.debug.ignoreTCPPacketLogs.includes(packetId)) ||
|
|
120
|
+
// (!this.state.clientOptions.debug.ignoreTCPPacketLogs)
|
|
121
|
+
// )
|
|
122
|
+
// )
|
|
123
|
+
// console.dir({
|
|
124
|
+
// packetId,
|
|
125
|
+
// readObject,
|
|
126
|
+
// fieldName,
|
|
127
|
+
// field,
|
|
128
|
+
// decoder
|
|
129
|
+
// }, { depth: null });
|
|
108
130
|
switch (field.type) {
|
|
109
131
|
case "string": return decoder.readString();
|
|
110
132
|
case "boolean": return decoder.readBoolean();
|
|
@@ -144,7 +166,17 @@ export class VersionCodec {
|
|
|
144
166
|
case "chat_type_decoration": return decoder.readChatTypeDecoration();
|
|
145
167
|
case "chat_type": return decoder.readChatType();
|
|
146
168
|
case "enum": return this.readField(packetId, readObject, fieldName, field.subType, decoder);
|
|
147
|
-
case "json_text":
|
|
169
|
+
case "json_text": {
|
|
170
|
+
const str = decoder.readString();
|
|
171
|
+
try {
|
|
172
|
+
return JSON.parse(str);
|
|
173
|
+
} catch (err) {
|
|
174
|
+
if (err instanceof SyntaxError)
|
|
175
|
+
return str;
|
|
176
|
+
else
|
|
177
|
+
throw err;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
148
180
|
case "object": {
|
|
149
181
|
const obj: Record<string, any> = {};
|
|
150
182
|
for (const [key, type] of Object.entries(field.fields))
|
|
@@ -152,15 +184,7 @@ export class VersionCodec {
|
|
|
152
184
|
return obj;
|
|
153
185
|
}
|
|
154
186
|
case "game_profile": {
|
|
155
|
-
return
|
|
156
|
-
uuid: decoder.readUUID(),
|
|
157
|
-
username: decoder.readString(),
|
|
158
|
-
pproperties: decoder.readPrefixedArray((decoder) => ({
|
|
159
|
-
name: decoder.readString(),
|
|
160
|
-
value: decoder.readString(),
|
|
161
|
-
signature: decoder.readPrefixedOptional((decoder) => decoder.readString())
|
|
162
|
-
}))
|
|
163
|
-
};
|
|
187
|
+
return decoder.readProfile();
|
|
164
188
|
}
|
|
165
189
|
case "heightmap": {
|
|
166
190
|
return {
|
|
@@ -170,12 +194,30 @@ export class VersionCodec {
|
|
|
170
194
|
}
|
|
171
195
|
case "bitset": return decoder.readPrefixedArray((decoder) => decoder.readLong());
|
|
172
196
|
case "teleport_flags": return decoder.readTeleportFlag();
|
|
197
|
+
case "slot": return decoder.readSlot();
|
|
173
198
|
case "null": return null;
|
|
174
199
|
case "not_implemented": {
|
|
175
200
|
if (field.skip_able === true)
|
|
176
201
|
throw new SkippableNotImplemented();
|
|
177
202
|
throw new NotImplemented();
|
|
178
203
|
}
|
|
204
|
+
case "infer": {
|
|
205
|
+
const args = field.args?.map(val =>
|
|
206
|
+
typeof val === "number"
|
|
207
|
+
? val
|
|
208
|
+
: val.startsWith("literal:")
|
|
209
|
+
? val.slice(8)
|
|
210
|
+
: readObject[val]
|
|
211
|
+
) || [];
|
|
212
|
+
let func: (...args: any[]) => any = () => undefined;
|
|
213
|
+
switch (field.function) {
|
|
214
|
+
case "bit_and": func = (a: number, b: number) => a & b; break;
|
|
215
|
+
case "bit_or": func = (a: number, b: number) => a | b; break;
|
|
216
|
+
case "shift_left": func = (val: number, pos: number) => val << pos; break;
|
|
217
|
+
case "shift_right": func = (val: number, pos: number) => val >>> pos; break;
|
|
218
|
+
}
|
|
219
|
+
return func(...args);
|
|
220
|
+
}
|
|
179
221
|
case "switch": {
|
|
180
222
|
if (!(field.dependsOn in readObject)) throw new MissingPacketField(packetId, fieldName, field.dependsOn);
|
|
181
223
|
const dependencyValue = readObject[field.dependsOn];
|
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,10 @@ 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",
|
|
207
|
+
"infer",
|
|
170
208
|
|
|
171
209
|
/**
|
|
172
210
|
* This is used for handle field that its type depends on previous field type, or value
|
|
@@ -188,6 +226,8 @@ export type FieldNode = ({
|
|
|
188
226
|
| "not_implemented"
|
|
189
227
|
| "fixed_bitset"
|
|
190
228
|
| "byte_array"
|
|
229
|
+
| "special"
|
|
230
|
+
| "infer"
|
|
191
231
|
>,
|
|
192
232
|
} | {
|
|
193
233
|
type: "not_implemented",
|
|
@@ -221,13 +261,35 @@ export type FieldNode = ({
|
|
|
221
261
|
} | {
|
|
222
262
|
type: "fixed_bitset" | "byte_array",
|
|
223
263
|
length: number,
|
|
264
|
+
} | {
|
|
265
|
+
type: "special",
|
|
266
|
+
comment: string,
|
|
267
|
+
} | { // Call a function
|
|
268
|
+
type: "infer",
|
|
269
|
+
args?: (string | number)[] | undefined,
|
|
270
|
+
function: string
|
|
224
271
|
}) & {
|
|
225
272
|
skip_able?: boolean | undefined
|
|
226
273
|
}
|
|
227
274
|
/** @hidden */
|
|
228
275
|
export const Field: z.ZodType<FieldNode> = z.union([
|
|
229
276
|
z.object({
|
|
230
|
-
type: SupportTypes.exclude([
|
|
277
|
+
type: SupportTypes.exclude([
|
|
278
|
+
"prefixed_array",
|
|
279
|
+
"prefixed_optional",
|
|
280
|
+
"string",
|
|
281
|
+
"id_or_x",
|
|
282
|
+
"array",
|
|
283
|
+
"switch",
|
|
284
|
+
"fixed_point",
|
|
285
|
+
"enum",
|
|
286
|
+
"not_implemented",
|
|
287
|
+
"object",
|
|
288
|
+
"fixed_bitset",
|
|
289
|
+
"byte_array",
|
|
290
|
+
"special",
|
|
291
|
+
"infer"
|
|
292
|
+
]),
|
|
231
293
|
}),
|
|
232
294
|
z.object({
|
|
233
295
|
type: z.literal(["not_implemented"]),
|
|
@@ -249,7 +311,9 @@ export const Field: z.ZodType<FieldNode> = z.union([
|
|
|
249
311
|
length: z.number().optional()
|
|
250
312
|
}),
|
|
251
313
|
z.object({
|
|
252
|
-
type: z.literal(["id_or_x",
|
|
314
|
+
type: z.literal(["id_or_x",
|
|
315
|
+
"enum",
|
|
316
|
+
"prefixed_optional"]),
|
|
253
317
|
/**
|
|
254
318
|
* For case like `Array<subType>` or `PrefixedOptional<subType>`
|
|
255
319
|
*/
|
|
@@ -301,6 +365,15 @@ export const Field: z.ZodType<FieldNode> = z.union([
|
|
|
301
365
|
// These type require predefined-length
|
|
302
366
|
type: z.union([z.literal("fixed_bitset"), z.literal("byte_array")]),
|
|
303
367
|
length: z.int()
|
|
368
|
+
}),
|
|
369
|
+
z.object({
|
|
370
|
+
type: z.literal("special"),
|
|
371
|
+
comment: z.string()
|
|
372
|
+
}),
|
|
373
|
+
z.object({
|
|
374
|
+
type: z.literal("infer"),
|
|
375
|
+
args: z.string().or(z.number()).array().optional(),
|
|
376
|
+
function: z.string(),
|
|
304
377
|
})
|
|
305
378
|
])
|
|
306
379
|
.and(
|
|
@@ -347,14 +420,14 @@ export class PacketRegistry {
|
|
|
347
420
|
*
|
|
348
421
|
* Should be called once time
|
|
349
422
|
*/
|
|
350
|
-
public static
|
|
423
|
+
public static load(version: string) {
|
|
351
424
|
if (!SupportVersions.includes(version))
|
|
352
425
|
throw new VersionNotSupport(version);
|
|
353
426
|
if (this.loaded) return;
|
|
354
427
|
this.loaded = true;
|
|
355
428
|
|
|
356
|
-
const
|
|
357
|
-
|
|
429
|
+
const json = getFile(version,
|
|
430
|
+
"packets") as Record<string, any>;
|
|
358
431
|
|
|
359
432
|
const definition = VersionDefinitions.parse(json);
|
|
360
433
|
for (const [stateKey, state] of Object.entries(definition)) {
|
|
@@ -383,7 +456,8 @@ export class PacketRegistry {
|
|
|
383
456
|
numbericId = parseInt(id, 16);
|
|
384
457
|
else numbericId = id;
|
|
385
458
|
if (isNaN(numbericId))
|
|
386
|
-
throw new UnexpectedValue("valid packet id", id.toString(),
|
|
459
|
+
throw new UnexpectedValue("valid packet id", id.toString(),
|
|
460
|
+
"name or number or hex");
|
|
387
461
|
else
|
|
388
462
|
return this.definition[state][type]["0x" + numbericId.toString(16).toUpperCase()];
|
|
389
463
|
}
|
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
|
*/
|
|
@@ -35,6 +44,10 @@ export class BlockState {
|
|
|
35
44
|
if (defaultVal) return defaultVal;
|
|
36
45
|
return null;
|
|
37
46
|
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Get the block facing
|
|
50
|
+
*/
|
|
38
51
|
public getFacing(): "north" | "south" | "west" | "east" | null { return this.get("facing"); }
|
|
39
52
|
}
|
|
40
53
|
|
|
@@ -42,6 +55,9 @@ export class BlockState {
|
|
|
42
55
|
* Represent a block
|
|
43
56
|
*/
|
|
44
57
|
export class Block {
|
|
58
|
+
/**
|
|
59
|
+
* A block will have multiple states (example: a stair will have difference facing).
|
|
60
|
+
*/
|
|
45
61
|
public states: BlockState[];
|
|
46
62
|
|
|
47
63
|
constructor(
|
|
@@ -65,16 +81,46 @@ export class Block {
|
|
|
65
81
|
if (defaultVal) return defaultVal;
|
|
66
82
|
return null;
|
|
67
83
|
}
|
|
68
|
-
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Get the block type.
|
|
87
|
+
*
|
|
88
|
+
* A block type will contain multiple blocks, like type `wall_sign` will have
|
|
89
|
+
* `oak_wall_sign`, `spruce_wall_sign`, `acacia_wall_sign`, etc...
|
|
90
|
+
*/
|
|
91
|
+
public getType() { return this.get<string>("type")!; }
|
|
92
|
+
/**
|
|
93
|
+
* Get the block friction.
|
|
94
|
+
*
|
|
95
|
+
* By default, block friction is 0.6, except for ice block family.
|
|
96
|
+
*/
|
|
69
97
|
public getFriction() { return this.get("friction", 0.6); }
|
|
98
|
+
/**
|
|
99
|
+
* Get the block bounciness.
|
|
100
|
+
*
|
|
101
|
+
* By default, block friction is 0, except for slime block.
|
|
102
|
+
*/
|
|
70
103
|
public getBounciness() { return this.get("bounciness", 0); }
|
|
104
|
+
/**
|
|
105
|
+
* Get the block speed factor.
|
|
106
|
+
*
|
|
107
|
+
* By default, block friction is 1, except for soul sand block.
|
|
108
|
+
*/
|
|
71
109
|
public getSpeedFactor() { return this.get("speed_factor", 1); }
|
|
110
|
+
/**
|
|
111
|
+
* Check if the block is climable.
|
|
112
|
+
*
|
|
113
|
+
* By default, block friction is false, except for ladder and vine.
|
|
114
|
+
*/
|
|
72
115
|
public getClimbable() { return this.get("climbable", false); }
|
|
116
|
+
/**
|
|
117
|
+
* Get the block jump factor.
|
|
118
|
+
*/
|
|
73
119
|
public getJumpFactor() { return this.get("jump_factor", 1); }
|
|
74
120
|
}
|
|
75
121
|
|
|
76
122
|
/**
|
|
77
|
-
*
|
|
123
|
+
* This class is for fetching blocks and chunks data.
|
|
78
124
|
*/
|
|
79
125
|
export class BlockManager {
|
|
80
126
|
constructor(private state: SharedState) { }
|
|
@@ -106,7 +152,9 @@ export class BlockManager {
|
|
|
106
152
|
this.state.checkReady();
|
|
107
153
|
let { x, y, z } = Vec3.loadArgs(a, b, c);
|
|
108
154
|
x = Math.floor(x); y = Math.floor(y); z = Math.floor(z);
|
|
155
|
+
// Chunk position
|
|
109
156
|
const sx = Math.floor(x / 16), sy = Math.floor((y + 64) / 16), sz = Math.floor(z / 16);
|
|
157
|
+
// Block position within the chunk
|
|
110
158
|
const px = ((x % 16) + 16) % 16, py = ((y % 16) + 16) % 16, pz = ((z % 16) + 16) % 16;
|
|
111
159
|
|
|
112
160
|
if (this.state.cache)
|
|
@@ -129,13 +177,10 @@ export class BlockManager {
|
|
|
129
177
|
const sectionCache = this.state.cache.get(sectionKey);
|
|
130
178
|
if (sectionCache) {
|
|
131
179
|
const cachedState = sectionCache.at(this.getLocalPosKey(localPos));
|
|
132
|
-
if (cachedState)
|
|
133
|
-
console.log("cache hit", sectionKey, localPos);
|
|
180
|
+
if (cachedState)
|
|
134
181
|
return this.getBlock(cachedState);
|
|
135
|
-
}
|
|
136
182
|
}
|
|
137
183
|
|
|
138
|
-
console.log("cache miss", sectionKey, localPos);
|
|
139
184
|
const section = this.state.world!.chunks[`${this.state.player!.dimension}:${sx}:${sz}`]?.sections[sy]?.block;
|
|
140
185
|
if (!section) return null;
|
|
141
186
|
if (section.data === null) return BlockRegistry.getState(section.palette[0]!.toString())!;
|
|
@@ -160,6 +205,13 @@ export class BlockManager {
|
|
|
160
205
|
const state = arr.at(this.getLocalPosKey(localPos))!;
|
|
161
206
|
return this.getBlock(state);
|
|
162
207
|
}
|
|
208
|
+
/**
|
|
209
|
+
* Delete the chunk cache
|
|
210
|
+
*
|
|
211
|
+
* @param sx chunk x
|
|
212
|
+
* @param sy section y
|
|
213
|
+
* @param sz chunk z
|
|
214
|
+
*/
|
|
163
215
|
public deleteChunkCache(
|
|
164
216
|
sx: number, sy: number, sz: number,
|
|
165
217
|
) {
|
|
@@ -187,7 +239,6 @@ export class BlockManager {
|
|
|
187
239
|
const stateId = bitsPerEntry <= 8 ? section.palette[localId]! : localId;
|
|
188
240
|
|
|
189
241
|
return BlockRegistry.getState(String(stateId));
|
|
190
|
-
|
|
191
242
|
}
|
|
192
243
|
private getLocalPosKey(pos: BaseVec3) {
|
|
193
244
|
return pos.x + 16 * (pos.y + 16 * pos.z);
|
|
@@ -219,11 +270,6 @@ export class BlockManager {
|
|
|
219
270
|
}
|
|
220
271
|
}
|
|
221
272
|
|
|
222
|
-
interface Context {
|
|
223
|
-
getBlockState: (position: BaseVec3) => VoxelShape,
|
|
224
|
-
from: Vec3,
|
|
225
|
-
to: Vec3
|
|
226
|
-
}
|
|
227
273
|
|
|
228
274
|
/**
|
|
229
275
|
* For clipping math
|
|
@@ -231,9 +277,9 @@ interface Context {
|
|
|
231
277
|
* @hidden
|
|
232
278
|
*/
|
|
233
279
|
export class BlockGetter {
|
|
234
|
-
public static clip(from: Vec3, to: Vec3,
|
|
280
|
+
public static clip(from: Vec3, to: Vec3, getBlockState: (position: BaseVec3) => VoxelShape) {
|
|
235
281
|
return this.traverseBlocks(
|
|
236
|
-
from, to,
|
|
282
|
+
from, to, { getBlockState, from, to },
|
|
237
283
|
(context, pos) => {
|
|
238
284
|
const blockState = context.getBlockState(pos);
|
|
239
285
|
const { from, to } = context;
|
|
@@ -247,14 +293,13 @@ export class BlockGetter {
|
|
|
247
293
|
);
|
|
248
294
|
}
|
|
249
295
|
|
|
250
|
-
public static traverseBlocks<T, C
|
|
296
|
+
public static traverseBlocks<T, C>(
|
|
251
297
|
from: Vec3,
|
|
252
298
|
to: Vec3,
|
|
253
299
|
context: C,
|
|
254
300
|
consumer: (context: C, position: BaseVec3) => T,
|
|
255
|
-
missFactory: (context: C) => T
|
|
256
|
-
|
|
257
|
-
|
|
301
|
+
missFactory: (context: C) => T
|
|
302
|
+
): T {
|
|
258
303
|
if (from.equal(to)) return missFactory(context);
|
|
259
304
|
const toX = lerp(-Epsilon, to.x, from.x);
|
|
260
305
|
const toY = lerp(-Epsilon, to.y, from.y);
|
|
@@ -276,32 +321,31 @@ export class BlockGetter {
|
|
|
276
321
|
const tDeltaX = signX == 0 ? Infinity : signX / dx;
|
|
277
322
|
const tDeltaY = signY == 0 ? Infinity : signY / dy;
|
|
278
323
|
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));
|
|
282
|
-
|
|
283
|
-
if (tX
|
|
284
|
-
if (tX <
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
tX += tDeltaX;
|
|
288
|
-
} else {
|
|
289
|
-
currentBlockZ += signZ;
|
|
290
|
-
tZ += tDeltaZ;
|
|
291
|
-
}
|
|
292
|
-
} else if (tY < tZ) {
|
|
293
|
-
currentBlockY += signY;
|
|
294
|
-
tY += tDeltaY;
|
|
324
|
+
let tX = signX === 0 ? Infinity : tDeltaX * (signX > 0 ? 1 - getFrac(fromX) : getFrac(fromX));
|
|
325
|
+
let tY = signY === 0 ? Infinity : tDeltaY * (signY > 0 ? 1 - getFrac(fromY) : getFrac(fromY));
|
|
326
|
+
let tZ = signZ === 0 ? Infinity : tDeltaZ * (signZ > 0 ? 1 - getFrac(fromZ) : getFrac(fromZ));
|
|
327
|
+
while (tX <= 1.0 || tY <= 1.0 || tZ <= 1.0) {
|
|
328
|
+
if (tX < tY) {
|
|
329
|
+
if (tX < tZ) {
|
|
330
|
+
currentBlockX += signX;
|
|
331
|
+
tX += tDeltaX;
|
|
295
332
|
} else {
|
|
296
333
|
currentBlockZ += signZ;
|
|
297
334
|
tZ += tDeltaZ;
|
|
298
335
|
}
|
|
299
|
-
|
|
336
|
+
} else if (tY < tZ) {
|
|
337
|
+
currentBlockY += signY;
|
|
338
|
+
tY += tDeltaY;
|
|
300
339
|
} else {
|
|
301
|
-
|
|
340
|
+
currentBlockZ += signZ;
|
|
341
|
+
tZ += tDeltaZ;
|
|
302
342
|
}
|
|
303
|
-
|
|
304
|
-
|
|
343
|
+
const result = consumer(context, { x: currentBlockX, y: currentBlockY, z: currentBlockZ });
|
|
344
|
+
|
|
345
|
+
if (result !== null)
|
|
346
|
+
return result;
|
|
347
|
+
}
|
|
348
|
+
return missFactory(context);
|
|
305
349
|
}
|
|
306
350
|
}
|
|
307
351
|
|
|
@@ -319,7 +363,7 @@ export class BlockHitResult {
|
|
|
319
363
|
) { }
|
|
320
364
|
|
|
321
365
|
public static hit(location: Vec3, direction: Direction, pos: BaseVec3) {
|
|
322
|
-
return new BlockHitResult(
|
|
366
|
+
return new BlockHitResult(false, location, direction, pos);
|
|
323
367
|
}
|
|
324
368
|
|
|
325
369
|
public static miss(location: Vec3, direction: Direction, pos: BaseVec3) {
|