@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.
Files changed (77) hide show
  1. package/README.md +25 -9
  2. package/assets/minecraft/26.2/components.json +113 -0
  3. package/assets/minecraft/26.2/packets.json +8 -28
  4. package/assets/minecraft/26.3/packets.json +4957 -0
  5. package/assets/minecraft/versionMapping.json +16 -0
  6. package/dist/binary/decoder.d.ts +35 -0
  7. package/dist/binary/decoder.d.ts.map +1 -1
  8. package/dist/binary/decoder.js +665 -2
  9. package/dist/binary/decoder.js.map +1 -1
  10. package/dist/binary/encoder.d.ts +3 -3
  11. package/dist/binary/encoder.d.ts.map +1 -1
  12. package/dist/binary/encoder.js +4 -4
  13. package/dist/binary/encoder.js.map +1 -1
  14. package/dist/client/client.d.ts +178 -22
  15. package/dist/client/client.d.ts.map +1 -1
  16. package/dist/client/client.js +132 -13
  17. package/dist/client/client.js.map +1 -1
  18. package/dist/net/tcp.d.ts +6 -9
  19. package/dist/net/tcp.d.ts.map +1 -1
  20. package/dist/net/tcp.js +16 -19
  21. package/dist/net/tcp.js.map +1 -1
  22. package/dist/packet/listener.d.ts +6 -1
  23. package/dist/packet/listener.d.ts.map +1 -1
  24. package/dist/packet/listener.js +220 -60
  25. package/dist/packet/listener.js.map +1 -1
  26. package/dist/packet/sender.d.ts +4 -1
  27. package/dist/packet/sender.d.ts.map +1 -1
  28. package/dist/packet/sender.js +30 -4
  29. package/dist/packet/sender.js.map +1 -1
  30. package/dist/physics/aabb.d.ts.map +1 -1
  31. package/dist/physics/aabb.js +17 -9
  32. package/dist/physics/aabb.js.map +1 -1
  33. package/dist/physics/direction.js +2 -2
  34. package/dist/physics/direction.js.map +1 -1
  35. package/dist/physics/player.js +1 -5
  36. package/dist/physics/player.js.map +1 -1
  37. package/dist/version/codec.d.ts.map +1 -1
  38. package/dist/version/codec.js +63 -14
  39. package/dist/version/codec.js.map +1 -1
  40. package/dist/version/registry.d.ts +26 -5
  41. package/dist/version/registry.d.ts.map +1 -1
  42. package/dist/version/registry.js +78 -18
  43. package/dist/version/registry.js.map +1 -1
  44. package/dist/world/block.d.ts +54 -10
  45. package/dist/world/block.d.ts.map +1 -1
  46. package/dist/world/block.js +78 -32
  47. package/dist/world/block.js.map +1 -1
  48. package/dist/world/container.d.ts +106 -0
  49. package/dist/world/container.d.ts.map +1 -0
  50. package/dist/world/container.js +240 -0
  51. package/dist/world/container.js.map +1 -0
  52. package/dist/world/entity.d.ts +3 -0
  53. package/dist/world/entity.d.ts.map +1 -1
  54. package/dist/world/entity.js +3 -0
  55. package/dist/world/entity.js.map +1 -1
  56. package/dist/world/state.d.ts +11 -5
  57. package/dist/world/state.d.ts.map +1 -1
  58. package/dist/world/state.js +11 -0
  59. package/dist/world/state.js.map +1 -1
  60. package/package.json +2 -2
  61. package/src/binary/decoder.ts +691 -2
  62. package/src/binary/encoder.ts +8 -9
  63. package/src/client/client.ts +234 -34
  64. package/src/net/tcp.ts +27 -22
  65. package/src/packet/listener.ts +275 -76
  66. package/src/packet/sender.ts +42 -7
  67. package/src/physics/aabb.ts +18 -9
  68. package/src/physics/direction.ts +2 -2
  69. package/src/physics/player.ts +5 -5
  70. package/src/version/codec.ts +56 -14
  71. package/src/version/registry.ts +92 -18
  72. package/src/world/block.ts +83 -39
  73. package/src/world/container.ts +180 -0
  74. package/src/world/entity.ts +3 -0
  75. package/src/world/state.ts +21 -6
  76. package/test/client/index.ts +56 -21
  77. package/test/client/registry.json +1735 -0
@@ -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?.packetLogger)
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?.packetLogger)
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: packetId,
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": return JSON.parse(decoder.readString());
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];
@@ -1,4 +1,4 @@
1
- import { readFile } from "node:fs/promises";
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 async load(version: string) {
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 file = await readFile(join(BASE_REGISTRY_PATH, version, "entities.json"), { encoding: "utf8" });
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 async load(version: string) {
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 file = await readFile(join(BASE_REGISTRY_PATH, version, "blocks.json"), { encoding: "utf8" });
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 async load(version: string) {
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 file = await readFile(join(BASE_REGISTRY_PATH, version, "effects.json"), { encoding: "utf8" });
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(["prefixed_array", "prefixed_optional", "string", "id_or_x", "array", "switch", "fixed_point", "enum", "not_implemented", "object", "fixed_bitset", "byte_array"]),
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", "enum", "prefixed_optional"]),
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 async load(version: string) {
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 file = await readFile(join(BASE_REGISTRY_PATH, version, "packets.json"), { encoding: "utf8" });
357
- const json = JSON.parse(file) as Record<string, any>;
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(), "name or number or hex");
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
  }
@@ -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
- public getType() { return this.get("type")!; }
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
- * Somehow, this is an equivalent implementation of Minecraft `Level`
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, context: Context) {
280
+ public static clip(from: Vec3, to: Vec3, getBlockState: (position: BaseVec3) => VoxelShape) {
235
281
  return this.traverseBlocks(
236
- from, to, context,
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 = Context>(
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): T {
256
- let result: T;
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
- do {
283
- if (tX <= 1.0 || tY <= 1.0 || tZ <= 1.0) {
284
- if (tX < tY) {
285
- if (tX < tZ) {
286
- currentBlockX += signX;
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
- result = consumer(context, { x: currentBlockX, y: currentBlockY, z: currentBlockZ });
336
+ } else if (tY < tZ) {
337
+ currentBlockY += signY;
338
+ tY += tDeltaY;
300
339
  } else {
301
- return missFactory(context);
340
+ currentBlockZ += signZ;
341
+ tZ += tDeltaZ;
302
342
  }
303
- } while (result == null);
304
- return result;
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(true, location, direction, pos);
366
+ return new BlockHitResult(false, location, direction, pos);
323
367
  }
324
368
 
325
369
  public static miss(location: Vec3, direction: Direction, pos: BaseVec3) {