packet-events-js 1.0.0 → 1.0.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.
@@ -0,0 +1,396 @@
1
+ import { EventEmitter } from '../events/EventEmitter.js';
2
+ import { PacketBuffer } from '../utils/PacketBuffer.js';
3
+
4
+ export class ProtocolMetrics {
5
+ constructor() {
6
+ this.reset();
7
+ }
8
+
9
+ reset() {
10
+ this.startTime = Date.now();
11
+ this.packetsReceived = 0;
12
+ this.packetsSent = 0;
13
+ this.bytesReceived = 0;
14
+ this.bytesSent = 0;
15
+ this.compressionSaved = 0;
16
+ this.encryptedPackets = 0;
17
+ this.errors = 0;
18
+ this.latencyHistory = [];
19
+ this.throughputHistory = [];
20
+ this.packetTypeStats = new Map();
21
+ this.stateTransitions = [];
22
+ }
23
+
24
+ recordPacket(direction, size, packetName, compressed = false, originalSize = 0) {
25
+ if (direction === 'CLIENTBOUND') {
26
+ this.packetsReceived++;
27
+ this.bytesReceived += size;
28
+ } else {
29
+ this.packetsSent++;
30
+ this.bytesSent += size;
31
+ }
32
+
33
+ if (compressed && originalSize > size) {
34
+ this.compressionSaved += (originalSize - size);
35
+ }
36
+
37
+ const stats = this.packetTypeStats.get(packetName) || { count: 0, bytes: 0 };
38
+ stats.count++;
39
+ stats.bytes += size;
40
+ this.packetTypeStats.set(packetName, stats);
41
+ }
42
+
43
+ recordLatency(ms) {
44
+ this.latencyHistory.push({ time: Date.now(), latency: ms });
45
+ if (this.latencyHistory.length > 1000) {
46
+ this.latencyHistory.shift();
47
+ }
48
+ }
49
+
50
+ recordStateTransition(from, to) {
51
+ this.stateTransitions.push({ time: Date.now(), from, to });
52
+ }
53
+
54
+ getAverageLatency() {
55
+ if (this.latencyHistory.length === 0) return 0;
56
+ return this.latencyHistory.reduce((a, b) => a + b.latency, 0) / this.latencyHistory.length;
57
+ }
58
+
59
+ getMinLatency() {
60
+ if (this.latencyHistory.length === 0) return 0;
61
+ return Math.min(...this.latencyHistory.map(l => l.latency));
62
+ }
63
+
64
+ getMaxLatency() {
65
+ if (this.latencyHistory.length === 0) return 0;
66
+ return Math.max(...this.latencyHistory.map(l => l.latency));
67
+ }
68
+
69
+ getThroughput() {
70
+ const elapsed = (Date.now() - this.startTime) / 1000;
71
+ return {
72
+ packetsPerSecond: ((this.packetsReceived + this.packetsSent) / elapsed).toFixed(2),
73
+ bytesPerSecond: ((this.bytesReceived + this.bytesSent) / elapsed).toFixed(2),
74
+ kbpsIn: ((this.bytesReceived / elapsed) / 1024 * 8).toFixed(2),
75
+ kbpsOut: ((this.bytesSent / elapsed) / 1024 * 8).toFixed(2)
76
+ };
77
+ }
78
+
79
+ getReport() {
80
+ const elapsed = (Date.now() - this.startTime) / 1000;
81
+ return {
82
+ duration: elapsed,
83
+ packets: {
84
+ received: this.packetsReceived,
85
+ sent: this.packetsSent,
86
+ total: this.packetsReceived + this.packetsSent
87
+ },
88
+ bytes: {
89
+ received: this.bytesReceived,
90
+ sent: this.bytesSent,
91
+ total: this.bytesReceived + this.bytesSent,
92
+ compressionSaved: this.compressionSaved
93
+ },
94
+ latency: {
95
+ average: this.getAverageLatency().toFixed(2),
96
+ min: this.getMinLatency(),
97
+ max: this.getMaxLatency(),
98
+ samples: this.latencyHistory.length
99
+ },
100
+ throughput: this.getThroughput(),
101
+ packetTypes: Object.fromEntries(this.packetTypeStats),
102
+ stateTransitions: this.stateTransitions,
103
+ errors: this.errors
104
+ };
105
+ }
106
+ }
107
+
108
+ export class PacketInspector {
109
+ constructor() {
110
+ this.inspectors = new Map();
111
+ this.registerDefaultInspectors();
112
+ }
113
+
114
+ registerDefaultInspectors() {
115
+ this.register('position', (packet) => {
116
+ const fields = ['x', 'y', 'z', 'yaw', 'pitch'];
117
+ const result = {};
118
+ for (const field of fields) {
119
+ if (packet[field] !== undefined) {
120
+ result[field] = packet[field];
121
+ }
122
+ }
123
+ if (Object.keys(result).length > 0) {
124
+ result.blockPos = {
125
+ x: Math.floor(result.x || 0),
126
+ y: Math.floor(result.y || 0),
127
+ z: Math.floor(result.z || 0)
128
+ };
129
+ result.chunkPos = {
130
+ x: Math.floor((result.x || 0) / 16),
131
+ z: Math.floor((result.z || 0) / 16)
132
+ };
133
+ }
134
+ return result;
135
+ });
136
+
137
+ this.register('entity', (packet) => {
138
+ const fields = ['entityId', 'entityUUID', 'entityType', 'velocityX', 'velocityY', 'velocityZ'];
139
+ const result = {};
140
+ for (const field of fields) {
141
+ if (packet[field] !== undefined) {
142
+ result[field] = packet[field];
143
+ }
144
+ }
145
+ return result;
146
+ });
147
+
148
+ this.register('chat', (packet) => {
149
+ const result = {};
150
+ if (packet.message) result.message = packet.message;
151
+ if (packet.content) result.content = packet.content;
152
+ if (packet.sender) result.sender = packet.sender;
153
+ if (packet.type !== undefined) result.type = packet.type;
154
+ return result;
155
+ });
156
+ }
157
+
158
+ register(name, inspector) {
159
+ this.inspectors.set(name, inspector);
160
+ return this;
161
+ }
162
+
163
+ inspect(packet, inspectorName = null) {
164
+ const results = {};
165
+
166
+ if (inspectorName) {
167
+ const inspector = this.inspectors.get(inspectorName);
168
+ if (inspector) {
169
+ results[inspectorName] = inspector(packet);
170
+ }
171
+ } else {
172
+ for (const [name, inspector] of this.inspectors) {
173
+ const result = inspector(packet);
174
+ if (Object.keys(result).length > 0) {
175
+ results[name] = result;
176
+ }
177
+ }
178
+ }
179
+
180
+ return results;
181
+ }
182
+
183
+ hexDump(buffer, bytesPerLine = 16) {
184
+ const lines = [];
185
+ for (let i = 0; i < buffer.length; i += bytesPerLine) {
186
+ const slice = buffer.subarray(i, Math.min(i + bytesPerLine, buffer.length));
187
+ const hex = [...slice].map(b => b.toString(16).padStart(2, '0')).join(' ');
188
+ const ascii = [...slice].map(b => b >= 32 && b <= 126 ? String.fromCharCode(b) : '.').join('');
189
+ lines.push(`${i.toString(16).padStart(8, '0')} ${hex.padEnd(bytesPerLine * 3 - 1)} |${ascii}|`);
190
+ }
191
+ return lines.join('\n');
192
+ }
193
+
194
+ analyzeStructure(packet) {
195
+ const structure = {
196
+ className: packet.constructor.name,
197
+ packetId: packet.constructor.packetId,
198
+ packetName: packet.constructor.packetName,
199
+ fields: []
200
+ };
201
+
202
+ for (const [key, value] of Object.entries(packet)) {
203
+ if (key.startsWith('_')) continue;
204
+
205
+ let type = typeof value;
206
+ let details = {};
207
+
208
+ if (value === null) {
209
+ type = 'null';
210
+ } else if (value === undefined) {
211
+ type = 'undefined';
212
+ } else if (Buffer.isBuffer(value)) {
213
+ type = 'Buffer';
214
+ details.length = value.length;
215
+ } else if (Array.isArray(value)) {
216
+ type = 'Array';
217
+ details.length = value.length;
218
+ } else if (typeof value === 'bigint') {
219
+ type = 'BigInt';
220
+ details.value = value.toString();
221
+ } else if (value && typeof value === 'object') {
222
+ type = value.constructor.name;
223
+ if (value.toString) {
224
+ details.string = value.toString();
225
+ }
226
+ }
227
+
228
+ structure.fields.push({ name: key, type, value: details.value || value, details });
229
+ }
230
+
231
+ return structure;
232
+ }
233
+ }
234
+
235
+ export class ProtocolAnalyzer extends EventEmitter {
236
+ constructor(options = {}) {
237
+ super();
238
+
239
+ this.metrics = new ProtocolMetrics();
240
+ this.inspector = new PacketInspector();
241
+ this.isAnalyzing = false;
242
+ this.filters = options.filters || [];
243
+ this.alerts = [];
244
+ this.alertRules = new Map();
245
+
246
+ this.setupDefaultAlerts();
247
+ }
248
+
249
+ setupDefaultAlerts() {
250
+ this.addAlertRule('high_latency', (metrics) => {
251
+ if (metrics.getAverageLatency() > 200) {
252
+ return { level: 'warning', message: `High latency: ${metrics.getAverageLatency().toFixed(0)}ms` };
253
+ }
254
+ return null;
255
+ });
256
+
257
+ this.addAlertRule('packet_flood', (metrics) => {
258
+ const throughput = metrics.getThroughput();
259
+ if (parseFloat(throughput.packetsPerSecond) > 1000) {
260
+ return { level: 'warning', message: `High packet rate: ${throughput.packetsPerSecond}/s` };
261
+ }
262
+ return null;
263
+ });
264
+
265
+ this.addAlertRule('error_rate', (metrics) => {
266
+ const total = metrics.packetsReceived + metrics.packetsSent;
267
+ if (total > 100 && (metrics.errors / total) > 0.01) {
268
+ return { level: 'error', message: `High error rate: ${(metrics.errors / total * 100).toFixed(2)}%` };
269
+ }
270
+ return null;
271
+ });
272
+ }
273
+
274
+ addAlertRule(name, rule) {
275
+ this.alertRules.set(name, rule);
276
+ return this;
277
+ }
278
+
279
+ removeAlertRule(name) {
280
+ this.alertRules.delete(name);
281
+ return this;
282
+ }
283
+
284
+ checkAlerts() {
285
+ for (const [name, rule] of this.alertRules) {
286
+ const alert = rule(this.metrics);
287
+ if (alert) {
288
+ const alertRecord = {
289
+ time: Date.now(),
290
+ rule: name,
291
+ ...alert
292
+ };
293
+ this.alerts.push(alertRecord);
294
+ this.emit('alert', alertRecord);
295
+ }
296
+ }
297
+ }
298
+
299
+ start() {
300
+ this.isAnalyzing = true;
301
+ this.metrics.reset();
302
+ this.alerts = [];
303
+ this.emit('start');
304
+ return this;
305
+ }
306
+
307
+ stop() {
308
+ this.isAnalyzing = false;
309
+ this.emit('stop', this.getReport());
310
+ return this;
311
+ }
312
+
313
+ analyzePacket(packet, direction, state, rawData) {
314
+ if (!this.isAnalyzing) return null;
315
+
316
+ const analysis = {
317
+ timestamp: Date.now(),
318
+ direction,
319
+ state: typeof state === 'object' ? state.name : state,
320
+ packetName: packet.constructor.packetName,
321
+ packetId: packet.constructor.packetId,
322
+ size: rawData.length,
323
+ inspection: this.inspector.inspect(packet),
324
+ structure: this.inspector.analyzeStructure(packet)
325
+ };
326
+
327
+ this.metrics.recordPacket(direction, rawData.length, packet.constructor.packetName);
328
+
329
+ if ((this.metrics.packetsReceived + this.metrics.packetsSent) % 100 === 0) {
330
+ this.checkAlerts();
331
+ }
332
+
333
+ this.emit('packet', analysis);
334
+ return analysis;
335
+ }
336
+
337
+ recordLatency(ms) {
338
+ this.metrics.recordLatency(ms);
339
+ this.emit('latency', ms);
340
+ }
341
+
342
+ recordError(error) {
343
+ this.metrics.errors++;
344
+ this.emit('error', error);
345
+ }
346
+
347
+ recordStateTransition(from, to) {
348
+ this.metrics.recordStateTransition(from, to);
349
+ this.emit('stateChange', { from, to });
350
+ }
351
+
352
+ getReport() {
353
+ return {
354
+ ...this.metrics.getReport(),
355
+ alerts: this.alerts,
356
+ isAnalyzing: this.isAnalyzing
357
+ };
358
+ }
359
+
360
+ getTopPackets(limit = 10) {
361
+ const sorted = [...this.metrics.packetTypeStats.entries()]
362
+ .sort((a, b) => b[1].count - a[1].count)
363
+ .slice(0, limit);
364
+
365
+ return sorted.map(([name, stats]) => ({
366
+ name,
367
+ count: stats.count,
368
+ bytes: stats.bytes,
369
+ avgSize: (stats.bytes / stats.count).toFixed(1)
370
+ }));
371
+ }
372
+
373
+ generateTrafficGraph(width = 60) {
374
+ const history = this.metrics.latencyHistory.slice(-width);
375
+ if (history.length === 0) return 'No data';
376
+
377
+ const max = Math.max(...history.map(h => h.latency), 1);
378
+ const height = 10;
379
+ const lines = [];
380
+
381
+ for (let y = height; y >= 0; y--) {
382
+ let line = `${Math.round(max * y / height).toString().padStart(4)} |`;
383
+ for (const h of history) {
384
+ const barHeight = Math.round((h.latency / max) * height);
385
+ line += barHeight >= y ? '█' : ' ';
386
+ }
387
+ lines.push(line);
388
+ }
389
+ lines.push(' +' + '-'.repeat(width));
390
+ lines.push(' Latency over time (ms)');
391
+
392
+ return lines.join('\n');
393
+ }
394
+ }
395
+
396
+ export default { ProtocolAnalyzer, ProtocolMetrics, PacketInspector };
@@ -0,0 +1,32 @@
1
+ export {
2
+ PacketRecorder,
3
+ PacketReplayer,
4
+ PacketRecord
5
+ } from './PacketRecorder.js';
6
+
7
+ export {
8
+ ProtocolAnalyzer,
9
+ ProtocolMetrics,
10
+ PacketInspector
11
+ } from './ProtocolAnalyzer.js';
12
+
13
+ export {
14
+ MiddlewarePipeline,
15
+ MiddlewareContext,
16
+ Middleware,
17
+ LoggingMiddleware,
18
+ RateLimitMiddleware,
19
+ ValidationMiddleware,
20
+ TransformMiddleware,
21
+ CacheMiddleware,
22
+ MetricsMiddleware
23
+ } from './MiddlewareSystem.js';
24
+
25
+ export {
26
+ AutoReconnect,
27
+ ReconnectStrategy,
28
+ HealthMonitor,
29
+ HealthStatus,
30
+ ConnectionPool,
31
+ CircuitBreaker
32
+ } from './ConnectionManager.js';
@@ -1,6 +1,7 @@
1
1
  import { MinecraftEncryption, EncryptedConnection } from '../crypto/Encryption.js';
