A replay system for Minecraft PocketMine-MP servers that records and plays back player sessions with extensive entity and action tracking.
Initially used in my proprietary software, now for public.
- Session Recording: Capture complete player sessions with configurable parameters
- Tape System: Compressed binary storage format for efficient replay data
- Synchronized Playback: Precise timing and synchronization for accurate replays
- Movement Tracking: Comprehensive player movement recording
- Animation Support:
- Block placing and breaking animations
- Attack animations
- Eating animations
- Sprinting animations
- Inventory Tracking: Visual changes for hand, offhand, and armor slots
- Health & Food Monitoring: Real-time health and hunger level tracking
- Projectile Tracking: Movement recording for various projectiles:
- Arrows
- Eggs
- Ender Pearls
- Experience Bottles
- Fishing Hooks
- Potions
- Snowballs
- Firework Rockets
- Special Animations:
- Arrow shake animations
- Firework particle effects
- Block Operations: Placing, breaking, and interaction recording
- Container Support: Chest opening and closing
- World Interactions: Various block interaction types
- Status Effects: Visual changes for potion effects and status modifications
- Attribute Updates: Real-time attribute monitoring and playback
- Skin & Metadata: Player skin and metadata synchronization
- Place the plugin in your
plugins/directory - Restart your server
- Configure the plugin according to your needs
- PocketMine-MP
- libasync (for asynchronous operations, optional)
<?php
use blackjack200\libreplay\Recorder;
use blackjack200\libreplay\tape\WriteOnlyTape;
use function libasync\async;
use function libasync\thread;
// Start recording a player session
$recorder = Recorder::for($plugin, $player);
$recorder->mount(new WriteOnlyTape());
$recorder->start();
// Session is now being recorded...
// Eject tape from the recorder, stopping the recorder.
$tape = $recorder->eject();
// Write compressed tape data to `tape.bin`
async(function() use ($tape) {
$compressed = yield from $tape->awaitCompress();
yield from thread(static fn() => file_put_contents('tape.bin', $compressed));
})->panic();<?php
use blackjack200\libreplay\TapePlayer;
use blackjack200\libreplay\tape\ReadonlyTape;
use function libasync\async;
use function libasync\thread;
// Load and play back a recorded session
async(function() use ($plugin, $player) : \Generator {
$tape_data = yield from thread(static fn() => file_get_contents('tape.bin'));
$tape = yield from ReadonlyTape::readCompressedAsync($tape_data);
$player = new TapePlayer();
$player->play(
plugin: $plugin,
world: $player->getWorld(),
tape: $tape,
viewers: [$player],
on_finish: static fn() => $player->sendMessage("Playback completed"),
start_time: 0,
speed: 1.0
);
})->panic();<?php
use blackjack200\libreplay\actor\projectile\ProjectileActorFactory;
// Register custom projectile types for recording
ProjectileActorFactory::register(
name: "minecraft:custom_projectile_actor",
class: CustomProjectileActor::class
);