Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
d85ce79
Add -ipv6 flag so IPv6 works under PowerShell.
jhead Jul 26, 2026
770943d
Fix stuck sessions when upstream UDP write returns connection refused.
jhead Jul 26, 2026
d0eb0e7
Fix UDP socket exhaustion from self-echoed LAN pings.
jhead Jul 26, 2026
15bbc4c
Always inject bind port into LAN pongs for Geyser.
jhead Jul 26, 2026
1516d53
Fix multi-server LAN discovery by fanning out from one :19132.
jhead Jul 26, 2026
dad4e29
Document Pi ARM binary selection; pin arm64 to GOARM64=v8.0.
jhead Jul 26, 2026
d86301a
Fix silent UDP truncation that stalls resource-pack loading.
jhead Jul 26, 2026
b35661a
Add cross-version compatibility harness (T0 + T1)
jhead Jul 26, 2026
91a6689
Fix false OfflinePong after a single UDP read timeout.
jhead Jul 26, 2026
11c7d7a
Fix LAN discovery stuck after console disconnect.
jhead Jul 26, 2026
2f4b27d
Fix iSH: linux/386 target and reuseport EINVAL fallback.
jhead Jul 26, 2026
654eaf5
Document Termux ARM Linux binary selection.
jhead Jul 26, 2026
66e7b18
Merge branch 'fix/issue-138-pi-arm-binary-docs' into dev
jhead Jul 26, 2026
a89d0a6
Merge branch 'fix/issue-111-termux-arm-docs' into dev
jhead Jul 26, 2026
5a6ee13
Merge branch 'fix/issue-124-ipv6-flag' into dev
jhead Jul 26, 2026
091b25e
Merge branch 'fix/issue-108-ish-linux-x86' into dev
jhead Jul 26, 2026
c39c47e
Merge branch 'fix/issue-161-geyser-motd-ports' into dev
jhead Jul 26, 2026
cfa7458
Merge branch 'fix/issue-79-udp-write-connrefused' into dev
jhead Jul 26, 2026
da0fd71
Merge branch 'fix/issue-104-false-offline-timeout' into dev
jhead Jul 26, 2026
386be07
Restore offlineErrorRegex dropped during merge.
jhead Jul 26, 2026
e88b1d2
Merge branch 'fix/issue-128-udp-recv-buffer' into dev
jhead Jul 26, 2026
80f5735
Merge branch 'fix/issue-117-discovery-ping-session' into dev
jhead Jul 26, 2026
4486019
Merge branch 'fix/issue-184-too-many-open-files' into dev
jhead Jul 26, 2026
666c036
Merge fix/issue-175-multi-instance-discovery into dev
jhead Jul 26, 2026
7a32535
Finish multi-server merge: nil-safe Close and discovery hub tests.
jhead Jul 26, 2026
652a62e
ci: use a current Go toolchain for the macOS T0 job
jhead Jul 26, 2026
e8cd02a
docs: rewrite DESIGN.md as a standalone strategy document
jhead Jul 26, 2026
245092e
Rename test tiers from T0/T1 to unit/e2e
jhead Jul 26, 2026
b65bb70
docs: drop the repository layout section
jhead Jul 26, 2026
13f565b
Merge dev and remove the known-failure registry
jhead Jul 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
name: CI

on:
push:
branches: [master, main, dev]
pull_request:

permissions:
contents: read

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
# Pins the toolchain to go.mod's declared minimum, so accidental use of a
# newer language feature is caught. Linux only, see the note on the unit job.
vet:
name: vet + race
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true

- name: gofmt
run: |
unformatted="$(gofmt -l ./cmd ./internal ./test)"
if [ -n "$unformatted" ]; then
echo "These files need gofmt:"
echo "$unformatted"
exit 1
fi

- name: go vet
run: go vet ./...

- name: go test -race
run: go test -race ./...