2
2
  import { MojangAPI } from './MojangAPI.js';
3
3
  import { UUID } from '../protocol/types/UUID.js';
4
+ import { createHmac } from 'crypto';
4
5
 
5
6
  export const AuthMode = Object.freeze({
6
7
  ONLINE: 'online',
@@ -9,11 +10,228 @@ export const AuthMode = Object.freeze({
9
10
  VELOCITY: 'velocity'
10
11
  });
11
12
 
13
+ export class BungeeCordForwarding {
14
+ static parse(serverAddress) {
15
+ const parts = serverAddress.split('\0');
16
+
17
+ if (parts.length < 3) {
18
+ return { success: false, reason: 'INVALID_FORMAT' };
19
+ }
20
+
21
+ const hostname = parts[0];
22
+ const clientIP = parts[1];
23
+ const uuidString = parts[2];
24
+
25
+ let properties = [];
26
+ if (parts.length > 3 && parts[3]) {
27
+ try {
28
+ properties = JSON.parse(parts[3]);
29
+ } catch (e) {
30
+ properties = [];
31
+ }
32
+ }
33
+
34
+ let uuid;
35
+ try {
36
+ uuid = UUID.fromString(uuidString);
37
+ } catch (e) {
38
+ return { success: false, reason: 'INVALID_UUID' };
39
+ }
40
+
41
+ return {
42
+ success: true,
43
+ hostname,
44
+ clientIP,
45
+ uuid,
46
+ properties
47
+ };
48
+ }
49
+
50
+ static create(hostname, clientIP, uuid, properties = []) {
51
+ const uuidString = uuid.toString(false);
52
+ const propsJson = properties.length > 0 ? JSON.stringify(properties) : '';
53
+
54
+ if (propsJson) {
55
+ return `${hostname}\0${clientIP}\0${uuidString}\0${propsJson}`;
56
+ }
57
+ return `${hostname}\0${clientIP}\0${uuidString}`;
58
+ }
59
+ }
60
+
61
+ export class VelocityForwarding {
62
+ static MODERN_FORWARDING_VERSION = 1;
63
+ static MODERN_LAZY_SESSION = 2;
64
+ static MODERN_FORWARDING_WITH_KEY = 3;
65
+ static MODERN_FORWARDING_WITH_KEY_V2 = 4;
66
+
67
+ static verify(data, secret) {
68
+ if (!secret) {
69
+ return { success: false, reason: 'NO_SECRET' };
70
+ }
71
+
72
+ if (data.length < 32) {
73
+ return { success: false, reason: 'DATA_TOO_SHORT' };
74
+ }
75
+
76
+ const signature = data.subarray(0, 32);
77
+ const payload = data.subarray(32);
78
+
79
+ const expectedSignature = createHmac('sha256', secret)
80
+ .update(payload)
81
+ .digest();
82
+
83
+ if (!signature.equals(expectedSignature)) {
84
+ return { success: false, reason: 'INVALID_SIGNATURE' };
85
+ }
86
+
87
+ return VelocityForwarding.parsePayload(payload);
88
+ }
89
+
90
+ static parsePayload(payload) {
91
+ try {
92
+ let offset = 0;
93
+
94
+ const version = payload.readInt32BE(offset);
95
+ offset += 4;
96
+
97
+ const ipLength = payload.readInt16BE(offset);
98
+ offset += 2;
99
+ const clientIP = payload.subarray(offset, offset + ipLength).toString('utf8');
100
+ offset += ipLength;
101
+
102
+ const uuidBytes = payload.subarray(offset, offset + 16);
103
+ offset += 16;
104
+ const uuid = UUID.fromBytes(uuidBytes);
105
+
106
+ const usernameLength = payload.readInt16BE(offset);
107
+ offset += 2;
108
+ const username = payload.subarray(offset, offset + usernameLength).toString('utf8');
109
+ offset += usernameLength;
110
+
111
+ let properties = [];
112
+ if (offset < payload.length) {
113
+ const propsCount = payload.readInt32BE(offset);
114
+ offset += 4;
115
+
116
+ for (let i = 0; i < propsCount; i++) {
117
+ const nameLen = payload.readInt16BE(offset);
118
+ offset += 2;
119
+ const name = payload.subarray(offset, offset + nameLen).toString('utf8');
120
+ offset += nameLen;
121
+
122
+ const valueLen = payload.readInt16BE(offset);
123
+ offset += 2;
124
+ const value = payload.subarray(offset, offset + valueLen).toString('utf8');
125
+ offset += valueLen;
126
+
127
+ const hasSignature = payload.readInt8(offset) === 1;
128
+ offset += 1;
129
+
130
+ let signature = null;
131
+ if (hasSignature) {
132
+ const sigLen = payload.readInt16BE(offset);
133
+ offset += 2;
134
+ signature = payload.subarray(offset, offset + sigLen).toString('utf8');
135
+ offset += sigLen;
136
+ }
137
+
138
+ properties.push({ name, value, signature });
139
+ }
140
+ }
141
+
142
+ return {
143
+ success: true,
144
+ version,
145
+ clientIP,
146
+ uuid,
147
+ username,
148
+ properties
149
+ };
150
+ } catch (e) {
151
+ return { success: false, reason: 'PARSE_ERROR', error: e.message };
152
+ }
153
+ }
154
+
155
+ static create(secret, clientIP, uuid, username, properties = [], version = 1) {
156
+ const ipBuffer = Buffer.from(clientIP, 'utf8');
157
+ const usernameBuffer = Buffer.from(username, 'utf8');
158
+ const uuidBytes = uuid.toBytes();
159
+
160
+ let propsSize = 4;
161
+ const propBuffers = [];
162
+ for (const prop of properties) {
163
+ const nameBuffer = Buffer.from(prop.name, 'utf8');
164
+ const valueBuffer = Buffer.from(prop.value, 'utf8');
165
+ propBuffers.push({ name: nameBuffer, value: valueBuffer, signature: prop.signature });
166
+ propsSize += 2 + nameBuffer.length + 2 + valueBuffer.length + 1;
167
+ if (prop.signature) {
168
+ const sigBuffer = Buffer.from(prop.signature, 'utf8');
169
+ propBuffers[propBuffers.length - 1].sigBuffer = sigBuffer;
170
+ propsSize += 2 + sigBuffer.length;
171
+ }
172
+ }
173
+
174
+ const payloadSize = 4 + 2 + ipBuffer.length + 16 + 2 + usernameBuffer.length + propsSize;
175
+ const payload = Buffer.alloc(payloadSize);
176
+ let offset = 0;
177
+
178
+ payload.writeInt32BE(version, offset);
179
+ offset += 4;
180
+
181
+ payload.writeInt16BE(ipBuffer.length, offset);
182
+ offset += 2;
183
+ ipBuffer.copy(payload, offset);
184
+ offset += ipBuffer.length;
185
+
186
+ uuidBytes.copy(payload, offset);
187
+ offset += 16;
188
+
189
+ payload.writeInt16BE(usernameBuffer.length, offset);
190
+ offset += 2;
191
+ usernameBuffer.copy(payload, offset);
192
+ offset += usernameBuffer.length;
193
+
194
+ payload.writeInt32BE(properties.length, offset);
195
+ offset += 4;
196
+
197
+ for (const prop of propBuffers) {
198
+ payload.writeInt16BE(prop.name.length, offset);
199
+ offset += 2;
200
+ prop.name.copy(payload, offset);
201
+ offset += prop.name.length;
202
+
203
+ payload.writeInt16BE(prop.value.length, offset);
204
+ offset += 2;
205
+ prop.value.copy(payload, offset);
206
+ offset += prop.value.length;
207
+
208
+ if (prop.signature && prop.sigBuffer) {
209
+ payload.writeInt8(1, offset);
210
+ offset += 1;
211
+ payload.writeInt16BE(prop.sigBuffer.length, offset);
212
+ offset += 2;
213
+ prop.sigBuffer.copy(payload, offset);
214
+ offset += prop.sigBuffer.length;
215
+ } else {
216
+ payload.writeInt8(0, offset);
217
+ offset += 1;
218
+ }
219
+ }
220
+
221
+ const signature = createHmac('sha256', secret)
222
+ .update(payload)
223
+ .digest();
224
+
225
+ return Buffer.concat([signature, payload]);
226
+ }
227
+ }
228
+
12
229
  export class AuthHandler {
13
230
  constructor(options = {}) {
14
231
  this.mode = options.mode || AuthMode.OFFLINE;
15
232
  this.preventProxyConnections = options.preventProxyConnections || false;
16
233
  this.velocitySecret = options.velocitySecret || null;
234
+ this.bungeeCordEnabled = options.bungeeCordEnabled || (this.mode === AuthMode.BUNGEECORD);
17
235
  this.keyPair = null;
18
236
  }
19
237
 
@@ -23,6 +241,34 @@ export class AuthHandler {
23
241
  }
24
242
  }
25
243
 
244
+ handleBungeeCordForwarding(serverAddress) {
245
+ if (this.mode !== AuthMode.BUNGEECORD) {
246
+ return { success: false, reason: 'BUNGEECORD_NOT_ENABLED' };
247
+ }
248
+ return BungeeCordForwarding.parse(serverAddress);
249
+ }
250
+
251
+ handleVelocityForwarding(data) {
252
+ if (this.mode !== AuthMode.VELOCITY) {
253
+ return { success: false, reason: 'VELOCITY_NOT_ENABLED' };
254
+ }
255
+ if (!this.velocitySecret) {
256
+ return { success: false, reason: 'NO_VELOCITY_SECRET' };
257
+ }
258
+ return VelocityForwarding.verify(data, this.velocitySecret);
259
+ }
260
+
261
+ createBungeeCordHandshake(hostname, clientIP, uuid, properties = []) {
262
+ return BungeeCordForwarding.create(hostname, clientIP, uuid, properties);
263
+ }
264
+
265
+ createVelocityForwarding(clientIP, uuid, username, properties = []) {
266
+ if (!this.velocitySecret) {
267
+ throw new Error('Velocity secret not configured');
268
+ }
269
+ return VelocityForwarding.create(this.velocitySecret, clientIP, uuid, username, properties);
270
+ }
271
+
26
272
  getPublicKey() {
27
273
  if (!this.keyPair) return null;
28
274
  return this.keyPair.publicKey;
package/src/index.js CHANGED
@@ -15,4 +15,6 @@ export { Logger, LogLevel } from './utils/Logger.js';
15
15
 
16
16
  export { MinecraftEncryption, EncryptedConnection } from './crypto/Encryption.js';
17
17
  export { MojangAPI } from './auth/MojangAPI.js';
18
- export { AuthHandler, ClientAuthHandler, AuthMode } from './auth/AuthHandler.js';
18
+ export { AuthHandler, ClientAuthHandler, AuthMode, BungeeCordForwarding, VelocityForwarding } from './auth/AuthHandler.js';
19
+
20
+ export * from './advanced/index.js';