packet-events-js
v1.0.1
Published
A protocol library for Minecraft Java Edition in pure JavaScript
Maintainers
Readme
PacketEvents-JS
A robust protocol library for Minecraft Java Edition written entirely in pure JavaScript. Inspired by PacketEvents, designed to facilitate the processing and transmission of Minecraft protocol packets with full online mode support.
Features
- Pure JavaScript - No external dependencies, only native Node.js
- TCP Connection - Complete TCP client for Minecraft
- Packet System - Read/write all protocol data types
- Event System - Priority-based events with cancellation and async/await
- Compression - Full zlib compression support
- Encryption - Complete AES-128-CFB8 encryption support for online mode
- Online Mode - Full Mojang/Microsoft authentication support
- Offline Mode - Offline player UUID generation (version 3)
- BungeeCord Forwarding - Legacy IP forwarding support
- Velocity Forwarding - Modern forwarding with HMAC verification
- Complete NBT - Named Binary Tag read/write
- Chat Components - JSON Text Component with colors, formatting, and events
- Multi-version - Support for Minecraft 1.7.2 to 1.21.11 (66 versions)
- Packet Recorder - Record, export, import, and replay packet streams
- Protocol Analyzer - Deep packet inspection, metrics, and traffic analysis
- Middleware System - Plugin-based packet processing pipeline
- Connection Manager - AutoReconnect, HealthMonitor, ConnectionPool, CircuitBreaker
Requirements
- Node.js 18.0.0 or higher
- ES Modules enabled
Installation
npm install packet-events-jsOr clone the repository:
git clone https://github.com/FrannnnDev/PacketEvents-JS.git
cd PacketEvents-JSBasic Usage
Server List Ping
import { MinecraftClient, ProtocolVersion } from 'packet-events-js';
const client = new MinecraftClient({
host: 'mc.hypixel.net',
port: 25565,
version: ProtocolVersion.V1_21_11
});
const status = await client.ping();
console.log(`Players: ${status.players.online}/${status.players.max}`);
console.log(`Latency: ${status.latency}ms`);Connect to Server (Offline Mode)
import {
MinecraftClient,
ProtocolVersion,
Logger,
LogLevel,
KeepAliveClientboundPacket,
KeepAliveServerboundPacket
} from 'packet-events-js';
Logger.setGlobalLevel(LogLevel.DEBUG);
const client = new MinecraftClient({
host: 'localhost',
port: 25565,
version: ProtocolVersion.V1_21_11,
username: 'MyBot',
offline: true
});
client.onPacket(KeepAliveClientboundPacket, async (event) => {
const response = new KeepAliveServerboundPacket();
response.keepAliveId = event.packet.keepAliveId;
await client.sendPacket(response);
});
await client.login();
console.log(`Connected as ${client.playerName}`);Encryption API
import { MinecraftEncryption, EncryptedConnection } from 'packet-events-js';
const keyPair = MinecraftEncryption.generateKeyPair();
console.log('Public Key:', keyPair.publicKey);
console.log('Private Key:', keyPair.privateKey);
const verifyToken = MinecraftEncryption.generateVerifyToken();
const sharedSecret = MinecraftEncryption.generateSharedSecret();
const encrypted = MinecraftEncryption.encryptRSA(keyPair.publicKey, sharedSecret);
const decrypted = MinecraftEncryption.decryptRSA(keyPair.privateKey, encrypted);
const aesEncrypted = MinecraftEncryption.encryptAES(sharedSecret, Buffer.from('Hello'));
const aesDecrypted = MinecraftEncryption.decryptAES(sharedSecret, aesEncrypted);
const serverHash = MinecraftEncryption.computeServerIdHash('', sharedSecret, keyPair.publicKey);Auth Handler
import { AuthHandler, ClientAuthHandler, AuthMode } from 'packet-events-js';
const serverAuth = new AuthHandler({ mode: AuthMode.ONLINE });
serverAuth.initialize();
const publicKey = serverAuth.getPublicKey();
const verifyToken = serverAuth.generateVerifyToken();
const offlineUUID = serverAuth.generateOfflineUUID('PlayerName');
const clientAuth = new ClientAuthHandler({
accessToken: 'token',
selectedProfile: { id: 'uuid', name: 'Name' }
});BungeeCord Forwarding
import { BungeeCordForwarding, UUID } from 'packet-events-js';
const uuid = UUID.randomUUID();
const handshakeAddress = BungeeCordForwarding.create(
'play.example.com',
'192.168.1.100',
uuid,
[{ name: 'textures', value: '...' }]
);
const result = BungeeCordForwarding.parse(handshakeAddress);
if (result.success) {
console.log(`Client IP: ${result.clientIP}`);
console.log(`UUID: ${result.uuid.toString()}`);
}Velocity Modern Forwarding
import { VelocityForwarding, AuthHandler, AuthMode, UUID } from 'packet-events-js';
const auth = new AuthHandler({
mode: AuthMode.VELOCITY,
velocitySecret: 'your-secret-key'
});
const result = auth.handleVelocityForwarding(forwardingData);
if (result.success) {
console.log(`Player: ${result.username}`);
console.log(`UUID: ${result.uuid.toString()}`);
console.log(`IP: ${result.clientIP}`);
}
const uuid = UUID.randomUUID();
const data = VelocityForwarding.create(
'your-secret-key',
'10.0.0.1',
uuid,
'PlayerName',
[{ name: 'textures', value: '...', signature: '...' }]
);Intercept Packets
import { EventPriority } from 'packet-events-js';
client.packetManager.on('packetSend', (event) => {
if (event.packet.packetName === 'SetPlayerPosition') {
console.log(`Moving to: ${event.packet.x}, ${event.packet.y}, ${event.packet.z}`);
if (event.packet.y < 0) {
event.cancel();
}
}
}, { priority: EventPriority.HIGH });Create Custom Packets
import {
Packet,
defaultRegistry,
ConnectionState,
PacketDirection
} from 'packet-events-js';
class MyCustomPacket extends Packet {
static get packetId() { return 0x50; }
static get packetName() { return 'MyPacket'; }
constructor() {
super();
this.myField = 0;
}
read(buffer, context) {
this.myField = buffer.readVarInt();
}
write(buffer, context) {
buffer.writeVarInt(this.myField);
}
}
defaultRegistry.register({
packetClass: MyCustomPacket,
state: ConnectionState.PLAY,
direction: PacketDirection.CLIENTBOUND,
packetId: 0x50
});Advanced Features
Packet Recorder
Record, export, import, and replay complete packet streams:
import { PacketRecorder, PacketReplayer, PacketRecord } from 'packet-events-js';
const recorder = new PacketRecorder({
includeRaw: true,
maxRecords: 10000,
filter: (packet) => true
});
recorder.start();
client.packetManager.on('packetReceive', (event) => {
recorder.record(event.packet, 'CLIENTBOUND', 'PLAY', event.rawData);
});
const stats = recorder.getStats();
console.log(`Recorded: ${stats.totalPackets} packets, ${stats.totalBytes} bytes`);
console.log(`Duration: ${stats.duration}ms`);
console.log(`Packets by type:`, stats.packetsByType);
const exported = recorder.export();
const json = JSON.stringify(exported, null, 2);
const imported = recorder.import(JSON.parse(json));
const replayer = new PacketReplayer(recorder.records, {
speed: 1.0,
loop: false,
onPacket: (record) => {
console.log(`Replaying: ${record.packetName}`);
}
});
await replayer.start();
replayer.pause();
replayer.resume();
replayer.stop();Protocol Analyzer
Deep protocol analysis with metrics and traffic visualization:
import { ProtocolAnalyzer, ProtocolMetrics, PacketInspector } from 'packet-events-js';
const analyzer = new ProtocolAnalyzer({
trackHistory: true,
historySize: 1000,
alertThresholds: {
latency: 500,
packetLoss: 0.1,
throughputMin: 1000
}
});
analyzer.on('alert', (alert) => {
console.log(`Alert: ${alert.type} - ${alert.message}`);
});
analyzer.start();
client.packetManager.on('packetReceive', (event) => {
analyzer.analyzePacket(event.packet, 'CLIENTBOUND', 'PLAY', event.rawData);
});
analyzer.recordLatency(50);
const report = analyzer.getReport();
console.log(`Packets: ${report.packets.total}`);
console.log(`Bytes: ${report.bytes.total}`);
console.log(`Avg Latency: ${report.latency.average}ms`);
const graph = analyzer.getTrafficGraph();
console.log(graph);
const metrics = new ProtocolMetrics();
metrics.recordLatency(100);
metrics.recordPacketLoss(1, 100);
console.log(`Avg Latency: ${metrics.getAverageLatency()}ms`);
console.log(`Packet Loss: ${metrics.getPacketLoss() * 100}%`);
const inspector = new PacketInspector();
const hexDump = inspector.hexDump(packetBuffer);
console.log(hexDump);
const structure = inspector.analyzeStructure(packet);
console.log(structure);Middleware System
Plugin-based packet processing pipeline with built-in middleware:
import {
MiddlewarePipeline,
LoggingMiddleware,
RateLimitMiddleware,
ValidationMiddleware,
TransformMiddleware,
CacheMiddleware,
MetricsMiddleware
} from 'packet-events-js';
const pipeline = new MiddlewarePipeline();
pipeline.use(new LoggingMiddleware({
logLevel: 'debug',
includePayload: true,
filter: (ctx) => ctx.packet.constructor.packetName !== 'KeepAlive'
}));
pipeline.use(new RateLimitMiddleware({
maxPackets: 100,
windowMs: 1000,
onLimit: (ctx) => console.log('Rate limited!')
}));
pipeline.use(new ValidationMiddleware({
rules: {
'ChatMessage': (packet) => packet.message.length <= 256,
'SetPlayerPosition': (packet) => Math.abs(packet.y) < 320
},
onInvalid: (ctx, rule) => console.log(`Invalid: ${rule}`)
}));
pipeline.use(new TransformMiddleware({
transforms: {
'ChatMessage': (packet) => {
packet.message = packet.message.trim();
return packet;
}
}
}));
const metricsMiddleware = new MetricsMiddleware();
pipeline.use(metricsMiddleware);
pipeline.use(async (ctx, next) => {
console.log(`Before: ${ctx.packet.constructor.packetName}`);
await next();
console.log(`After: processing time ${ctx.processingTime}ms`);
});
client.packetManager.on('packetReceive', async (event) => {
const ctx = await pipeline.process({
packet: event.packet,
direction: 'CLIENTBOUND',
state: 'PLAY'
});
if (ctx.cancelled) {
event.cancel();
}
});
const metrics = metricsMiddleware.getMetrics();
console.log(`Processed: ${metrics.processed} packets`);
console.log(`Blocked: ${metrics.blocked} packets`);Connection Manager
Advanced connection management with auto-reconnect, health monitoring, and connection pooling:
import {
AutoReconnect,
ReconnectStrategy,
HealthMonitor,
HealthStatus,
ConnectionPool,
CircuitBreaker
} from 'packet-events-js';
const reconnect = new AutoReconnect({
strategy: ReconnectStrategy.EXPONENTIAL,
baseDelay: 1000,
maxDelay: 30000,
maxAttempts: 10,
jitter: true,
onReconnect: (attempt) => console.log(`Reconnecting... attempt ${attempt}`),
onMaxAttempts: () => console.log('Max reconnect attempts reached'),
onSuccess: () => console.log('Reconnected!')
});
client.on('disconnect', async () => {
await reconnect.attempt(async () => {
await client.connect();
await client.login();
});
});
const healthMonitor = new HealthMonitor({
latencyThreshold: 200,
packetLossThreshold: 0.05,
checkInterval: 5000
});
healthMonitor.on('statusChange', (status, previousStatus) => {
console.log(`Health: ${previousStatus} -> ${status}`);
});
healthMonitor.on('alert', (alert) => {
console.log(`${alert.type}: ${alert.message}`);
});
client.packetManager.on('packetReceive', (event) => {
if (event.packet.constructor.packetName === 'KeepAliveClientbound') {
healthMonitor.recordLatency(Date.now() - event.timestamp);
}
});
const pool = new ConnectionPool({
minConnections: 2,
maxConnections: 10,
strategy: 'least-loaded',
healthCheck: async (conn) => conn.isConnected,
healthCheckInterval: 30000
});
await pool.add('lobby', createClient('lobby.server.com'));
await pool.add('game1', createClient('game1.server.com'));
await pool.add('game2', createClient('game2.server.com'));
const { id, connection } = pool.get();
await connection.sendPacket(packet);
pool.release(id);
const breaker = new CircuitBreaker({
failureThreshold: 5,
successThreshold: 3,
timeout: 30000,
onOpen: () => console.log('Circuit OPEN - requests blocked'),
onClose: () => console.log('Circuit CLOSED - requests allowed'),
onHalfOpen: () => console.log('Circuit HALF-OPEN - testing')
});
try {
await breaker.execute(async () => {
await client.sendPacket(packet);
});
} catch (error) {
if (breaker.isOpen) {
console.log('Circuit is open, request blocked');
}
}Project Structure
PacketEvents-JS/
├── src/
│ ├── index.js
│ ├── auth/
│ │ ├── AuthHandler.js
│ │ └── MojangAPI.js
│ ├── client/
│ │ └── MinecraftClient.js
│ ├── crypto/
│ │ └── Encryption.js
│ ├── events/
│ │ ├── EventEmitter.js
│ │ └── PacketEvent.js
│ ├── manager/
│ │ └── PacketManager.js
│ ├── protocol/
│ │ ├── ConnectionState.js
│ │ ├── PacketDirection.js
│ │ ├── ProtocolVersion.js
│ │ ├── types/
│ │ │ ├── VarInt.js
│ │ │ ├── UUID.js
│ │ │ ├── Position.js
│ │ │ ├── NBT.js
│ │ │ └── TextComponent.js
│ │ └── packets/
│ │ ├── Packet.js
│ │ ├── PacketRegistry.js
│ │ ├── handshake/
│ │ ├── status/
│ │ ├── login/
│ │ └── play/
│ ├── advanced/
│ │ ├── PacketRecorder.js
│ │ ├── ProtocolAnalyzer.js
│ │ ├── MiddlewareSystem.js
│ │ └── ConnectionManager.js
│ └── utils/
│ ├── PacketBuffer.js
│ └── Logger.js
└── examples/
├── authentication.js
├── basic-client.js
├── connection-flows.js
├── custom-packets.js
├── data-types.js
├── encryption.js
├── event-system.js
├── packet-buffer.js
├── packet-interception.js
├── packet-types.js
├── protocol-versions.js
└── server-ping.jsMain API
MinecraftClient
const client = new MinecraftClient({
host: string,
port: number,
version: ProtocolVersion,
username: string,
offline: boolean,
accessToken: string,
selectedProfile: object,
registry: PacketRegistry
});
await client.connect();
await client.ping();
await client.login();
await client.sendPacket(packet);
await client.disconnect();
client.isConnected;
client.isReady;
client.uuid;
client.playerName;PacketBuffer
import { PacketBuffer } from 'packet-events-js';
const buffer = PacketBuffer.writer();
buffer.writeBoolean(true);
buffer.writeByte(42);
buffer.writeShort(1000);
buffer.writeInt(100000);
buffer.writeLong(9999999999n);
buffer.writeFloat(3.14);
buffer.writeDouble(3.14159265);
buffer.writeVarInt(12345);
buffer.writeString("Hello Minecraft");
buffer.writeUUID(uuid);
buffer.writePosition(position);
buffer.writeNBT(nbtCompound);
const data = buffer.getBuffer();
const reader = PacketBuffer.reader(data);
const bool = reader.readBoolean();
const num = reader.readVarInt();
const str = reader.readString();MinecraftEncryption
import { MinecraftEncryption, EncryptedConnection } from 'packet-events-js';
const keyPair = MinecraftEncryption.generateKeyPair();
const verifyToken = MinecraftEncryption.generateVerifyToken();
const sharedSecret = MinecraftEncryption.generateSharedSecret();
MinecraftEncryption.encryptRSA(publicKey, data);
MinecraftEncryption.decryptRSA(privateKey, data);
MinecraftEncryption.encryptAES(secret, data);
MinecraftEncryption.decryptAES(secret, data);
MinecraftEncryption.computeServerIdHash(serverId, sharedSecret, publicKey);
const conn = new EncryptedConnection(sharedSecret);
conn.enable();
const encrypted = conn.encrypt(data);
const decrypted = conn.decrypt(data);AuthHandler
import { AuthHandler, ClientAuthHandler, AuthMode, BungeeCordForwarding, VelocityForwarding } from 'packet-events-js';
AuthMode.ONLINE;
AuthMode.OFFLINE;
AuthMode.BUNGEECORD;
AuthMode.VELOCITY;
const handler = new AuthHandler({ mode: AuthMode.ONLINE });
handler.initialize();
handler.getPublicKey();
handler.generateVerifyToken();
handler.generateOfflineUUID(username);
const bungeAuth = new AuthHandler({ mode: AuthMode.BUNGEECORD });
const result = bungeAuth.handleBungeeCordForwarding(serverAddress);
const velocityAuth = new AuthHandler({
mode: AuthMode.VELOCITY,
velocitySecret: 'your-secret'
});
const result2 = velocityAuth.handleVelocityForwarding(loginPluginData);EventEmitter
import { EventPriority } from 'packet-events-js';
EventPriority.LOWEST;
EventPriority.LOW;
EventPriority.NORMAL;
EventPriority.HIGH;
EventPriority.HIGHEST;
EventPriority.MONITOR;
emitter.on('event', callback, { priority: EventPriority.HIGH });
emitter.on('packet', (event) => {
event.cancel();
});
await emitter.emitAsync('event', data);TextComponent
import { TextComponent, ChatColor } from 'packet-events-js';
const message = TextComponent.text('Hello!')
.color(ChatColor.GREEN)
.bold()
.append(
TextComponent.text(' World')
.color(ChatColor.YELLOW)
.italic()
)
.clickEvent('run_command', '/help')
.showText(TextComponent.text('Click for help'));
const json = message.toString();Supported Versions
66 versions supported from 1.7.2 to 1.21.11:
| Range | Versions | |-------|----------| | 1.21.x | 1.21.11, 1.21.10, 1.21.9, 1.21.8, 1.21.7, 1.21.6, 1.21.5, 1.21.4, 1.21.3, 1.21.2, 1.21.1, 1.21 | | 1.20.x | 1.20.6, 1.20.5, 1.20.4, 1.20.3, 1.20.2, 1.20.1, 1.20 | | 1.19.x | 1.19.4, 1.19.3, 1.19.2, 1.19.1, 1.19 | | 1.18.x | 1.18.2, 1.18.1, 1.18 | | 1.17.x | 1.17.1, 1.17 | | 1.16.x | 1.16.5, 1.16.4, 1.16.3, 1.16.2, 1.16.1, 1.16 | | 1.15.x | 1.15.2, 1.15.1, 1.15 | | 1.14.x | 1.14.4, 1.14.3, 1.14.2, 1.14.1, 1.14 | | 1.13.x | 1.13.2, 1.13.1, 1.13 | | 1.12.x | 1.12.2, 1.12.1, 1.12 | | 1.11.x | 1.11.2, 1.11.1, 1.11 | | 1.10.x | 1.10.2, 1.10.1, 1.10 | | 1.9.x | 1.9.4, 1.9.3, 1.9.2, 1.9.1, 1.9 | | 1.8.x | 1.8.9, 1.8.8, 1.8.7, 1.8.6, 1.8.5, 1.8.4, 1.8.3, 1.8.2, 1.8.1, 1.8 | | 1.7.x | 1.7.10, 1.7.9, 1.7.6, 1.7.5, 1.7.4, 1.7.2 |
Examples
See the examples folder for complete usage examples:
basic-client.js- Basic client connectionserver-ping.js- Server list pingpacket-types.js- All packet types demonstrationencryption.js- Encryption usageauthentication.js- Authentication modesevent-system.js- Event system usagepacket-buffer.js- PacketBuffer operationsdata-types.js- Protocol data typesprotocol-versions.js- Protocol version utilitiesconnection-flows.js- Connection flow examplescustom-packets.js- Custom packet creationpacket-interception.js- Packet interception
References
This project is inspired by:
- PacketEvents - Original Java library
- wiki.vg Protocol - Protocol documentation
- MCProtocolLib - Reference implementation
License
MIT License
