@vaitosoi/minejs 0.4.3 → 0.5.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.
Files changed (105) hide show
  1. package/README.md +12 -4
  2. package/assets/minecraft/26.2/effects.json +42 -0
  3. package/assets/minecraft/26.2/packets.json +1 -1
  4. package/dist/base/bitset.d.ts +43 -4
  5. package/dist/base/bitset.d.ts.map +1 -1
  6. package/dist/base/bitset.js +251 -30
  7. package/dist/base/bitset.js.map +1 -1
  8. package/dist/base/cache.d.ts +24 -0
  9. package/dist/base/cache.d.ts.map +1 -0
  10. package/dist/base/cache.js +58 -0
  11. package/dist/base/cache.js.map +1 -0
  12. package/dist/base/error.d.ts +7 -0
  13. package/dist/base/error.d.ts.map +1 -1
  14. package/dist/base/error.js +12 -1
  15. package/dist/base/error.js.map +1 -1
  16. package/dist/base/math.d.ts +12 -0
  17. package/dist/base/math.d.ts.map +1 -1
  18. package/dist/base/math.js +39 -0
  19. package/dist/base/math.js.map +1 -1
  20. package/dist/base/typing.d.ts +7 -0
  21. package/dist/base/typing.d.ts.map +1 -1
  22. package/dist/{packet → binary}/decoder.d.ts.map +1 -1
  23. package/dist/{packet → binary}/decoder.js.map +1 -1
  24. package/dist/{packet → binary}/encoder.d.ts +2 -2
  25. package/dist/binary/encoder.d.ts.map +1 -0
  26. package/dist/{packet → binary}/encoder.js +6 -2
  27. package/dist/binary/encoder.js.map +1 -0
  28. package/dist/{packet → binary}/static.d.ts.map +1 -1
  29. package/dist/{packet → binary}/static.js.map +1 -1
  30. package/dist/client/client.d.ts +23 -2
  31. package/dist/client/client.d.ts.map +1 -1
  32. package/dist/client/client.js +32 -15
  33. package/dist/client/client.js.map +1 -1
  34. package/dist/index.d.ts +12 -4
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +12 -4
  37. package/dist/index.js.map +1 -1
  38. package/dist/{client → net}/tcp.d.ts +2 -2
  39. package/dist/net/tcp.d.ts.map +1 -0
  40. package/dist/{client → net}/tcp.js +2 -2
  41. package/dist/net/tcp.js.map +1 -0
  42. package/dist/packet/{dispatcher.d.ts → listener.d.ts} +7 -18
  43. package/dist/packet/listener.d.ts.map +1 -0
  44. package/dist/packet/{dispatcher.js → listener.js} +71 -137
  45. package/dist/packet/listener.js.map +1 -0
  46. package/dist/packet/sender.d.ts +25 -0
  47. package/dist/packet/sender.d.ts.map +1 -0
  48. package/dist/packet/sender.js +144 -0
  49. package/dist/packet/sender.js.map +1 -0
  50. package/dist/physics/aabb.d.ts.map +1 -1
  51. package/dist/physics/aabb.js +9 -8
  52. package/dist/physics/aabb.js.map +1 -1
  53. package/dist/physics/player.d.ts +1 -0
  54. package/dist/physics/player.d.ts.map +1 -1
  55. package/dist/physics/player.js +21 -3
  56. package/dist/physics/player.js.map +1 -1
  57. package/dist/version/codec.d.ts +1 -1
  58. package/dist/version/codec.d.ts.map +1 -1
  59. package/dist/version/codec.js +21 -5
  60. package/dist/version/codec.js.map +1 -1
  61. package/dist/version/registry.d.ts +16 -1
  62. package/dist/version/registry.d.ts.map +1 -1
  63. package/dist/version/registry.js +31 -2
  64. package/dist/version/registry.js.map +1 -1
  65. package/dist/world/block.d.ts +5 -0
  66. package/dist/world/block.d.ts.map +1 -1
  67. package/dist/world/block.js +56 -0
  68. package/dist/world/block.js.map +1 -1
  69. package/dist/world/state.d.ts +19 -0
  70. package/dist/world/state.d.ts.map +1 -1
  71. package/dist/world/state.js +16 -0
  72. package/dist/world/state.js.map +1 -1
  73. package/package.json +8 -10
  74. package/src/base/bitset.ts +312 -31
  75. package/src/base/cache.ts +62 -0
  76. package/src/base/error.ts +9 -0
  77. package/src/base/math.ts +43 -1
  78. package/src/base/typing.ts +5 -0
  79. package/src/{packet → binary}/encoder.ts +6 -2
  80. package/src/client/client.ts +54 -19
  81. package/src/index.ts +12 -4
  82. package/src/{client → net}/tcp.ts +3 -3
  83. package/src/packet/{dispatcher.ts → listener.ts} +90 -175
  84. package/src/packet/sender.ts +176 -0
  85. package/src/physics/aabb.ts +9 -8
  86. package/src/physics/player.ts +19 -3
  87. package/src/version/codec.ts +22 -6
  88. package/src/version/registry.ts +49 -2
  89. package/src/world/block.ts +72 -2
  90. package/src/world/state.ts +39 -2
  91. package/test/bitset/index.ts +3 -8
  92. package/test/client/index.ts +12 -2
  93. package/test/state/index.ts +7 -0
  94. package/dist/client/tcp.d.ts.map +0 -1
  95. package/dist/client/tcp.js.map +0 -1
  96. package/dist/packet/dispatcher.d.ts.map +0 -1
  97. package/dist/packet/dispatcher.js.map +0 -1
  98. package/dist/packet/encoder.d.ts.map +0 -1
  99. package/dist/packet/encoder.js.map +0 -1
  100. /package/dist/{packet → binary}/decoder.d.ts +0 -0
  101. /package/dist/{packet → binary}/decoder.js +0 -0
  102. /package/dist/{packet → binary}/static.d.ts +0 -0
  103. /package/dist/{packet → binary}/static.js +0 -0
  104. /package/src/{packet → binary}/decoder.ts +0 -0
  105. /package/src/{packet → binary}/static.ts +0 -0
