Thank you for your interest in contributing! This document explains the process.
- Code of Conduct
- Development Lifecycle
- How to Contribute
- Development Setup
- Architecture
- Design Principles
- Commit Style
- Pull Request Process
- Testing
- Release Process
- Continuous Improvement
This project follows the Contributor Covenant. Be respectful and constructive.
Every change in Oxidized follows a structured lifecycle:
Identify → Research → Decide → Plan → Test First → Implement → Review → Integrate → Retrospect
The lifecycle ensures that we:
- Understand the problem before writing code
- Write tests before implementation (TDD)
- Actively identify improvements during review
For trivial changes (typo fixes, dependency bumps), an abbreviated lifecycle applies.
| Type | How |
|---|---|
| 🐛 Bug report | Open a bug report issue |
| 💡 Feature request | Open a feature request issue |
| 📖 Docs | Edit any .md file and open a PR |
| 🧩 Implementation | Pick an open issue and open a PR |
| 🔍 Review | Review open PRs and leave constructive feedback |
| 💡 Improvement | Found a better approach? Open an issue or PR |
# 1. Fork and clone
git clone https://github.com/oxidized-mc/server.git
cd Oxidized
# 2. Rust stable (toolchain pinned via rust-toolchain.toml)
rustup update stable
# 3. Build
cargo build
# 4. Run tests
cargo test --workspace
# 5. Check formatting and lints
cargo fmt --check
cargo clippy --workspace --all-targets -- -D warningscargo install cargo-deny # licence + advisory checks
cargo install cargo-nextest # faster test runner
cargo install cargo-watch # auto-rebuild on saveThe workspace has six crates — keep concerns separated:
| Crate | Responsibility | Must NOT depend on |
|---|---|---|
oxidized-nbt |
NBT binary format | all other oxidized crates |
oxidized-macros |
Proc-macro derives | all other oxidized crates |
oxidized-protocol |
Packet codec, connection state | oxidized-world, oxidized-game |
oxidized-world |
Chunks, blocks, Anvil I/O | oxidized-protocol, oxidized-game |
oxidized-game |
Entities, AI, commands | — |
oxidized-server |
Server bootstrap, tick loop | — (depends on all) |
Reference code: The decompiled vanilla server lives in mc-server-ref/decompiled/
(gitignored). When implementing something, always check the Java reference first.
Rewrite idiomatically in Rust — do not transliterate Java line-by-line.
Key principle: the wire protocol is sacred (we can't change what the client sends or expects), but everything server-side uses modern, data-oriented Rust design rather than cloning vanilla Java patterns.
All commits must follow Conventional Commits:
<type>(<scope>): <short description>
| Type | When | Version bump |
|---|---|---|
feat |
New user-visible feature | Minor |
fix |
Bug fix | Patch |
perf |
Performance improvement | Patch |
refactor |
Restructure, no behaviour change | None |
test |
Tests only | None |
docs |
Documentation only | None |
chore |
Dependencies, CI, tooling | None |
ci |
CI/CD workflow changes | None |
Use the crate name as scope: nbt, macros, protocol, world, game, server.
Use ci for workflow files, deps for dependency updates.
feat(protocol): implement VarInt read/write
fix(world): correct PalettedContainer bit packing for edge case
perf(game): cache entity bounding boxes to avoid recomputation
test(nbt): add round-trip tests for all 13 tag types
chore(deps): bump tokio from 1.43 to 1.44
Breaking changes: add ! after type or add BREAKING CHANGE: footer.
- Create a branch:
git checkout -b feat/varint-codec - Make your changes following the lifecycle
- Commit with conventional commits
- Open a PR targeting
main; fill in the PR template completely - At least one approving review is required before merge
- Squash-merge preferred for feature branches
Reviews are not just about catching bugs — they actively seek improvements:
- Does the code follow the project's design principles?
- Could any existing pattern be improved?
- Are there learnings to record?
- Unit tests live next to the code in
#[cfg(test)]modules - Integration tests live in
crates/<name>/tests/ - All public API must have at least one test
- Test modules use
#[allow(clippy::unwrap_used, clippy::expect_used)]for assertion-like code - Reference the Java behaviour when writing expected values:
// VarInt encoding mirrors net.minecraft.network.VarInt in the reference assert_eq!(encode_varint(300), &[0xAC, 0x02]);
Run with nextest for faster feedback:
cargo nextest run --workspaceCriterion benchmarks live in
crates/<crate>/benches/. Run the full suite:
cargo bench --workspaceRun a single crate's benchmarks:
cargo bench -p oxidized-nbt
cargo bench -p oxidized-protocol
cargo bench -p oxidized-world
cargo bench -p oxidized-gameBenchmark results are written to target/criterion/ with HTML reports.
Fuzz targets live in crates/<crate>/fuzz/ and use
cargo-fuzz (requires nightly).
Install cargo-fuzz:
cargo install cargo-fuzzList available targets for a crate:
cd crates/oxidized-nbt && cargo +nightly fuzz list
cd crates/oxidized-protocol && cargo +nightly fuzz list
cd crates/oxidized-world && cargo +nightly fuzz listRun a fuzz target (runs until stopped with Ctrl-C):
cd crates/oxidized-nbt && cargo +nightly fuzz run fuzz_nbt_read
cd crates/oxidized-protocol && cargo +nightly fuzz run fuzz_varint
cd crates/oxidized-protocol && cargo +nightly fuzz run fuzz_packet_decode
cd crates/oxidized-world && cargo +nightly fuzz run fuzz_paletted_containerCrash artifacts are stored in fuzz/artifacts/. If a crash is found, add a
regression test in the corresponding crate's tests/ directory.
Oxidized uses automated versioning and release management based on conventional commits. See the release strategy in the commit history for the full design rationale.
- Commit with conventional prefixes —
feat,fix,perf, etc. - release-please automatically creates and maintains a "Release PR" on GitHub that accumulates changes and proposes the next version bump.
- When a maintainer merges the Release PR, a git tag (
v0.X.Y) and GitHub Release are created automatically. - A build pipeline then compiles cross-platform binaries and attaches them to the release.
Every push to main that passes CI automatically publishes a nightly pre-release with
cross-platform binaries. These are tagged nightly and are always overwritten with the
latest build.
| Commit prefix | Bump |
|---|---|
feat!: or BREAKING CHANGE: footer |
Minor (pre-1.0) / Major (post-1.0) |
feat(scope): |
Minor |
fix(scope):, perf(scope): |
Patch |
refactor, test, docs, chore, ci |
No version bump |
| Platform | Archive |
|---|---|
| Linux x86_64 (glibc) | .tar.gz |
| Linux x86_64 (musl/static) | .tar.gz |
| Windows x86_64 | .zip |
| macOS Intel | .tar.gz |
| macOS Apple Silicon | .tar.gz |
We believe the codebase should always be getting better.
After every milestone:
- Conduct a retrospective
- Record learnings and identify improvements
- Record any technical debt incurred
During every PR review:
- Identify outdated patterns or decisions
- Look for patterns that should be extracted or formalized
- Suggest improvements (not just catch bugs)
When you find something better:
- Don't just note it — act on it
- Open an issue or PR
- Plan and execute the refactoring