Skip to content

A proof-of-concept replay system for Minecraft 1.8 that uses Netty and NMS to record and play back player actions with packet-level accuracy.

License

Notifications You must be signed in to change notification settings

arnaudrmt/echoreplay

Repository files navigation

EchoReplay Banner

Java Spigot API Status License

The spark for EchoReplay came from one particular places: a Hypixel dev blog. They were discussing their replay system and the technical hurdles, specifically how recording raw packets creates massive files. While their optimized approach is impressive, I found myself intrigued by the path not taken. I thought, "How cool would it be to actually build a system that saves everything?"

That's what drove me to create this project. It's a hands-on exploration of a pure, packet-based replay system. The goal wasn't to be the most efficient, but to create a proof-of-concept that could capture and reconstruct a player's session with the highest possible fidelity, using the very data the client sees.

Sticking with Minecraft 1.8 gave me a stable foundation to dig into the server's core. This project was my playground for NMS, a deep dive into Netty, and a fun challenge to create my own serialization format. It’s a developer's log of a fun experiment, built for the love of the challenge.


Showcase: The Replay Experience

The goal was to create a system that could record a gameplay session and play it back with frame-perfect accuracy, making it look as if the original players were still on the server.

Gameplay Moment Description
Replay Showcase Frame-Perfect Playback: The core of the system. Player movements, actions, and animations are recorded tick-by-tick and played back with precision, creating a perfect "ghost" of the original player.
Packet Visualization Packet-Level Fidelity: Instead of relying on high-level server events, EchoReplay hooks directly into the network stream. This allows it to capture the raw packet data for every action, resulting in the most authentic recreation possible as it records and plays back the very data the client itself uses.
Replay Showcase Multi-Player Scene Reconstruction: EchoReplay can record all players on the server simultaneously. During playback, it spawns an NPC for each recorded player, allowing for the complete reconstruction of complex, multi-player interactions.

Dev Log: A Look Behind the Scenes

This project is built on four core architectural pillars. Here’s a breakdown of the design philosophy behind each one.

1. The Listener: Non-Intrusive Packet Interception

To record anything, you first have to listen. The most efficient and non-intrusive way to access the raw data stream is to hook directly into the server's networking layer.

The Solution: Netty Pipeline Injection

Instead of listening for Bukkit events, EchoReplay injects a custom ChannelDuplexHandler directly into each player's Netty pipeline.

  • Injection: When a recording starts (PacketInterceptor.inject()), the system gets the player's underlying Netty Channel. It then adds a custom handler just before the server's main packet_handler.
  • Listening: This handler silently intercepts two key methods:
    1. channelRead(): Fires for every packet coming from the client (e.g., movement, chat, arm swings).
    2. write(): Fires for every packet being sent to the client (e.g., health updates, other entity spawns).
  • Passing Through: After recording the packet, the handler simply calls the next element in the pipeline (super.channelRead()), allowing the server to process the packet as normal. This makes the entire recording process invisible.

2. The Recording Engine & The Ticker

Once a packet is intercepted, it needs to be processed and stored in a structured way. Simply saving a massive list of packets is not enough; they need to be grouped by time to allow for synchronized playback.

The Solution: A Frame-Based, Asynchronous Task

The ReplayRecorder is the heart of the recording process. When a recording starts, it kicks off a BukkitRunnable that acts as a master clock.

  • The "Frame": Every single server tick (1/20th of a second), the task creates a ReplayFrame object. This object is a container, holding the current tick number, a timestamp, and a list of all packets that will be recorded during this tick.
  • Packet Collection: When the PacketInterceptor captures a packet, it doesn't write it to a file immediately. Instead, it serializes the packet into a RecordedPacket object and adds it to the currentFrame.
  • Flushing: At the start of the next tick, the flushFrame() method saves the completed frame to a list in memory and a new, empty frame is created for the next tick. This ensures packets are perfectly grouped in the exact tick they occurred.

This "bucket" approach is critical for ensuring that actions that happen at the same time are played back at the same time.

3. The Blueprint: A Custom .erpl File Format

A replay is only as good as the data it's saved in. To store the recording efficiently, EchoReplay uses a custom binary file format with the .erpl extension. This provides complete control over the data structure and is far more space-efficient than text-based formats like JSON or YAML.

The Solution: Sequential Binary Serialization

The ReplayFileWriter and ReplayFileReader classes manage this format. When a recording is stopped, the file is written in a precise order:

// .erpl Custom File Structure

[HEADER]
  ├─ Magic Bytes ("ECHO")
  ├─ Format Version (int)
  └─ Timestamp (long)

[PLAYER DATA]
  ├─ Player Count (int)
  └─ For each player:
      ├─ UUID, Name, Entity ID
      ├─ Initial Location (World, X, Y, Z, Yaw, Pitch)
      ├─ Initial Health, Inventory, Armor
      └─ Skin Data (Value & Signature)

[FRAME DATA]
  ├─ Frame Count (int)
  └─ For each frame:
      ├─ Tick, Timestamp (longs)
      ├─ Packet Count (int)
      └─ For each packet:
          ├─ Packet Class Name (String)
          ├─ Direction (IN/OUT)
          ├─ Player UUID (who sent/received it)
          └─ Serialized Packet Data (byte[])

This structured, binary approach allows the ReplayFileReader to load an entire recording into memory quickly and reliably, ready for playback.

4. The Playback Engine & The Actor

This is where the data is brought to life. The playback system is designed around a "Director" and an "Actor."

  • The Director (ReplayPlayer): This class is responsible for managing the playback session. It reads the .erpl file, spawns the "Actors," and runs a tick-based BukkitRunnable that serves as the playback clock. Each tick, it retrieves the next ReplayFrame from the loaded data.

  • The Actor (ReplayNPC): This is the entity that re-enacts the recorded player's actions.

    1. Creation: At the start of the replay, a ReplayNPC is spawned for each RecordedPlayer found in the file. It's a true NMS EntityPlayer object, complete with the original player's skin, name, and starting equipment.
    2. Instruction: The ReplayPlayer (the Director) loops through the packets in the current frame and passes them to the appropriate ReplayNPC.
    3. The Translator (PacketInterpreter): The NPC doesn't just blindly handle raw packets. It uses a PacketInterpreter utility class. This is a crucial design choice that acts as an abstraction layer. The interpreter's job is to translate the raw, often obfuscated NMS packet data into meaningful information (e.g., extracting a Location from a PacketPlayInFlying).
    4. Action: A large switch statement in the ReplayPlayer routes the deserialized packet to the correct method on the ReplayNPC (e.g., npc.move(), npc.animateArm(), npc.chat()). The NPC then executes the action, creating a perfect playback of the original event.

This Director/Actor/Translator model keeps the code clean, readable, and highly organized, separating the playback loop from the NMS entity manipulation.


Features

  • Netty Pipeline Injection: A high-performance, non-intrusive method for capturing raw packet data.
  • Frame-Based Recording: A tick-synchronized system that groups packets temporally for perfect playback.
  • Custom Binary File Format (.erpl): An efficient, custom-designed file structure for storing replay data.
  • Full NMS Entity Control: Replays are performed by true EntityPlayer NPCs, allowing for complete control over appearance, equipment, and animations.
  • Packet Serialization/Deserialization: A robust system for converting live NMS packet objects into byte arrays and back.
  • Clean Architectural Design: Separation of concerns using models, handlers, and a PacketInterpreter abstraction layer for readable and maintainable code.

About

A proof-of-concept replay system for Minecraft 1.8 that uses Netty and NMS to record and play back player actions with packet-level accuracy.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages