Skip to content

ImperaZim/LibPacket

Repository files navigation

LibPacket

Description

A comprehensive packet handling library for PocketMine-MP that provides interception, monitoring, filtering, rewriting, profiling, throttling, recording, and testing utilities for network packets.

Features

  • Packet Interception: Register handlers to intercept and cancel or modify incoming and outgoing packets at configurable event priorities.
  • Packet Monitoring: Observe packet flow at MONITOR priority without altering packets, ideal for logging and debugging.
  • Declarative Filtering: Compose chainable conditions to allow or deny packets based on player state, packet data, or custom logic.
  • Packet Rewriting: Modify packet fields in-flight using a fluent builder API with optional conditions.
  • Rate Limiting: Throttle packets per type per player with configurable actions (drop, kick, or log) on violation.
  • Network Profiling: Track real-time per-player and per-type bandwidth and packet volume statistics.
  • Session Recording: Record and replay packet sessions for debugging, saved as JSON files with full timing data.
  • Packet Mocking: Simulate incoming packets and capture outgoing packets for unit testing without a live connection.
  • Batched Delivery: Queue multiple packets for batched delivery on the next tick, reducing per-packet overhead.
  • Statistics Collection: Collect per-type and per-player packet counts with top-N queries and packets-per-second metrics.

How to Use

Include or autoload LibPacket in your PocketMine-MP plugin. No external dependencies are required. Use the LibPacket entry point to create interceptors and monitors:

use imperazim\packet\LibPacket;

// Create an interceptor for modifying/blocking packets
$interceptor = LibPacket::createInterceptor($this);

// Create a monitor for observing packets (read-only)
$monitor = LibPacket::createMonitor($this);

Components

imperazim\packet

Highlights

  • Added LibPacket class in imperazim\packet namespace.

Usage

The main entry point provides factory methods for creating interceptors and monitors:

use imperazim\packet\LibPacket;
use pocketmine\event\EventPriority;

// Default priority (NORMAL), does not handle cancelled events
$interceptor = LibPacket::createInterceptor($this);

// Custom priority and cancelled-event handling
$interceptor = LibPacket::createInterceptor($this, EventPriority::HIGH, handleCancelled: true);

// Monitor (always MONITOR priority)
$monitor = LibPacket::createMonitor($this, handleCancelled: false);

imperazim\packet\handler

Highlights

  • Added PacketHandlerInterface interface in imperazim\packet\handler namespace.
  • Added PacketMonitorHandlerInterface interface in imperazim\packet\handler namespace.
  • Added PacketHandler abstract class in imperazim\packet\handler namespace.
  • Added PacketMonitorHandler abstract class in imperazim\packet\handler namespace.
  • Added PacketHandlerTrait trait in imperazim\packet\handler namespace.

Usage

Extend the abstract classes or implement the interfaces to create packet handlers:

use imperazim\packet\handler\PacketHandler;
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\protocol\TextPacket;

class ChatInterceptor extends PacketHandler {
    public function __construct() {
        parent::__construct([TextPacket::class]);
    }

    public function handle($packet, NetworkSession $session): bool {
        if ($packet instanceof TextPacket && str_contains($packet->message, 'spam')) {
            return false; // Cancel the packet
        }
        return true; // Allow the packet
    }
}

$interceptor = LibPacket::createInterceptor($this);
$interceptor->registerIncoming(new ChatInterceptor());

For monitoring (read-only observation):

use imperazim\packet\handler\PacketMonitorHandler;
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\protocol\MovePlayerPacket;

class MovementLogger extends PacketMonitorHandler {
    public function __construct() {
        parent::__construct([MovePlayerPacket::class]);
    }

    public function handle($packet, NetworkSession $session): void {
        $player = $session->getPlayer();
        if ($player !== null) {
            $this->plugin->getLogger()->debug($player->getName() . " moved");
        }
    }
}

$monitor = LibPacket::createMonitor($this);
$monitor->registerIncoming(new MovementLogger());

imperazim\packet\interceptor

Highlights

  • Added PacketInterceptorInterface interface in imperazim\packet\interceptor namespace.
  • Added PacketInterceptor class in imperazim\packet\interceptor namespace.

Usage

The interceptor manages incoming and outgoing packet handlers. Handlers returning false cancel the packet:

use imperazim\packet\LibPacket;
use imperazim\packet\handler\PacketHandler;
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\protocol\AvailableCommandsPacket;

class CommandFilter extends PacketHandler {
    public function __construct() {
        parent::__construct([AvailableCommandsPacket::class]);
    }

    public function handle($packet, NetworkSession $session): bool {
        // Modify or filter the outgoing command list
        return true;
    }
}

$interceptor = LibPacket::createInterceptor($this);
$interceptor->registerOutgoing(new CommandFilter());

// Later, unregister the handler
$interceptor->unregisterOutgoing($handler);

imperazim\packet\monitor

