Papyrus is a Minecraft server software fork of Paper. It keeps full compatibility with the Paper plugin ecosystem (io.papermc.paper API, Paper plugins, and existing configs) while adding first-class options for vanilla parity and performance tuning.
Repository: github.com/codingsushi79/Papyrus
Documentation: docs.sushii.dev/papyrus
- What Papyrus adds
- Requirements
- Quick start
- Running in production
- Configuration
- Papyrus-specific options
- Integrated anticheat
- Performance tuning
- Vanilla compatibility preset
- Redstone presets
- Building from source
- Project structure
- Plugin development
- Continuous integration
- Contributing
- License
- Credits
- FAQ
Paper optimizes Minecraft in ways that sometimes diverge from vanilla behavior. Papyrus makes those trade-offs explicit and configurable instead of fixed.
| Area | Paper behavior | Papyrus change |
|---|---|---|
| Entity RNG | Shared random source across all entities (faster) | Configurable: SHARED or VANILLA per-entity RNG |
| Redstone | Vanilla by default; optional fast engines | Same engines, documented presets for vanilla vs tech servers |
| Experience orbs | Paper defaults | Configurable despawn, pickup radius, merge radius, and merge disable |
| Performance defaults | Paper defaults | Tuned defaults for chunk I/O, explosions, hoppers, idle worlds, JVM/Netty |
| Update checker | Checks PaperMC | Disabled by default (fork-specific builds) |
| Anticheat | External plugins (GrimAC, etc.) | Built-in configurable engine (x-ray heuristics, reach, rates) |
Everything else — Moonrise chunk system, incremental saves, hopper optimizations, plugin API, and the patch-based build — comes from upstream Paper unchanged.
- Java 21+ to run the server jar (Java 25 recommended)
- 2 GB RAM minimum for small servers; 8 GB+ recommended for production
- A machine capable of running a Paper-based server (same as Paper)
- Git — you must clone the repository; zip downloads will not build
- Java 25 JDK to compile (Gradle can auto-provision this via toolchains if you only have JRE 21+)
- ~4 GB RAM for Gradle during compilation
Current Minecraft version: 26.1.2 (see gradle.properties).
From releases: Download Papyrus-<mcVersion>.jar (e.g. Papyrus-26.1.2.jar) from GitHub Releases or see Documentation → Download.
From CI: Download the papyrus-server artifact from the latest successful GitHub Actions build.
From source:
git clone https://github.com/codingsushi79/Papyrus.git
cd Papyrus
./gradlew applyPatches createPaperclipJar syncPapyrusPaperclipJarThe runnable jar is at:
papyrus-server/build/distributions/papyrus-paperclip-<version>.jar
mkdir papyrus-server && cd papyrus-server
cp ../Papyrus/papyrus-server/build/distributions/papyrus-paperclip-*.jar .
java -jar papyrus-paperclip-*.jarAccept the EULA by editing eula.txt, then start again. Papyrus generates configs on first boot:
config/
papyrus-global.yml # Global server settings
papyrus-world-defaults.yml # Defaults for all worlds
world/
papyrus-world.yml # Per-world overrides (optional)
spigot.yml # Spigot settings (activation ranges, etc.)
bukkit.yml
server.properties
Papyrus includes a production start script with recommended G1GC flags:
# Copy the jar into your server directory first
cp /path/to/papyrus-paperclip-*.jar ./papyrus-paperclip.jar
# From the repo (or copy scripts/start.sh into your server dir)
JAR=papyrus-paperclip.jar ./scripts/start.shEnvironment variables:
| Variable | Default | Purpose |
|---|---|---|
JAVA |
java |
Path to Java binary |
JAR |
papyrus-paperclip.jar |
Server jar filename |
PAPYRUS_AUTO_UPDATE |
1 |
Set to 0 to skip pre-start GitHub release checks |
Papyrus can keep itself up to date for the same Minecraft version using GitHub Releases (Papyrus-<mcVersion>.jar assets).
Recommended: start the server with scripts/start.sh. On each launch it will:
- Apply a pending
*.updatejar downloaded earlier (backup saved as*.backup) - Check GitHub for a newer release for this MC version and download it as
*.updateif needed - Start the server — restart again to run the newly downloaded build
While the server is running, Papyrus also checks releases on startup when enabled in config:
# config/papyrus-global.yml
update-checker:
enabled: true # notify / check GitHub releases
auto-download: true # download *.update next to the running jarSet PAPYRUS_AUTO_UPDATE=0 to disable the pre-start script check, or set update-checker.enabled: false to disable the in-server check.
Manual update check:
./scripts/update-papyrus.sh papyrus-paperclip.jarRequires curl, jq, and unzip for the shell updater.
Edit -Xms8G -Xmx8G in the script to match your host. Always set -Xms equal to -Xmx to avoid heap resize pauses during gameplay.
If you prefer not to use the script:
java -Xms8G -Xmx8G \
-XX:+UseG1GC -XX:+ParallelRefProcEnabled -XX:MaxGCPauseMillis=200 \
-XX:+UnlockExperimentalVMOptions -XX:+DisableExplicitGC -XX:+AlwaysPreTouch \
-XX:G1NewSizePercent=30 -XX:G1MaxNewSizePercent=40 \
-XX:G1HeapRegionSize=8M -XX:G1ReservePercent=20 \
-XX:InitiatingHeapOccupancyPercent=15 -XX:MaxTenuringThreshold=1 \
-jar papyrus-paperclip.jar noguiPaper's Aikar flags documentation remains a good reference for tuning beyond these defaults.
Optional JVM flags for advanced tuning:
| Property | Example | Effect |
|---|---|---|
Papyrus.WorkerThreadCount |
-DPapyrus.WorkerThreadCount=4 |
Override Moonrise worker thread count |
Papyrus.NumaScheduling |
-DPapyrus.NumaScheduling=true |
NUMA-aware threading on multi-socket hosts |
Papyrus.MaxViewDistance |
-DPapyrus.MaxViewDistance=32 |
Hard cap on view distance |
Papyrus uses Paper's layered config system. Files are YAML; keys use kebab-case.
| File | Scope |
|---|---|
config/papyrus-global.yml |
Server-wide settings |
config/papyrus-world-defaults.yml |
Defaults applied to every world |
<world>/papyrus-world.yml |
Overrides for a specific world |
spigot.yml |
Entity activation/tracking ranges, hopper rates, Netty threads |
bukkit.yml |
Spawn limits, chunk settings |
server.properties |
Port, gamemode, difficulty, etc. |
After editing configs, restart the server or use /papyrus reload where supported (some settings require a full restart).
For Papyrus-specific options and presets, see docs.sushii.dev/papyrus. For the full Paper config reference, see docs.papermc.io. The sections below cover Papyrus-specific behavior and recommended values.
File: config/papyrus-global.yml
performance:
entity-random-source: SHARED # or VANILLA
apply-runtime-jvm-defaults: true # Netty buffer caps, JNA nosys
netty-threads: -1 # -1 = auto (4–8 based on CPU)| Value | Behavior | Use when |
|---|---|---|
SHARED |
All entities share one random source (Paper default, faster) | Performance matters; enchantment seed manipulation is not needed |
VANILLA |
Each entity gets RandomSource.create() like vanilla |
Speedrunners, enchantment seed manipulation, or any vanilla-like RNG dependency |
File: config/papyrus-world-defaults.yml or <world>/papyrus-world.yml
environment:
experience-orb-despawn-rate: 6000 # ticks (vanilla: 6000)
experience-orb-pickup-radius: 8.0 # blocks (vanilla: 8)
entities:
spawning:
experience-orb-merge-radius: 1.0 # merge search radius in blocks
behavior:
disable-experience-orb-merge: falseLower experience-orb-pickup-radius to reduce orb lag on grinder servers. Set disable-experience-orb-merge: true if you want orbs to stay separate.
Lower experience-orb-pickup-radius to reduce orb lag on grinder servers. Set disable-experience-orb-merge: true if you want orbs to stay separate.
File: config/papyrus-world-defaults.yml or <world>/papyrus-world.yml
misc:
redstone-implementation: VANILLA # VANILLA | EIGENCRAFT | ALTERNATE_CURRENT
alternate-current-update-order: HORIZONTAL_FIRST_OUTWARD # only for ALTERNATE_CURRENT| Engine | Speed | Vanilla parity |
|---|---|---|
VANILLA |
Baseline | Full — same update order as Minecraft |
EIGENCRAFT |
~95% fewer wire updates on depower | Different update order; fixes MC-11193 |
ALTERNATE_CURRENT |
Fastest in most benchmarks | Different update order; configurable direction |
Default is VANILLA. Switch to EIGENCRAFT or ALTERNATE_CURRENT only on redstone-heavy technical servers where speed matters more than vanilla timing.
File: config/papyrus-global.yml
chunk-system:
io-threads: -1 # auto-detect from CPU count (recommended)
worker-threads: -1 # auto-detect from CPU count (recommended)Papyrus auto-scales I/O threads to min(4, max(1, cpu_cores / 4)) when set to -1 or 0. Paper previously pinned this to a single I/O thread.
Papyrus ships a built-in anticheat engine under config/papyrus-global.yml. It runs at packet level (before actions apply) and does not require a plugin. Disable external anticheat plugins if you use this to avoid duplicate kicks.
anticheat:
engine:
enabled: true
alerts:
console: true
notify-ops: false
punishments:
kick-enabled: true
kick-violation-level: 40
violation-decay-per-second: 2.0
checks:
xray:
enabled: true
window-seconds: 300
suspicious-ore-count: 14
tracked-ores:
- minecraft:diamond_ore
- minecraft:deepslate_diamond_ore
fast-place:
max-per-second: 12
cancel-action: true
fast-break:
max-per-second: 8
inventory:
max-clicks-per-second: 25
hand-swap:
max-swaps-per-second: 8
reach:
block-extra-distance: 0.35
movement:
max-horizontal-blocks-per-tick: 0.85
setback: true
timer:
max-packets-per-second: 140| Check | What it detects |
|---|---|
| xray | Valuable ores mined unusually fast or with suspicious ore-to-stone ratios |
| fast-place / fast-break | Block place/break rates above human limits |
| inventory / hand-swap | Inventory click and offhand swap spam (autoclickers, quick swap macros) |
| reach | Block interactions beyond vanilla interaction range |
| movement | Horizontal/vertical movement per tick above configured limits |
| timer | Incoming packet rate spikes |
Bypass permission: papyrus.anticheat.bypass
Plugin hook: PlayerAnticheatViolationEvent — cancel the event to ignore a flag.
Item component obfuscation (anticheat.obfuscation) and per-world Anti-Xray (anticheat.anti-xray in world config) remain separate features and complement the engine.
These defaults apply to new config files. Existing servers keep their saved values until you change them manually.
| Key | Papyrus default | Effect |
|---|---|---|
chunk-system.io-threads |
auto | Scales chunk load/save throughput with CPU |
performance.entity-random-source |
SHARED |
Fast entity RNG |
performance.apply-runtime-jvm-defaults |
true |
Sets Netty buffer caps and jna.nosys at startup |
performance.netty-threads |
-1 (auto) |
Scales Netty event-loop threads (4–8) when not set in spigot.yml |
spark.enabled |
false |
No Spark profiler overhead |
misc.region-file-cache-size |
512 |
Larger region file cache (uses more RAM) |
update-checker.enabled |
false |
No PaperMC update checks |
| Key | Papyrus default | Effect |
|---|---|---|
misc.update-pathfinding-on-block-update |
false |
Mobs don't repath on every nearby block change |
environment.optimize-explosions |
true |
Faster TNT/creeper blast processing |
environment.experience-orb-despawn-rate |
6000 |
Ticks until XP orbs despawn (vanilla: 6000) |
environment.experience-orb-pickup-radius |
8.0 |
Player pickup range in blocks |
entities.spawning.experience-orb-merge-radius |
1.0 |
Radius for orb merge search |
entities.behavior.disable-experience-orb-merge |
false |
When true, orbs never merge |
unsupported-settings.disable-world-ticking-when-empty |
true |
Worlds with no players stop ticking |
hopper.ignore-occluding-blocks |
true |
Hoppers skip entity scans under solid blocks |
entities.armor-stands.tick |
false |
Armor stands don't tick (display/map servers) |
entities.markers.tick |
false |
Marker entities don't tick |
scoreboards.allow-non-player-entities-on-scoreboards |
false |
Skips scoreboard team lookups for non-players |
chunks.entity-per-chunk-save-limit.* |
capped | Limits arrow/orb/pearl buildup per chunk |
| Key | Papyrus default | Effect |
|---|---|---|
commands.log |
false |
No disk I/O logging every command |
settings.netty-threads |
auto | Scales Netty I/O threads with CPU when unset |
For mob-heavy servers, lower tracking and activation ranges in spigot.yml:
world-settings:
default:
entity-tracking-range:
animals: 48
monsters: 48
misc: 32
entity-activation-range:
animals: 24
monsters: 24
water: 8
villagers: 16Tracking controls network packets; activation controls server-side AI ticks. Lower both before touching game rules.
Use this when vanilla behavior matters more than peak performance (speedrunning, strict survival, enchantment seed work):
# config/papyrus-global.yml
performance:
entity-random-source: VANILLA
# config/papyrus-world-defaults.yml
misc:
redstone-implementation: VANILLA
update-pathfinding-on-block-update: true # restore Paper-like mob repathing
environment:
optimize-explosions: false
entities:
armor-stands:
tick: true
markers:
tick: trueAlso set entity-random-source: VANILLA if players use enchantment table seed manipulation — this is the setting that restores per-entity RNG.
misc:
redstone-implementation: VANILLAmisc:
redstone-implementation: EIGENCRAFTmisc:
redstone-implementation: ALTERNATE_CURRENT
alternate-current-update-order: HORIZONTAL_FIRST_OUTWARDTest contraptions after switching engines. Timing and update order will differ from vanilla.
git clone https://github.com/codingsushi79/Papyrus.git
cd Papyrus
# Apply Minecraft patches and compile
./gradlew applyPatches build
# Create the runnable paperclip jar (copied to papyrus-paperclip-*.jar)
./gradlew createPaperclipJar syncPapyrusPaperclipJarCommon Gradle tasks:
| Task | Purpose |
|---|---|
./gradlew applyPatches |
Apply all Minecraft source patches |
./gradlew build |
Compile and run tests |
./gradlew createPaperclipJar |
Build the downloadable server jar |
./gradlew syncPapyrusPaperclipJar |
Copy paperclip output to papyrus-paperclip-*.jar |
./gradlew rebuildPatches |
Regenerate patch files after editing papyrus-server/src/minecraft |
./gradlew fixupSourcePatches |
Fix patch context after manual edits |
Windows: use gradlew instead of ./gradlew.
Development requires cloning with Git — the build system applies patches against a generated git tree inside papyrus-server/src/minecraft. Downloading a zip from GitHub will not work.
See CONTRIBUTING.md for patch workflow details (inherited from Paper).
Papyrus/
├── papyrus-api/ Public Bukkit/Paper plugin API (Gradle project :paper-api)
├── papyrus-server/ Server implementation (Gradle project :paper-server)
│ ├── patches/ Minecraft source patches (sources/, features/, resources/)
│ └── src/main/java/ Papyrus/Paper Java code (io.papermc.paper.*)
├── build-data/ Access wideners and mapping data
├── scripts/
│ └── start.sh Production JVM start script
├── gradle.properties MC version, apiVersion, Maven group
└── .github/workflows/ CI build, tests, and release uploads
Gradle project names stay :paper-api and :paper-server so upstream Paperweight and plugin examples keep working. Physical directories use the papyrus-* prefix.
Maven coordinates remain io.papermc.paper:paper-api for plugin compatibility. The API jar embeds apiVersioning.json (from apiVersion in gradle.properties) for runtime version checks.
Papyrus is API-compatible with Paper plugins. Use the same dependency and plugin metadata format.
Gradle (Kotlin DSL):
repositories {
maven("https://repo.papermc.io/repository/maven-public/")
}
dependencies {
compileOnly("io.papermc.paper:paper-api:26.1.2-R0.1-SNAPSHOT")
}
java {
toolchain.languageVersion.set(JavaLanguageVersion.of(21))
}Maven:
<repository>
<id>papermc</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>26.1.2-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>Replace the version with the current apiVersion from gradle.properties. Papyrus does not publish its own Maven repository; the API matches Paper's published artifact.
name: MyPlugin
version: 1.0.0
main: com.example.myplugin.MyPlugin
api-version: '26.1.2' # must match gradle.properties apiVersionUse api-version matching gradle.properties. Paper plugins use paper-plugin.yml; legacy Bukkit plugins use plugin.yml instead.
import io.papermc.paper.ServerBuildInfo;
import net.kyori.adventure.key.Key;
ServerBuildInfo info = ServerBuildInfo.buildInfo();
if (info.brandId().equals(ServerBuildInfo.BRAND_PAPYRUS_ID)) {
// Running on Papyrus (sushimc:papyrus)
}
if (info.isBrandCompatible(ServerBuildInfo.BRAND_PAPER_ID)) {
// true on both Paper and Papyrus — use for generic Paper-targeted plugins
}dependencies {
compileOnly(project(":paper-api"))
}Run ./gradlew publishToMavenLocal to install paper-api locally, then add mavenLocal() above the PaperMC repository in your plugin project. See CONTRIBUTING.md for running the included test-plugin module.
More plugin guides: docs.sushii.dev/papyrus and docs.papermc.io/paper/dev.
GitHub Actions builds on every push and pull request to main:
- Apply Minecraft patches
- Compile and run tests
- Build the paperclip jar
- Upload CI artifacts (
papyrus-serverjar + test results)
Releases: Pushing a version tag (e.g. v1.0.1) creates a GitHub Release with Papyrus-<mcVersion>.jar attached automatically. No external secrets are required.
Download CI builds from the Actions tab on github.com/codingsushi79/Papyrus.
Papyrus inherits Paper's patch-based development workflow. To contribute:
- Fork codingsushi79/Papyrus
- Clone your fork and create a feature branch from
main - Make changes — Java code in
papyrus-server/src/main/java/directly, Minecraft changes via the patch system - Run
./gradlew buildto verify - Open a pull request against
main
See CONTRIBUTING.md for the full patch workflow. SECURITY.md covers vulnerability reporting.
Papyrus inherits licensing from Paper, Spigot, and CraftBukkit. See LICENSE.md for details.
- Paper by PaperMC — upstream server software
- Spigot / CraftBukkit — original Bukkit implementation
Is Papyrus compatible with Paper plugins?
Yes. Same API package (io.papermc.paper), same Maven artifact (io.papermc.paper:paper-api), same config file names, same paper-plugin.yml format. Gradle modules are named :paper-api / :paper-server but live in papyrus-* directories. Use ServerBuildInfo.BRAND_PAPYRUS_ID only if you need Papyrus-specific behavior.
What's the difference between performance.netty-threads and spigot.yml Netty settings?
performance.netty-threads in papyrus-global.yml sets io.netty.eventLoopThreads when spigot.yml does not override Netty thread count. Set either one, not both, unless you know you need different values.
Will my existing Paper config work?
Yes. Drop in your existing config/, spigot.yml, and worlds. Papyrus-specific defaults only apply to keys that aren't already set.
How do I restore enchantment seed manipulation?
Set performance.entity-random-source: VANILLA in config/papyrus-global.yml and restart.
How do I get vanilla redstone behavior?
Set misc.redstone-implementation: VANILLA in your world config. This is already the default.
Why is the jar named papyrus-paperclip?
The bootstrap tool (paperclip) comes from upstream Paper. Papyrus copies the build output to papyrus-paperclip-*.jar.
Can I use GrimAC or another anticheat plugin alongside Papyrus?
You can, but Papyrus includes a built-in engine (anticheat.engine in papyrus-global.yml). Running both may cause duplicate flags — disable one or set anticheat.engine.enabled: false if you prefer an external plugin.
Where do I report bugs?
Open an issue at github.com/codingsushi79/Papyrus/issues. Specify whether the bug exists in upstream Paper or is Papyrus-specific.
Can I redistribute the built jar?
Yes, under the terms of the GPL/MIT licenses described in LICENSE.md.