# Unit tests over the cross-version corpus. Pure Go, no network, no Node, ~1s.
# The OS matrix is nearly free here and catches path, endianness and
# line-ending surprises that only ever show up off Linux.
unit:
name: unit (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
# A current toolchain, NOT go.mod's minimum, because macos-latest is
# now macos-26 and its dyld refuses binaries without an LC_UUID load
# command. Go's internal linker only started emitting one in 1.26
# (golang/go#69987, golang/go#78012), so building this repo with Go
# 1.21 on macOS 26 fails before any phantom code runs - including on
# pre-existing tests like internal/util.
#
# go.mod's directive stays at the true LANGUAGE minimum and is still
# exercised by the `vet` job on Linux, where LC_UUID is irrelevant.
go-version: 'stable'
cache: true

- name: Corpus + rewrite tests
run: go test ./internal/...

- name: Fuzz the pong parser (short)
if: matrix.os == 'ubuntu-latest'
run: go test ./internal/proto/ -run=Fuzz -fuzz=FuzzReadUnconnectedPing -fuzztime=30s

# End-to-end tests against a real phantom subprocess.
#
# Sharded for ISOLATION as much as for speed: phantom binds UDP :19132
# unconditionally with SO_REUSEPORT, so two concurrent instances on one host
# silently load-balance each other's datagrams. One shard per runner gives
# each its own network stack; cases run serially within a shard.
e2e:
name: e2e (shard ${{ matrix.shard }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
shard: [0, 1, 2, 3]
env:
PHANTOM_SHARD: ${{ matrix.shard }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true
- uses: actions/setup-node@v4
with:
node-version: '22'
cache: npm
cache-dependency-path: test/compat/node/package-lock.json

- name: Install bedrock-protocol
working-directory: test/compat/node
run: npm ci

- name: End-to-end
run: go test -tags='e2e slow node' ./test/compat/... -timeout=20m -v

cross-compile:
name: cross-compile
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true

- name: Build every release target
run: make build
74 changes: 74 additions & 0 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
name: Nightly compatibility

on:
schedule:
- cron: '0 6 * * *'
workflow_dispatch:

permissions:
contents: read

jobs:
# Runs the live tier against EVERY supported version rather than the sampled
# subset CI uses, by clearing PHANTOM_SHARD.
full-sweep:
name: Full version sweep
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true
- uses: actions/setup-node@v4
with:
node-version: '22'
cache: npm
cache-dependency-path: test/compat/node/package-lock.json

- name: Install bedrock-protocol
working-directory: test/compat/node
run: npm ci

- name: Every version, unsharded
run: go test -tags='e2e slow node' ./test/compat/... -timeout=45m -v

extended-fuzz:
name: Extended fuzzing
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true

- name: Fuzz the pong parser (10m)
run: go test ./internal/proto/ -run=Fuzz -fuzz=FuzzReadUnconnectedPing -fuzztime=10m

- name: Upload any crashers
if: failure()
uses: actions/upload-artifact@v4
with:
name: fuzz-corpus
path: internal/proto/testdata/fuzz/

# The mechanism that keeps "N versions" honest over time: when Mojang ships a
# version and bedrock-protocol adds support, this goes red and tells you
# exactly which file to regenerate.
matrix-drift:
name: Version matrix drift
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'

- name: Install generator deps
working-directory: internal/corpus/gen
run: npm install --no-audit --no-fund

- name: Check for new Minecraft versions
working-directory: internal/corpus/gen
run: node drift.js
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
bin/
.DS_Store

# Compatibility harness: Node deps are installed by `npm ci` from the committed
# lockfiles, never committed themselves.
node_modules/
64 changes: 53 additions & 11 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,61 +1,103 @@
SHELL=/bin/bash
.PHONY: prep
.PHONY: prep build build-target clean test test-e2e test-all fuzz corpus drift

OUT=bin/phantom-windows.exe bin/phantom-windows-32bit.exe bin/phantom-macos bin/phantom-macos-arm8 bin/phantom-linux bin/phantom-linux-arm5 bin/phantom-linux-arm6 bin/phantom-linux-arm7 bin/phantom-linux-arm8
# ARM note:
# arm5/arm6/arm7 = 32-bit (GOARCH=arm + GOARM)
# arm8/arm64 = 64-bit aarch64 (GOARCH=arm64). "arm8" does NOT mean
# "any ARMv8 Raspberry Pi" — most Pi OS installs are still
# 32-bit and need arm7 (or arm6 on Pi Zero / Pi 1).
# linux-x86 = GOARCH=386 (iSH on iOS is x86 userspace; ARM/amd64 builds fail there)
OUT=bin/phantom-windows.exe bin/phantom-windows-32bit.exe bin/phantom-macos bin/phantom-macos-arm8 bin/phantom-linux bin/phantom-linux-x86 bin/phantom-linux-arm5 bin/phantom-linux-arm6 bin/phantom-linux-arm7 bin/phantom-linux-arm8 bin/phantom-linux-arm64
CMDSRC=phantom.go

build: prep ${OUT}

bin/phantom-windows.exe:
pushd cmd && \
GOOS=windows GOARCH=amd64 go build -o ../bin/phantom-windows.exe ${CMDSRC} && \
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o ../bin/phantom-windows.exe ${CMDSRC} && \
popd

bin/phantom-windows-32bit.exe:
pushd cmd && \
GOOS=windows GOARCH=386 go build -o ../bin/phantom-windows-32bit.exe ${CMDSRC} && \
CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -o ../bin/phantom-windows-32bit.exe ${CMDSRC} && \
popd

bin/phantom-macos:
pushd cmd && \
GOOS=darwin GOARCH=amd64 go build -o ../bin/phantom-macos ${CMDSRC} && \
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o ../bin/phantom-macos ${CMDSRC} && \
popd

bin/phantom-macos-arm8:
pushd cmd && \
GOOS=darwin GOARCH=arm64 go build -o ../bin/phantom-macos-arm8 ${CMDSRC} && \
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -o ../bin/phantom-macos-arm8 ${CMDSRC} && \
popd

bin/phantom-linux:
pushd cmd && \
GOOS=linux GOARCH=amd64 go build -o ../bin/phantom-linux ${CMDSRC} && \
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ../bin/phantom-linux ${CMDSRC} && \
popd

# Termux on Android uses these linux/arm* targets (not GOOS=android).
# 32-bit x86 for iSH (iOS) and other linux/386 environments.
bin/phantom-linux-x86:
pushd cmd && \
CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -o ../bin/phantom-linux-x86 ${CMDSRC} && \
popd

bin/phantom-linux-arm5:
pushd cmd && \
GOOS=linux GOARCH=arm GOARM=5 go build -o ../bin/phantom-linux-arm5 ${CMDSRC} && \
CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=5 go build -o ../bin/phantom-linux-arm5 ${CMDSRC} && \
popd

bin/phantom-linux-arm6:
pushd cmd && \
GOOS=linux GOARCH=arm GOARM=6 go build -o ../bin/phantom-linux-arm6 ${CMDSRC} && \
CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=6 go build -o ../bin/phantom-linux-arm6 ${CMDSRC} && \
popd

bin/phantom-linux-arm7:
pushd cmd && \
GOOS=linux GOARCH=arm GOARM=7 go build -o ../bin/phantom-linux-arm7 ${CMDSRC} && \
CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build -o ../bin/phantom-linux-arm7 ${CMDSRC} && \
popd

# 64-bit ARM / aarch64 only (64-bit Raspberry Pi OS, etc.)
# GOARM64=v8.0 keeps the baseline compatible with Pi 3/4 (no LSE requirement).
bin/phantom-linux-arm8:
pushd cmd && \
GOOS=linux GOARCH=arm64 go build -o ../bin/phantom-linux-arm8 ${CMDSRC} && \
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 GOARM64=v8.0 go build -o ../bin/phantom-linux-arm8 ${CMDSRC} && \
popd

# Clearer alias for arm8 (same aarch64 binary).
bin/phantom-linux-arm64: bin/phantom-linux-arm8
cp -f bin/phantom-linux-arm8 bin/phantom-linux-arm64

prep:
mkdir -p bin

clean:
rm -rf bin

# Unit tests over the cross-version corpus. Fast, no deps.
test:
go test ./...

# End-to-end tests against a real phantom subprocess.
# Needs `npm ci` in test/compat/node first for the `node` tag to do anything;
# without it those cases skip rather than fail.
test-e2e:
go test -tags='e2e slow node' ./test/compat/... -timeout=20m

test-all: test test-e2e

fuzz:
go test ./internal/proto/ -run=Fuzz -fuzz=FuzzReadUnconnectedPing -fuzztime=60s

# Regenerates the captured pong corpus from real bedrock-protocol servers, one
# per supported Minecraft version. Dev-time only: CI replays the committed
# bytes and never runs this. Commit the diff.
corpus:
cd internal/corpus/gen && npm install --no-audit --no-fund && node capture.js

# Reports Minecraft versions that bedrock-protocol supports but this repo does
# not yet test.
drift:
cd internal/corpus/gen && node drift.js
Loading
Loading