Skip to content

Architecture

LordFinn - Yannick Suc edited this page Aug 28, 2026 · 3 revisions

Architecture

Why this exists

The original crazythings implementation stored every phone, contact, and message ever sent, in every conversation, forever, in one SavedData blob - and broadcast the entire thing to every online player on every login and every message sent. History was never pruned, so the payload only grew across the server's lifetime; that's what eventually crashed the server on player connect.

This rewrite splits that blob by purpose:

Data Storage Sync behavior
Phone registry, contacts, mayor state PhoneRegistrySavedData Small by construction, still synced in full on login
Conversation messages (incl. voice audio, images) ConversationSavedData, one bucket per conversation Never broadcast; fetched on demand when a conversation is opened, capped and trimmed on write

A new message now notifies only the participants who are online, instead of the whole server, so payload size stays bounded no matter how long the world has run or how much history has accumulated. The same principle carries through the voice features added later, and through the native photo pipeline: call audio, voice message audio, and photo bytes are only ever sent to the players who actually need them, on demand, never broadcast.

Platform support

One shared src/main/java tree, preprocessed per target by Stonecutter (//? if fabric / //? if neoforge, plus per-version checks) into 5 build targets. NeoForge 1.21.1 is the primary development target; everything else is kept in sync with it.

NeoForge 1.20.4 NeoForge 1.21.1 NeoForge 1.21.10 Fabric 1.20.1 Fabric 1.21.1
Phone, messaging, contacts, groups
Mayor election
Native camera (capture/viewer/photo item/My Photos) (pending)
Sneak-presenting (hold a photo up, two-hand grip) (pending)
Voice calls & voice messages (SVC) (SVC) (SVC)
Soulbound enchantment
Runtime-configurable settings
  • NeoForge 1.21.10's camera and sneak-presenting features compile but don't work yet: Mojang reworked both item rendering and the screenshot/texture APIs the native pipeline uses on that version, and porting to the new APIs is a separate, tracked follow-up.
  • Fabric 1.20.1 is a walking skeleton for now - the item exists and registers, but the phone's networking layer needs an API (CustomPacketPayload) that doesn't exist before 1.20.5, so none of the screens/messaging/camera work yet on that specific version.
  • Fabric 1.21.1 has the core feature set, the native camera pipeline (including punch-to-shoot, standalone capture, and My Photos), sneak-presenting, and the Soulbound enchantment - but no voice calls/messages, since Simple Voice Chat integration hasn't been ported to Fabric yet.

Project structure

Stonecutter splits the codebase into a shared source tree and one subproject per target version/loader:

CrazyPhone/
├── src/main/java/fr/lordfinn/crazyphone/   Shared source tree - every target compiles from here
│   ├── client/gui/       Screens (one per phone page) + shared widgets
│   ├── client/picture/   Native screenshot capture + texture cache, shared by both loaders
│   ├── command/          /crazyphone command tree
│   ├── data/             SavedData + player attachments (the crash fix lives here)
│   ├── enchantment/       Soulbound enchantment (≥1.20.5 only)
│   ├── fabric/            Fabric entrypoints + per-loader registration glue
│   ├── init/              Item/menu/screen/tab/sound/permission registration
│   ├── item/              The Crazy Phone item and photo item, vanilla item models & inventory capability
│   ├── mixin/             Cross-loader mixins (capture overlay input/rendering, sneak-presenting pose/hand-grip)
│   ├── network/           Client-server packets
│   ├── procedures/        Gameplay logic (ported from the original mod, adapted to the new data layer)
│   ├── utils/              Shared helpers (contacts, screen navigation, NBT/registry compat)
│   ├── voicechat/         Optional Simple Voice Chat integration (NeoForge only)
│   └── world/inventory/   Container menus backing each screen
├── versions/                One Stonecutter subproject per build target (1.20.4, 1.21.1, 1.21.10,
│                            1.20.1-fabric, 1.21.1-fabric) - each has its own gradle.properties pinning
│                            that target's Minecraft/loader/dependency versions
├── build.gradle.kts          Default build script - NeoForge targets
├── build.fabric.gradle.kts    Build script used by the two "-fabric" subprojects (Fabric Loom)
└── stonecutter.gradle.kts     Wires up the //? if fabric / //? if neoforge / //? if >=X.Y preprocessor

Per-loader and per-version differences in the shared tree are handled with Stonecutter comment directives (e.g. //? if fabric { ... }, //? if >=1.20.5 { ... }) rather than separate source sets or an abstraction layer - most files are identical on every target; only the files that genuinely differ (registries, networking, attachments, a handful of NeoForge/Fabric API divergences) carry any gating.

Localization

Every piece of UI text (button labels, tooltips, placeholders, headers) is routed through the lang files, no hardcoded strings:

src/main/resources/assets/crazyphone/lang/en_us.json
src/main/resources/assets/crazyphone/lang/fr_fr.json

Want to add another language? Copy en_us.json to <your_locale>.json and translate the values; the keys must stay identical between files.

Credits

  • Simple Voice Chat by henkelmax: the voice engine calls and voice messages are built on top of (NeoForge only).
  • Original crazythings project: source of the feature set and assets this mod ports and rebuilds.

Clone this wiki locally