-
Notifications
You must be signed in to change notification settings - Fork 8
Architecture
GoCraft is built around a protocol-independent game core with edition-specific network adapters at the boundary.
Java Edition client ───▶ java/ adapter
│
┌──────▼──────┐
│ core/ │ ← no Java or Bedrock imports
│ World │
│ Entity │
│ Player │
└──────┬──────┘
│
Bedrock client ────────▶ bedrock/ adapter
-
core/owns the edition-neutral game state: blocks, chunks, world, entities, players, inventories, and spatial types. It never importsjava/orbedrock/. This is enforced by a compile-time architecture test incore/world/arch_test.go. -
java/reads fromcore/and produces native Java Edition packets: TCP framing, login auth, encryption, chunk encoding, and play-state management. It does not know Bedrock exists. -
bedrock/uses RakNet/UDP and optional Xbox authentication, translates canonical chunks to Bedrock block hashes, posts gameplay intents to the core, and synchronizes shared state back to every Bedrock session. -
server/wires configuration, the core, and the active adapters into the executable.
The canonical block type carries no edition-specific IDs:
// core/world — shared by all adapters
type Block struct {
Namespace string // "minecraft"
Name string // "stone", "grass_block", …
Properties map[string]string // {"snowy": "false"}, nil = default state
}Edition-specific IDs are resolved entirely at the adapter boundary:
Canonical Block
│
┌──────┴──────┐
▼ ▼
Java global Bedrock runtime
state ID ID
Only the encoder packages change when updating Java versions or adding new Bedrock support; core/ is untouched.
Known-packs negotiation and registry delivery sit behind a registry.Provider interface:
type Provider interface {
Packs() []Pack
SendRegistries(conn *network.ClientConn) error
}VanillaProvider uses the Known-Packs shortcut (zero registry packets for vanilla 1.21.4). A future ExplicitProvider will send full registry data for custom dimensions and additional Java versions.
GoCraft is not a protocol translator like Geyser. The Bedrock adapter does not consume Java packets and re-encode them. Both adapters independently read from the same canonical game state and produce their own native wire format:
┌─────────────────────────────┐
│ core/ │
│ World · Entity · Inventory │ ← no Java, no Bedrock
└────────────┬────────────────┘
│ canonical state
┌─────────────────┴──────────────────┐
▼ ▼
┌───────────────────────┐ ┌───────────────────────┐
│ java/ adapter │ │ bedrock/ adapter │
│ native Java packets │ │ native Bedrock packets│
│ Java state IDs │ │ Bedrock runtime IDs │
└───────────────────────┘ └───────────────────────┘
Java client Bedrock client
This lets GoCraft evolve both protocol implementations independently, without the coupling issues proxy-based translators face.
Block states, item IDs, entity types, biomes, and packet IDs are loaded from versioned JSON at startup via go:embed (internal/gamedata/ and internal/protocoldata/). There are no hardcoded ID maps, which keeps version bumps localized to data files and encoders.
Adapters translate client input into edition-neutral intents posted to the core, and the core emits events (e.g. PlayerJoinEvent, BlockBreakEvent) consumed by plugins. Neither packets nor numeric protocol IDs are exposed to plugins. See Native Go Plugins.
See Project Structure for the full directory tour.
GoCraft — native-Go Minecraft server · Java 1.21.4 + Bedrock 1.26.45 cross-play · GPL-3.0 · © 2026 Oreo Studios · Discord
Playing on GoCraft
Server & cross-play
Internals