Highlights

  • Added PacketMonitorInterface interface in imperazim\packet\monitor namespace.
  • Added PacketMonitor class in imperazim\packet\monitor namespace.

Usage

Monitors observe packets at MONITOR priority without modifying them:

use imperazim\packet\LibPacket;

$monitor = LibPacket::createMonitor($this);
$monitor->registerIncoming($movementLogger);
$monitor->registerOutgoing($outgoingObserver);

// Unregister when no longer needed
$monitor->unregisterIncoming($movementLogger);

imperazim\packet\utils

Highlights

  • Added PacketFilter class in imperazim\packet\utils namespace.
  • Added PacketRewriter class in imperazim\packet\utils namespace.
  • Added PacketQueue class in imperazim\packet\utils namespace.
  • Added PacketLogger class in imperazim\packet\utils namespace.
  • Added PacketStatistics class in imperazim\packet\utils namespace.
  • Added PacketUtils class in imperazim\packet\utils namespace.
  • Added TypeValidator class in imperazim\packet\utils namespace.

Usage

PacketFilter -- Declarative packet filtering with composable conditions:

use imperazim\packet\utils\PacketFilter;
use pocketmine\network\mcpe\protocol\MovePlayerPacket;
use pocketmine\world\GameMode;

// Block movement packets from creative-mode players
$filter = PacketFilter::create([MovePlayerPacket::class])
    ->where(fn($pkt, $session) => $session->getPlayer()?->getGamemode() === GameMode::CREATIVE)
    ->deny();

$interceptor->registerIncoming($filter);

// Allow only packets matching a condition
$filter = PacketFilter::create([TextPacket::class])
    ->where(fn($pkt, $session) => !str_contains($pkt->message, 'spam'))
    ->allow();

$interceptor->registerIncoming($filter);

PacketRewriter -- Modify packet fields before delivery:

use imperazim\packet\utils\PacketRewriter;
use pocketmine\network\mcpe\protocol\TextPacket;
use pocketmine\network\mcpe\protocol\SetTimePacket;

// Censor bad words in chat packets
$rewriter = PacketRewriter::create([TextPacket::class])
    ->rewrite(function(TextPacket $packet, $session): void {
        $packet->message = str_replace('bad', '***', $packet->message);
    });

$interceptor->registerIncoming($rewriter);

// Conditional rewriting: force daytime in lobby world
$rewriter = PacketRewriter::create([SetTimePacket::class])
    ->when(fn($pkt, $s) => $s->getPlayer()?->getWorld()->getFolderName() === 'lobby')
    ->rewrite(fn(SetTimePacket $pkt, $s) => $pkt->time = 6000);

$interceptor->registerOutgoing($rewriter);

PacketQueue -- Batched packet delivery on the next tick:

use imperazim\packet\utils\PacketQueue;

$queue = new PacketQueue($this);
$queue->enqueue($player, $packet1);
$queue->enqueue($player, $packet2);
// Both packets are sent together on the next tick

// Broadcast to multiple players
$queue->broadcast([$player1, $player2], $packet);

// Check pending count
$count = $queue->getPendingCount();

// Flush immediately or clear without sending
$queue->flush();
$queue->clear();

PacketLogger -- Rate-limited packet logging:

use imperazim\packet\utils\PacketLogger;
use pocketmine\network\mcpe\protocol\MovePlayerPacket;
use pocketmine\network\mcpe\protocol\TextPacket;

$logger = new PacketLogger(
    [MovePlayerPacket::class, TextPacket::class],
    fn(string $line) => $this->getLogger()->debug($line),
    maxPerSecond: 10,
    includeData: true
);

$monitor->registerIncoming($logger);

// Adjust settings at runtime
$logger->setMaxPerSecond(50);
$logger->setIncludeData(false);

PacketStatistics -- Per-type and per-player packet counting:

use imperazim\packet\utils\PacketStatistics;
use pocketmine\network\mcpe\protocol\MovePlayerPacket;
use pocketmine\network\mcpe\protocol\TextPacket;

$stats = new PacketStatistics([MovePlayerPacket::class, TextPacket::class]);
$monitor->registerIncoming($stats);

// Query statistics
$totals = $stats->getTotals();              // ['MovePlayerPacket' => 1523, ...]
$perPlayer = $stats->getByPlayer('Steve');  // ['MovePlayerPacket' => 42, ...]
$topTypes = $stats->getTopTypes(5);         // Top 5 by count
$pps = $stats->getPacketsPerSecond();       // Packets per second
$grand = $stats->getGrandTotal();           // Total across all types

// Cleanup on player quit
$stats->cleanupPlayer('Steve');

// Reset all data
$stats->reset();

PacketUtils -- Closure signature validation and type reflection:

use imperazim\packet\utils\PacketUtils;
use pocketmine\network\mcpe\protocol\Packet;
use pocketmine\network\mcpe\NetworkSession;