@@ -1,42 +1,323 @@
1
- import BitSet_ from "bitset";
1
+ import assert from "node:assert";
2
+ import { UnexpectedValue } from "./error";
3
+ import { numberOfLeadingZeros, numberOfTrailingZeros } from "./math";
4
+
2
5
 
3
6
  /**
4
- * Custom implementation of BitSet to implement missing method
7
+ * Simplified implementation of the Java's BitSet class
8
+ *
9
+ * @see https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/BitSet.java
10
+ * @hidden
5
11
  */
6
- export class BitSet extends BitSet_ {
7
- public nextClearBit(fromIndex: number) {
8
- fromIndex = fromIndex || 0;
9
- const data = (this as any)['data'];
10
- let wordIndex = fromIndex >>> 5;
12
+ export class BitSet {
13
+ public static readonly ADDRESS_BITS_PER_WORD = 6;
14
+ public static readonly BITS_PER_WORD = 1 << this.ADDRESS_BITS_PER_WORD;
15
+ public static readonly WORD_MASK = 0xffffffffffffffffn;
16
+
17
+ public words: BigUint64Array;
18
+ public wordsInUse: number = 0;
19
+
20
+ private static wordIndex(bitIndex: number) {
21
+ return bitIndex >> this.ADDRESS_BITS_PER_WORD;
22
+ }
23
+
24
+ private static bitIndexInWord(bitIndex: number) {
25
+ return bitIndex & (this.BITS_PER_WORD - 1); // should be bitIndex & 63
26
+ }
27
+
28
+ /**
29
+ * Create a mask with lowest `n` bits set to 1
30
+ *
31
+ * For example:
32
+ * * n = 0 -> `0b0n`
33
+ * * n = 3 -> `0b111n`
34
+ * * n = 5 -> `0b11111n`
35
+ *
36
+ * @param nBits
37
+ * @returns
38
+ */
39
+ private static lowBits(nBits: number) {
40
+ return (1n << BigInt(nBits)) - 1n;
41
+ }
42
+
43
+ private initWords(nBits: number) {
44
+ return new BigUint64Array(BitSet.wordIndex(nBits - 1) + 1);
45
+ }
46
+
47
+ private static checkRange(fromIndex: number, toIndex: number) {
48
+ if (fromIndex < 0)
49
+ throw new UnexpectedValue("value of fromIndex", fromIndex.toString(), "non-negative number");
50
+ if (toIndex < 0)
51
+ throw new UnexpectedValue("value of toIndex", toIndex.toString(), "non-negative number");
52
+ if (fromIndex > toIndex)
53
+ throw new UnexpectedValue("fromIndex and toIndex", `fromIndex ${fromIndex}; toIndex ${toIndex}`, "fromIndex has to be smaller than toIndex");
54
+ }
55
+
56
+
57
+ constructor(nBits?: number) {
58
+ if (nBits && nBits < 0)
59
+ throw new UnexpectedValue("length of bitset", nBits.toString(), "non-negative number");
60
+ this.words = this.initWords(nBits ?? BitSet.BITS_PER_WORD);
61
+ }
62
+
63
+ public clone() {
64
+ const bitset = new BitSet(0);
65
+ bitset.words = structuredClone(this.words);
66
+ bitset.wordsInUse = this.wordsInUse;
67
+ bitset.checkInvariants();
68
+ return bitset;
69
+ }
11
70
 
12
- if (wordIndex >= data.length) {
13
- // Past the stored words — depends on the infinite tail bit
14
- return (this as any)['_'] === 0 ? fromIndex : Infinity;
71
+ private expandTo(wordIndex: number) {
72
+ const wordsRequired = wordIndex + 1;
73
+ if (this.wordsInUse < wordsRequired) {
74
+ this.ensureCapacity(wordsRequired);
75
+ this.wordsInUse = wordsRequired;
15
76
  }
77
+ }
16
78
 
17
- // ~word marks clear bits; mask off anything before fromIndex.
18
- // JS's << only uses the low 5 bits of the shift amount, so
19
- // `-1 << fromIndex` masks correctly even when fromIndex > 31.
20
- let word = ~data[wordIndex] & (-1 << fromIndex);
79
+ private ensureCapacity(wordsRequired: number) {
80
+ if (wordsRequired > this.words.length) {
81
+ // Allocate larger of doubled size or required size
82
+ const request = Math.max(2 * this.words.length, wordsRequired);
83
+ const newWords = new BigUint64Array(request);
84
+ newWords.set(this.words);
85
+ this.words = newWords;
86
+ }
87
+ }
21
88
 
22
- while (true) {
23
- if (word !== 0) {
24
- const lowestSet = word & -word; // isolate lowest set bit
25
- let c = 0;
26
- for (let v = lowestSet; (v >>>= 1); c++) {
27
- //
28
- }
29
- return wordIndex * 32 + c;
30
- }
31
- wordIndex++;
32
- if (wordIndex >= data.length) {
33
- return (this as any)['_'] === 0 ? wordIndex * 32 : Infinity;
89
+ private recalculateWordsInUse() {
90
+ let i = this.wordsInUse - 1;
91
+ while (i >= 0 && this.words[i] === 0n) i--;
92
+ this.wordsInUse = i + 1; // The new logical size
93
+ }
94
+
95
+ private checkInvariants() {
96
+ assert(this.wordsInUse === 0 || this.words[this.wordsInUse - 1] !== 0n);
97
+ assert(this.wordsInUse >= 0 && this.wordsInUse <= this.words.length);
98
+ assert(this.wordsInUse === this.words.length || this.words[this.wordsInUse] === 0n);
99
+ }
100
+
101
+
102
+ public toByteArray() {
103
+ const n = this.wordsInUse;
104
+ if (n == 0)
105
+ return [];
106
+
107
+ const bytes: number[] = [];
108
+ for (const word of this.words) {
109
+ let coppiedWord = structuredClone(word);
110
+ // Slice a 64-bit (or 8-byte word) into 8 8-bit (or 1-byte) slices
111
+ for (let i = 0; i < 8; i++) {
112
+ bytes.push(Number(coppiedWord & 0xffn));
113
+ coppiedWord >>= 8n;
34
114
  }
35
- word = ~data[wordIndex];
36
115
  }
37
- };
38
116
 
39
- public override clone(): BitSet {
40
- return new BitSet(this.toString());
117
+ while (bytes.length > 0 && bytes.at(-1) === 0) bytes.pop();
118
+ return bytes;
119
+ }
120
+
121
+ public flip(bitIndex: number) {
122
+ if (bitIndex < 0)
123
+ throw new UnexpectedValue("value of bitIndex", bitIndex.toString(), "non-negative number");
124
+
125
+ const wordIndex = BitSet.wordIndex(bitIndex);
126
+ this.expandTo(wordIndex);
127
+
128
+ this.words[wordIndex]! ^= BigInt(BitSet.bitIndexInWord(bitIndex));
129
+
130
+ this.recalculateWordsInUse();
131
+ this.checkInvariants();
41
132
  }
42
- }
133
+
134
+ public set(bitIndex: number) {
135
+ if (bitIndex < 0)
136
+ throw new UnexpectedValue("value of bitIndex", bitIndex.toString(), "non-negative number");
137
+
138
+ const wordIndex = BitSet.wordIndex(bitIndex);
139
+ this.expandTo(wordIndex);
140
+
141
+ this.words[wordIndex]! |= 1n << BigInt(BitSet.bitIndexInWord(bitIndex)); // Restores invariants
142
+
143
+ this.checkInvariants();
144
+ }
145
+ public setRange(fromIndex: number, toIndex: number) {
146
+ BitSet.checkRange(fromIndex, toIndex);
147
+
148
+ if (fromIndex == toIndex)
149
+ return;
150
+
151
+ // Increase capacity if necessary
152
+ const startWordIndex = BitSet.wordIndex(fromIndex);
153
+ const endWordIndex = BitSet.wordIndex(toIndex - 1);
154
+ this.expandTo(endWordIndex);
155
+
156
+ const fromBit = BitSet.bitIndexInWord(fromIndex),
157
+ toBitInclusive = BitSet.bitIndexInWord(toIndex - 1) + 1,
158
+
159
+ /**
160
+ * The NOT (~) bitwise meaning:
161
+ *
162
+ * Let say we want to set 2 to 6:
163
+ * ```
164
+ * 110010100
165
+ * ^ ^
166
+ * | +-- this is the 2nd bit
167
+ * and this is the 6th bit
168
+ * ```
169
+ *
170
+ * So we call lowBits(2) and it return `...0011`
171
+ *
172
+ * Then we have to flip it to `...1100` to access the '2nd bit and the bits after it'
173
+ */
174
+ firstWordMask = ~BitSet.lowBits(fromBit),
175
+ lastWordMask = BitSet.lowBits(toBitInclusive);
176
+ if (startWordIndex == endWordIndex) {
177
+ // Case 1: One word
178
+ this.words[startWordIndex]! |= (firstWordMask & lastWordMask);
179
+ } else {
180
+ // Case 2: Multiple words
181
+ // Handle first word
182
+ this.words[startWordIndex]! |= firstWordMask;
183
+
184
+ // Handle intermediate words, if any
185
+ for (let i = startWordIndex + 1; i < endWordIndex; i++)
186
+ this.words[i] = BitSet.WORD_MASK;
187
+
188
+ // Handle last word (restores invariants)
189
+ this.words[endWordIndex]! |= lastWordMask;
190
+ }
191
+
192
+ this.checkInvariants();
193
+ }
194
+
195
+
196
+ public clear(bitIndex: number) {
197
+ if (bitIndex < 0)
198
+ throw new UnexpectedValue("value of bitIndex", bitIndex.toString(), "non-negative number");
199
+
200
+ const wordIndex = BitSet.wordIndex(bitIndex);
201
+ if (wordIndex >= this.wordsInUse)
202
+ return;
203
+
204
+ this.words[wordIndex]! &= ~(1n << BigInt(bitIndex));
205
+
206
+ this.recalculateWordsInUse();
207
+ this.checkInvariants();
208
+ }
209
+ public clearRange(fromIndex: number, toIndex: number) {
210
+ BitSet.checkRange(fromIndex, toIndex);
211
+
212
+ if (fromIndex == toIndex)
213
+ return;
214
+
215
+ const startWordIndex = BitSet.wordIndex(fromIndex);
216
+ if (startWordIndex >= this.wordsInUse)
217
+ return;
218
+
219
+ let endWordIndex = BitSet.wordIndex(toIndex - 1);
220
+ if (endWordIndex >= this.wordsInUse) {
221
+ toIndex = this.length();
222
+ endWordIndex = this.wordsInUse - 1;
223
+ }
224
+
225
+ const fromBit = BitSet.bitIndexInWord(fromIndex),
226
+ toBitInclusive = BitSet.bitIndexInWord(toIndex - 1) + 1,
227
+ /**
228
+ * See the explaination in the setRange function
229
+ */
230
+ firstWordMask = ~BitSet.lowBits(fromBit),
231
+ lastWordMask = BitSet.lowBits(toBitInclusive);
232
+ if (startWordIndex == endWordIndex) {
233
+ // Case 1: One word
234
+ this.words[startWordIndex]! &= ~(firstWordMask & lastWordMask);
235
+ } else {
236
+ // Case 2: Multiple words
237
+ // Handle first word
238
+ this.words[startWordIndex]! &= ~firstWordMask;
239
+
240
+ // Handle intermediate words, if any
241
+ for (let i = startWordIndex + 1; i < endWordIndex; i++)
242
+ this.words[i]! = 0n;
243
+
244
+ // Handle last word
245
+ this.words[endWordIndex]! &= ~lastWordMask;
246
+ }
247
+
248
+ this.recalculateWordsInUse();
249
+ this.checkInvariants();
250
+ }
251
+
252
+
253
+ public get(bitIndex: number) {
254
+ if (bitIndex < 0)
255
+ throw new UnexpectedValue("value of bitIndex", bitIndex.toString(), "non-negative number");
256
+
257
+ this.checkInvariants();
258
+
259
+ const wordIndex = BitSet.wordIndex(bitIndex);
260
+ if (wordIndex >= this.wordsInUse) return false;
261
+
262
+ const bit = 1n << BigInt(BitSet.bitIndexInWord(bitIndex));
263
+ return (Number(this.words[wordIndex]! & bit) != 0);
264
+ }
265
+
266
+ public nextSetBit(fromIndex: number) {
267
+ if (fromIndex < 0)
268
+ throw new UnexpectedValue("value of fromIndex", fromIndex.toString(), "non-negative number");
269
+
270
+ this.checkInvariants();
271
+
272
+ let u = BitSet.wordIndex(fromIndex);
273
+ if (u >= this.wordsInUse)
274
+ return -1;
275
+
276
+ let word = this.words[u]! & (~0n << BigInt(BitSet.bitIndexInWord(fromIndex)));
277
+
278
+ while (true) {
279
+ if (word != 0n)
280
+ return (u * BitSet.BITS_PER_WORD) + numberOfTrailingZeros(word);
281
+ if (++u == this.wordsInUse)
282
+ return -1;
283
+ word = this.words[u]!;
284
+ }
285
+ }
286
+
287
+ public nextClearBit(fromIndex: number) {
288
+ // Neither spec nor implementation handle bitsets of maximal length.
289
+ // See 4816253.
290
+ if (fromIndex < 0)
291
+ throw new UnexpectedValue("value of fromIndex", fromIndex.toString(), "non-negative number");
292
+
293
+ this.checkInvariants();
294
+
295
+ let u = BitSet.wordIndex(fromIndex);
296
+ if (u >= this.wordsInUse)
297
+ return fromIndex;
298
+
299
+ let word = ~this.words[u]! & (BitSet.WORD_MASK << BigInt(fromIndex));
300
+
301
+ while (true) {
302
+ if (word != 0n)
303
+ return (u * BitSet.BITS_PER_WORD) + numberOfTrailingZeros(word);
304
+ if (++u == this.wordsInUse)
305
+ return this.wordsInUse * BitSet.BITS_PER_WORD;
306
+ word = ~this.words[u]!;
307
+ }
308
+ }
309
+
310
+ public length() {
311
+ if (this.wordsInUse == 0)
312
+ return 0;
313
+
314
+ return BitSet.BITS_PER_WORD * (this.wordsInUse - 1) +
315
+ (BitSet.BITS_PER_WORD - numberOfLeadingZeros(this.words[this.wordsInUse - 1]!));
316
+ }
317
+
318
+ public isEmpty() {
319
+ return this.wordsInUse == 0;
320
+ }
321
+ }
322
+
323
+ export default BitSet;
@@ -0,0 +1,62 @@
1
+ /**
2
+ * The base implementation for the Cache class
3
+ */
4
+ export abstract class CacheImplementation<T = any> {
5
+ public abstract put(key: string, item: T): void;
6
+ public abstract get(key: string): T | undefined;
7
+ public abstract del(key: string): void;
8
+ }
9
+
10
+ /**
11
+ * The LRU cache implementation.
12
+ *
13
+ * @see https://en.wikipedia.org/wiki/Cache_replacement_policies#Time-Aware,_Least_Recently_Used_(TLRU)
14
+ */
15
+ export class LRUCache<T> extends CacheImplementation<T> {
16
+ private hash: Map<string, T> = new Map();
17
+ private list: string[] = [];
18
+
19
+ constructor(private capacity: number) {
20
+ super();
21
+ }
22
+
23
+ public put(key: string, item: T): void {
24
+ if (this.hash.has(key)) {
25
+ const oldIndex = this.list.indexOf(key);
26
+ if (oldIndex > -1)
27
+ this.list.splice(oldIndex, 1);
28
+ }
29
+
30
+ this.hash.set(key, item);
31
+ this.list.push(key);
32
+
33
+ this.truncateHash();
34
+ }
35
+
36
+ public get(key: string) {
37
+ if (!this.hash.has(key)) return undefined;
38
+
39
+ const item = this.hash.get(key);
40
+ const oldIndex = this.list.indexOf(key);
41
+ if (oldIndex > -1)
42
+ this.list.splice(oldIndex, 1);
43
+ this.list.push(key);
44
+
45
+ return item;
46
+ }
47
+
48
+ public del(key: string): void {
49
+ const oldIndex = this.list.indexOf(key);
50
+ if (oldIndex > -1)
51
+ this.list.splice(oldIndex, 1);
52
+ this.hash.delete(key);
53
+ }
54
+
55
+ private truncateHash() {
56
+ while (this.list.length > this.capacity) {
57
+ const popping = this.list.pop();
58
+ if (popping)
59
+ this.hash.delete(popping);
60
+ }
61
+ }
62
+ }
package/src/base/error.ts CHANGED
@@ -211,4 +211,13 @@ export class HaveSignatureButNotIndex extends Error {
211
211
  constructor() {
212
212
  super (`expecting both shouldVerifyMessageOrder set to true when shouldVerifyMessageSignature set to true`);
213
213
  }
214
+ }
215
+
216
+ /**
217
+ * @hidden
218
+ */
219
+ export class MessageTooLong extends Error {
220
+ constructor(public message: string) {
221
+ super(`your message is exceeding the 256 characters long limit, got ${message.length} characters`);
222
+ }
214
223
  }
package/src/base/math.ts CHANGED
@@ -112,4 +112,46 @@ export function minecraftSha1(
112
112
  function mcHexDigest(digest: Buffer | Uint8Array): string {
113
113
  const bigint = BigInt(`0x${digest.toString('hex')}`);
114
114
  return BigInt.asIntN(digest.length * 8, bigint).toString(16);
115
- }
115
+ }
116
+
117
+ /**
118
+ * Simplified implementation of Java's Long.numberOfTrailingZeros
119
+ * @param n
120
+ * @returns
121
+ */
122
+ export function numberOfTrailingZeros(n: bigint): number {
123
+ if (n === 0n) {
124
+ return 64;
125
+ }
126
+
127
+ let count = 0;
128
+ while ((n & 1n) === 0n) {
129
+ count++;
130
+ n >>= 1n;
131
+ }
132
+
133
+ return count;
134
+ }
135
+
136
+ /**
137
+ * Simplified implementation of Java's Long.numberOfLeadingZeros
138
+ * @param n
139
+ * @returns
140
+ */
141
+ export function numberOfLeadingZeros(n: bigint): number {
142
+ if (n === 0n) {
143
+ return 64;
144
+ }
145
+
146
+ let count = 0;
147
+ // Start checking from bit 63 (the most significant bit)
148
+ for (let i = 63; i >= 0; i--) {
149
+ if ((n & (1n << BigInt(i))) === 0n) {
150
+ count++;
151
+ } else {
152
+ break; // Stop at the first 1 bit
153
+ }
154
+ }
155
+
156
+ return count;
157
+ }
@@ -8,3 +8,8 @@ export type If<Condition extends boolean, True, False = null> = Condition extend
8
8
  * @see https://minecraft.wiki/w/Text_component_format
9
9
  */
10
10
  export type TextComponent = Record<string, any>;
11
+
12
+ export interface RegistryData {
13
+ registry_id: string,
14
+ entries: { entry_id: string, data: Record<string, any> | null }[]
15
+ }
@@ -62,9 +62,13 @@ export class BinaryEncoder {
62
62
  */
63
63
  public writeInt(val: number) { return this.write(4, val, (buf) => buf.writeInt32BE); }
64
64
  /**
65
- * Write 6 bytes singed integer
65
+ * Write 8 bytes singed integer
66
66
  */
67
- public writeLong(val: bigint) { return this.write(8, val, (buf) => buf.writeBigInt64BE); }
67
+ public writeLong(val: bigint | number) {
68
+ // Cast to BigInt
69
+ const v = typeof val === "bigint" ? val : BigInt(val);
70
+ return this.write(8, v, (buf) => buf.writeBigInt64BE);
71
+ }
68
72
  /**
69
73
  * Write 4 bytes float
70
74
  */
@@ -3,17 +3,19 @@ import { BlockManager, BlockState } from "../world/block";
3
3
  import { EntitiesManager } from "../world/entity";
4
4
  import { Input, Player } from "../physics/player";
5
5
  import { TickLoop } from "../world/tick";
6
- import { Message, TCPClient, TCPClientOption } from "./tcp";
6
+ import { Message, TCPClient, TCPClientOption } from "../net/tcp";
7
7
  import { TypedEmmiter as TypedEmitter } from "../base/event";
8
- import { BlockRegistry, EntityRegistry, PacketRegistry, ProtocolVersionMapping } from "../version/registry";
8
+ import { BlockRegistry, EffectRegistry, EntityRegistry, PacketRegistry, ProtocolVersionMapping } from "../version/registry";
9
9
  import { BaseVec3, Vec3 } from "../physics/direction";
10
- import { TextComponent } from "../base/typing";
10
+ import { RegistryData, TextComponent } from "../base/typing";
11
11
  import { ClientStatus, ConnectionState, EmittedEvent as EmittedEvent, Entity, SharedState } from "../world/state";
12
- import { Dispatcher } from "../packet/dispatcher";
12
+ import { Listener } from "../packet/listener";
13
13
  import { VersionCodec } from "../version/codec";
14
14
  import { AuthClient } from "./auth";
15
15
  import { computeUUID } from "../base/math";
16
16
  import { HaveSignatureButNotIndex } from "../base/error";
17
+ import { Sender } from "../packet/sender";
18
+ import { CacheImplementation } from "../base/cache";
17
19
 
18
20
  export type ClientOption = Omit<TCPClientOption, "protocolVersion"> & {
19
21
  version: string,
@@ -23,12 +25,26 @@ export type ClientOption = Omit<TCPClientOption, "protocolVersion"> & {
23
25
  *
24
26
  * When set this option to true, you HAVE TO set `shouldVerifyMessageOrder` to true
25
27
  */
26
- shouldVerifyMessageSignature?: boolean
28
+ shouldVerifyMessageSignature?: boolean,
27
29
 
28
30
  /**
29
31
  * Should verify the message orders to keep the sync with server.
30
32
  */
31
- shouldVerifyMessageOrder?: boolean
33
+ shouldVerifyMessageOrder?: boolean,
34
+
35
+ /**
36
+ * Whether to load the whole chunk section (16x16x16) and keep them in the cache or just load the needed block.
37
+ *
38
+ * Please consider this carefully since this can consume a lot of resource to calculate the whole chunk section and store it while you may not need that much.
39
+ *
40
+ * This option is useful when you have to constantly access to the world chunk, e.g pathfinding.
41
+ */
42
+ loadAndCacheChunk?: boolean,
43
+
44
+ /**
45
+ * Custom cache class. Default to `LRUCache`
46
+ */
47
+ cacheImplementation?: new () => CacheImplementation<Uint16Array>,
32
48
  }
33
49
 
34
50
  export interface ClientEvents {
@@ -71,6 +87,8 @@ export interface ClientEvents {
71
87
  updateEntity: [entityId: number],
72
88
  /** When an entity go out of client view distance or despawned */
73
89
  removeEntity: [entityId: number],
90
+ /** The registry_data packet */
91
+ registryData: [data: RegistryData],
74
92
 
75
93
  // Chat
76
94
  /** When a chat message got broadcasted */
@@ -101,6 +119,8 @@ export class Client<IsReady extends boolean = boolean> extends (EventEmitter as
101
119
 
102
120
  private tcp: TCPClient;
103
121
  private state: SharedState<IsReady>;
122
+ private packetListener: Listener;
123
+ private packetSender: Sender;
104
124
 
105
125
  constructor(private options: ClientOption) {
106
126
  if (options.shouldVerifyMessageSignature === true && options.shouldVerifyMessageOrder !== true)
@@ -111,7 +131,8 @@ export class Client<IsReady extends boolean = boolean> extends (EventEmitter as
111
131
  // For managing shared data
112
132
  this.state = new SharedState(options);
113
133
  // For handling packet
114
- const dispatcher = new Dispatcher(this.state, this.emit.bind(this));
134
+ this.packetSender = new Sender(this.state, this.emit.bind(this));
135
+ this.packetListener = new Listener(this.state, this.emit.bind(this), this.packetSender);
115
136
  // For reading packet
116
137
  const versionCodec = new VersionCodec(this.state);
117
138
  // For throwing packet through internet
@@ -126,12 +147,12 @@ export class Client<IsReady extends boolean = boolean> extends (EventEmitter as
126
147
  this.tcp.forwardPacket = versionCodec.handlePacket.bind(versionCodec);
127
148
  this.tcp.parsePacket = versionCodec.encodePacket.bind(versionCodec);
128
149
  this.tcp.sendInitPacket = () => {
129
- dispatcher.sendHandshake();
130
- dispatcher.sendLoginStart();
150
+ this.packetSender.sendHandshake();
151
+ this.packetSender.sendLoginStart();
131
152
  };
132
- dispatcher.sendPacket = this.tcp.sendPacket.bind(this.tcp);
133
- dispatcher.disconnect = this.disconnect.bind(this);
134
- versionCodec.consumePacket = dispatcher.handlePacket.bind(dispatcher);
153
+ this.packetSender.sendPacket = this.tcp.sendPacket.bind(this.tcp);
154
+ this.packetListener.disconnect = this.disconnect.bind(this);
155
+ versionCodec.consumePacket = this.packetListener.handlePacket.bind(this.packetListener);
135
156
 
136
157
  // For querying data
137
158
  this.blocks = new BlockManager(this.state);
@@ -143,10 +164,10 @@ export class Client<IsReady extends boolean = boolean> extends (EventEmitter as
143
164
  this.entities,
144
165
  this.blocks,
145
166
  );
146
- this.player.sendPlayerPos = dispatcher.sendPlayerPos;
147
- this.player.sendPlayerPosRot = dispatcher.sendPlayerPosRot;
148
- this.player.sendPlayerRot = dispatcher.sendPlayerRot;
149
- this.player.sendPlayerStatus = dispatcher.sendPlayerStatus;
167
+ this.player.sendPlayerPos = this.packetSender.sendPlayerPos;
168
+ this.player.sendPlayerPosRot = this.packetSender.sendPlayerPosRot;
169
+ this.player.sendPlayerRot = this.packetSender.sendPlayerRot;
170
+ this.player.sendPlayerStatus = this.packetSender.sendPlayerStatus;
150
171
 
151
172
  // For tick-tock tick-tock
152
173
  this.tickLoop = new TickLoop(() => {
@@ -159,6 +180,11 @@ export class Client<IsReady extends boolean = boolean> extends (EventEmitter as
159
180
  // Forward TCP events
160
181
  this.tcp.on("connect", () => this.emit("connect"));
161
182
  this.tcp.on("destroy", () => this.emit("destroy"));
183
+ this.on("loadChunk", (sx, sy, sz) => this.blocks.deleteChunkCache(sx, sy, sz));
184
+ this.on("unloadChunk", (sx, sz) => {
185
+ for (let sy = 0; sy < 16; sy++)
186
+ this.blocks.deleteChunkCache(sx, sy, sz);
187
+ });
162
188
  }
163
189
 
164
190
  private emitEvent(event: EmittedEvent) {
@@ -168,9 +194,11 @@ export class Client<IsReady extends boolean = boolean> extends (EventEmitter as
168
194
  private async loadRegistries() {
169
195
  if (Client.loadRegistry) return;
170
196
  Client.loadRegistry = true;
171
- await BlockRegistry.load(this.options.version);
172
- await EntityRegistry.load(this.options.version);
173
- await PacketRegistry.load(this.options.version);
197
+ const { version } = this.options;
198
+ await BlockRegistry.load(version);
199
+ await EntityRegistry.load(version);
200
+ await PacketRegistry.load(version);
201
+ await EffectRegistry.load(version);
174
202
  }
175
203
 
176
204
  // Start / stop
@@ -239,6 +267,13 @@ export class Client<IsReady extends boolean = boolean> extends (EventEmitter as
239
267
  this.player.releaseAllInputs();
240
268
  }
241
269
 
270
+ /**
271
+ * Send a message
272
+ */
273
+ public chat(message: string) {
274
+ this.packetSender.sendMessage(message);
275
+ }
276
+
242
277
  // World
243
278
  /**
244
279
  * Get the block state at position