Conflux synchronizes client-only player ghosts between separate Paper servers. A player on one copy of a world can see nearby players from other copies moving around with their skin, equipment, pose, and basic animations.
The remote players are packets, not Bukkit entities. They have no collision, hitbox, combat, inventory, persistence, or effect on the local world.
Relay provides the typed Redis Pub/Sub transport. Conflux does not open its own Redis connection, and Redis is the only external service required.
- cross-server position, rotation, and on-ground updates;
- two-tick movement interpolation at the default 10 Hz publication rate;
- signed skin texture synchronization;
- main hand, off hand, and armor synchronization;
- sneaking, sprinting, swimming, and gliding state;
- main-hand swing, off-hand swing, and hurt animations;
- configurable distance filtering and per-viewer limits;
- lightweight movement frames with change-only appearance updates;
- process-session ordering that survives node restarts;
- realm isolation, bounded receive queues, and spatial selection indexing;
/ghosts off|low|medium|highdensity controls; and- automatic removal after a remote node stops publishing.
- Java 25
- Paper 26.2 on every participating backend
- Relay 1.0.0 installed on every participating backend
- one Redis deployment configured through Relay
Every Relay node must have a unique ID and use the same Redis namespace. World names must match across servers for players in those worlds to see one another.
Conflux is Paper-only. Relay may also be installed on Velocity for other applications, but Conflux itself does not need or include a Velocity plugin.
Relay API is published through GitHub Packages. GitHub Packages requires credentials even for public Maven packages. Set GITHUB_ACTOR to your GitHub username and GITHUB_TOKEN to a personal access token (classic) with read:packages, then use the wrapper:
./gradlew lint
./gradlew check jarOn Windows, use .\gradlew.bat. The plugin is written to build/libs/Conflux-<version>.jar.
The build also checks Maven Local before GitHub Packages, which is useful while developing Conflux:
./gradlew :relay-api:publishToMavenLocalThe Gradle dependency is intentionally compileOnly:
repositories {
maven("https://maven.pkg.github.com/IanTapply22/Relay") {
credentials {
username = System.getenv("GITHUB_ACTOR")
password = System.getenv("GITHUB_TOKEN")
}
}
}
dependencies {
compileOnly("com.iantapply:relay-api:1.0.0")
}The equivalent Maven declaration is:
<dependency>
<groupId>com.iantapply</groupId>
<artifactId>relay-api</artifactId>
<version>1.0.0</version>
<scope>provided</scope>
</dependency>Do not shade relay-api into Conflux. Relay supplies the API classes at runtime, and Conflux declares Relay as a required Paper dependency with classpath joining enabled.
Pushing a semantic version tag such as v1.2.3 runs the release workflow. It verifies the project, publishes conflux-api and the combined conflux artifact to GitHub Packages, then creates a GitHub Release containing the plugin JAR, SHA-256 checksum, and CycloneDX SBOM. Maven publications are PGP-signed when the SIGNING_KEY and SIGNING_PASSWORD secrets are configured; the release JAR also receives a GitHub build-provenance attestation.
Module-specific Javadocs are collected under a single index and deployed to GitHub Pages from the main branch.
To trigger the release workflow, run:
git tag -a v<version> -m "Conflux <version>"
git push origin v<version>- Install
Relay-<version>.jarandConflux-<version>.jarin every Paper server'spluginsdirectory. - Start each server once.
- Configure Relay with a unique Paper node ID and the same Redis namespace on every server.
- Configure Conflux's display limits if desired.
- Restart the servers and run
/relay statusfollowed by/conflux.
Conflux obtains its node ID from Relay, so it has no Redis URI or duplicate node configuration.
Paper creates plugins/Conflux/config.yml:
ghosts:
realm-id: default
update-rate-hz: 10
full-snapshot-interval-seconds: 5
selection-period-ticks: 2
view-radius-blocks: 96
maximum-per-viewer: 30
maximum-remote-nodes: 128
stale-after-milliseconds: 1500
maximum-animation-age-milliseconds: 2000
show-equipment: true
teleport-threshold-blocks: 16
tab-list-removal-delay-ticks: 20realm-id isolates logical networks sharing a Relay namespace; only nodes with the same realm see one another. update-rate-hz accepts 1–20. Higher rates are smoother but increase Redis and client packet traffic. maximum-per-viewer accepts 0–200, and the radius accepts 1–512 blocks. Full snapshots repair missed change-only appearance messages, while selection-period-ticks controls how frequently nearby-player membership is recalculated.
Players can choose:
/ghosts off
/ghosts low
/ghosts medium
/ghosts high
These preferences last until the player disconnects. high uses the configured maximum. Administrators with conflux.admin can use /conflux to see the Relay node ID, known remote players, and currently rendered ghost count.
Paper A Redis / Relay Paper B
real players -- movement -----> conflux.ghost.movement.v2 ---\
appearance --- when changed --> conflux.ghost.appearance.v2 ---+-> remote state store
recovery ----- full snapshot -> conflux.ghost.frame.v2 --------/
animations ---- event packet -> conflux.ghost.animation.v2 ------> packet-only players
|
nearby local viewers
Each Paper process generates a random session ID. Sequence ordering applies within that session, so a restarted node can begin again without waiting for its old sequence to expire. Messages also carry an explicit protocol version and configured realm ID.
Each node publishes lightweight movement snapshots through Destination.paperServers(). Skin and equipment data is cached and sent when it changes, with a periodic complete snapshot for recovery. Receiving nodes merge the streams, discard stale or replayed data, index players by world and spatial cell, and create client-only player entities for nearby local viewers.
Frames are deliberately transient. A missed frame is replaced by the next one, so no durable queue or database is needed. Empty shutdown frames remove ghosts immediately; local staleness removal handles crashes and network partitions.
Protocol v2 uses new Relay topic names. Upgrade all participating Conflux servers together when moving from a v1 release; mixed v1/v2 nodes intentionally do not exchange ghost state.
The packet renderer targets Paper 26.2's Mojang-mapped internals. Minecraft protocol changes can require corresponding Conflux updates.
| Module | Responsibility |
|---|---|
conflux-api |
Ghost frame, equipment, and animation wire contracts |
conflux-platform-paper |
Relay integration, state capture, filtering, interpolation, and packet rendering |
conflux-distribution |
Paper plugin JAR without Relay API classes |
Contributions are welcome under CONTRIBUTING.md. Report suspected vulnerabilities privately as described in SECURITY.md.
Conflux is licensed under the GNU Affero General Public License v3.0 or later.