// Validate that a closure has the expected signature
$parsed = PacketUtils::parseClosureSignature(
    function(Packet $packet, NetworkSession $session): bool {
        return true;
    },
    [Packet::class, NetworkSession::class],
    'bool'
);

TypeValidator -- Validate packet classes and resolve their network IDs:

use imperazim\packet\utils\TypeValidator;
use pocketmine\network\mcpe\protocol\TextPacket;
use pocketmine\network\mcpe\protocol\MovePlayerPacket;

$ids = TypeValidator::validatePacketClasses([
    TextPacket::class,
    MovePlayerPacket::class,
]);
// Returns array of unique network IDs

imperazim\packet\profiler

Highlights

  • Added NetworkProfiler class in imperazim\packet\profiler namespace.
  • Added PacketThrottler class in imperazim\packet\profiler namespace.

Usage

NetworkProfiler -- Real-time bandwidth profiling per player and packet type:

use imperazim\packet\profiler\NetworkProfiler;

// Enable profiling
NetworkProfiler::enable();

// Record packets (call from your interceptor/monitor)
NetworkProfiler::recordOutgoing($player, $clientboundPacket);
NetworkProfiler::recordIncoming($player, $serverboundPacket);

// Query stats
$playerStats = NetworkProfiler::getPlayerStats($player); // ['in' => bytes, 'out' => bytes, 'packets' => [...]]
$topPackets = NetworkProfiler::getTopPackets(10);         // Top 10 by volume
NetworkProfiler::isEnabled();                             // bool

// Disable and get summary
$summary = NetworkProfiler::disable();
// $summary = ['duration' => float, 'totalIn' => int, 'totalOut' => int, 'topPackets' => [...]]

// Cleanup on player quit
NetworkProfiler::cleanup($player);

PacketThrottler -- Rate-limit packets per type per player to prevent spam/abuse:

use imperazim\packet\profiler\PacketThrottler;
use pocketmine\network\mcpe\protocol\MovePlayerPacket;
use pocketmine\network\mcpe\protocol\TextPacket;

// Configure limits
PacketThrottler::setLimit(MovePlayerPacket::class, maxPerSecond: 30, action: PacketThrottler::ACTION_DROP);
PacketThrottler::setLimit(TextPacket::class, maxPerSecond: 5, action: PacketThrottler::ACTION_KICK);

// Check in your packet handler
if (!PacketThrottler::allow($player, $serverboundPacket)) {
    return; // Packet was dropped or player was kicked
}

// Query violations and limits
$violations = PacketThrottler::getViolations($player, MovePlayerPacket::class);
$allLimits = PacketThrottler::getLimits();

// Remove a limit
PacketThrottler::removeLimit(TextPacket::class);

// Cleanup on player quit
PacketThrottler::cleanup($player);

imperazim\packet\recorder

Highlights

  • Added PacketRecorder class in imperazim\packet\recorder namespace.

Usage

Record packets from a session for replay and debug analysis:

use imperazim\packet\recorder\PacketRecorder;

// Start recording
PacketRecorder::start($player, "debug_session_1");

// Record packets (call from your interceptor/monitor)
PacketRecorder::recordIncoming($player, $serverboundPacket);
PacketRecorder::recordOutgoing($player, $clientboundPacket);

// Check recording state
PacketRecorder::isRecording($player); // true

// Stop and get recording data
$recording = PacketRecorder::stop($player);

// Save to disk
$path = PacketRecorder::save($recording, $this->getDataFolder() . "recordings/");

// Load a recording
$loaded = PacketRecorder::load($this->getDataFolder() . "recordings/debug_session_1.json");

// Get a summary
$summary = PacketRecorder::summarize($loaded);
// $summary = ['duration' => float, 'packetCount' => int, 'inbound' => int, 'outbound' => int, 'topPackets' => [...]]

// Cleanup on player quit
PacketRecorder::cleanup($player);

imperazim\packet\mock

Highlights

  • Added PacketMocker class in imperazim\packet\mock namespace.

Usage

Simulate packets for testing without a real player connection:

use imperazim\packet\mock\PacketMocker;
use pocketmine\network\mcpe\protocol\MovePlayerPacket;

// Create a mock packet with custom properties
$packet = PacketMocker::create(MovePlayerPacket::class, [
    'actorRuntimeId' => 1,
    'mode' => MovePlayerPacket::MODE_NORMAL,
]);

// Simulate handling an incoming packet
$result = PacketMocker::simulateIncoming($packet, $handler);

// Capture outgoing packets during a callback
$captured = PacketMocker::capture(function() use ($someObject) {
    $someObject->doSomethingThatSendsPackets();
});
// $captured = [ClientboundPacket, ...]

// Manual capture flow
PacketMocker::startCapture();
PacketMocker::record($clientboundPacket);
$packets = PacketMocker::stopCapture();

Licensing information

This project is licensed under MIT. Please see the LICENSE file for details.

About

Packet interception and network packet helper library for PocketMine-MP plugins.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages