Loading
Mod Development
EurekaBusiness: Modular Minecraft Retail & Commerce Architecture

EurekaBusiness is a multiplayer commercial simulation mod built for Minecraft 1.21.1 on NeoForge 21.1.x. The project translates physical world building, item gathering, and economic decision-making into an in-world retail loop. Instead of hiding transactions behind abstract inventory menus or standalone GUI screens, EurekaBusiness models shops through physical block entities, visible display pedestals, directional key devices, and autonomous customer NPCs that physically navigate through store aisles, inspect merchandise, queue at cash registers, and react to pricing.

Shop Overview
An active retail shop in EurekaBusiness, featuring display pedestals with floating price tags, customer queue aisles, and physical cash registers

The project enforces strict separation between foundational enterprise contracts and specific domain gameplay. The system separates the base layer (EurekaBusinessCore) from domain implementations such as EurekaBusinessRetail and EurekaBusinessRestaurant. All state validation, currency transfers, and inventory transactions operate under strict server-authoritative invariants to prevent duplication bugs and desynchronization across multiplayer sessions.

0:00
/0:00
Gameplay walkthrough of the EurekaRetail snapshot release, showing shop registration, pedestal pricing, customer browsing cycles, and cashier checkout

Core design principles

Building a multiplayer economy mod inside Minecraft presents concrete constraints: uncontrolled tick listeners degrade server tick rates, naive entity pathfinding bottlenecks CPU cores, and loose network protocols invite item duplication. EurekaBusiness adheres to four design rules:

  • Server authority: The server is the single source of truth for shop boundaries, permissions, stock counts, pricing, and ledgers. Clients send player interaction intents, and incoming packets trigger server-side distance, reachability, permission, and concurrency (CAS revision) checks.
  • Controller-less spatial indexing: Stores have no physical core block or centralized controller block entity. A store is defined by an axis-aligned bounding box (AABB) in world space, avoiding single-point-of-failure issues if a core block breaks.
  • Event-driven state updates: Stores and pedestals avoid per-tick polling. State updates, boundary revalidations, and queue transitions run on event triggers (block placement, destruction, inventory commits, or timer callbacks).
  • Pathfinding node interception: Fixtures (pedestals, key devices, cash registers) override isPathfindable(state, type) to return false and set getBlockPathType to PathType.BLOCKED. Combined with custom collision voxel shapes, this keeps mob pathfinding nodes from routing through display counters.
flowchart TD
    subgraph Client["Client (Presentation Layer)"]
        UI["In-World Value Box\n(Hit Zone + Face Highlight + Hover Tip)"]
        OUT["ShopOutlinerBridge\n(Ponder / Catnip Outliner)"]
        HOLO["HologramModelPresenter\n(Raw BakedQuad Bulk Put)"]
        BER["BER Price Tag Renderer\n(22.5 deg Tilted Plate)"]
    end

    subgraph Network["C2S / S2C Network Boundary"]
        PKT["Validated Network Packets\n(Distance, Permission, CAS Revision)"]
    end

    subgraph Server["Server Authority (Logic & State)"]
        SBI["ShopBoundaryIndex\n(Chunk-Allocated Spatial Candidates)"]
        BSS["BusinessStateStore\n(CAS Revisions & World SavedData)"]
        CUST["Customer Behavior Runtime\n(State Tree & Pathfinding Budget)"]
        SETTLE["Atomic Settlement Pipeline\n(All-or-Nothing Transaction)"]
    end

    UI --> PKT
    PKT --> Server
    Server --> OUT
    Server --> HOLO
    Server --> BER

System architecture

EurekaBusiness follows a decoupled multi-project Gradle layout targeting NeoForge. The codebase enforces a unidirectional dependency hierarchy: Core <- Retail and Core <- Restaurant. Retail and Restaurant share zero compile or runtime dependencies.

EurekaBusiness/
├── Core/
│   ├── Common/         # Loader-neutral business ID, codecs, state stores, spatial math
│   └── NeoForge/       # World SavedData, platform lifecycle, presentation bridges
├── Retail/
│   ├── Common/         # Pedestal rules, catalog structures, customer AI models
│   └── NeoForge/       # Pedestal blocks, GUI screens, customer entities, cashier registers
└── Restaurant/         # Dining tables, kitchen tickets, menu contracts (Independent module)
EurekaBusinessCore (Common Contract & Engine) Spatial AABB Engine ShopBoundaryIndex & Resolver Value Currency & Storage Value Fluid & Scalable Canister Presentation Presenters BlockHighlight & HologramModel depends on depends on EurekaBusinessRetail • Pedestal BlockEntities & Pricing Plugins • Customer NPC Behavior Tree & Rarity Sampler • Cashier Desks, Queue Zones & Atomic Settlement EurekaBusinessRestaurant (Addon) • Dining Tables, Waitlist Seating Zones • Kitchen Tickets & Cooking Work Orders • Shared Core contracts, zero Retail dependencies

Spatial partitioning and boundary indexing

Shops occupy continuous regions defined by ShopBounds. Core models these as immutable axis-aligned bounding boxes within a specific dimension. The engine rejects nested or intersecting shop regions on creation.

To avoid scanning entire worlds when an interaction occurs, ShopBoundaryIndex partitions shop bounds across Minecraft chunk coordinates:

public final class ShopBoundaryIndex {
    private final Map<ResourceKey<Level>, Long2ObjectMap<List<ShopBounds>>> chunkCandidates = new ConcurrentHashMap<>();

    public List<ShopBounds> getCandidateBounds(Level level, BlockPos pos) {
        long chunkKey = ChunkPos.asLong(pos.getX() >> 4, pos.getZ() >> 4);
        Long2ObjectMap<List<ShopBounds>> levelMap = chunkCandidates.get(level.dimension());
        if (levelMap == null) return List.of();
        List<ShopBounds> list = levelMap.get(chunkKey);
        return list != null ? list : List.of();
    }
}

When a player interacts with a pedestal or cashier desk, ShopPositionResolver fetches the candidate boundaries intersecting the target chunk and evaluates containment (bounds.contains(pos)). If no candidate matches, the block operates in an independent unmanaged state. If exactly one candidate matches, the block adopts that shop context.

Shop Configurator Boundary
The Shop Configurator tool displaying real-time Outliner bounding boxes for store perimeters, entrance areas, and exit zones

Entrance and exit sub-zones

Stores configure discrete entrance and exit sub-zones stored inside ShopSpaceStateExtension (Schema 2). Both entrances and exits are normalized as height-1 AABBs restricted entirely within the outer ShopBounds.

  • Entrance zones: Up to 16 discrete entrance areas per shop. When scheduling a customer wave, the server distributes spawn counts across available entrances based on horizontal surface area.
  • Exit zones: Up to 16 discrete exit areas. Departing customers query configured exits and pick one by balancing walking distance against real-time congestion.

Key device and shop key pairing

Store operating states (CLOSED and OPEN) are toggled through the physical key_device block entity with a bound shop_key item.

Key Device with Energy Columns
The Key Device block displaying dual-layer emissive energy columns and an animated floating hologram key model

The pairing mechanism functions entirely in-world:

  • Binding: Sneak right-clicking an unbound shop key onto a key device claims the shop identity, stamping the item with an immutable ShopKeyBinding component containing store UUID, owner UUID, and block coordinates.
  • Key device state machine: The block uses two blockstate properties: has_key and active. Inserting a key sets has_key=true, activating the outer column model. When the owner sneak-clicks the device, the server runs ShopOpeningCheck. If all preconditions pass (valid bounds, configured queue zone, valid cashier desk with value container), active=true activates the inner column and marks the shop open.
  • Hologram rendering: The rotating key model above the pedestal is drawn via HologramModelPresenter. When active, it tints into a translucent green projection.

Retail gameplay architecture

Display pedestals and augment plugins

Display pedestals serve as the physical merchandising surface. Each pedestal holds a single item variant and accepts a stock count bounded by that item’s native maximum stack size (1 to 64).

Pedestals feature eight internal augment slots (2 x 4 layout) governed by PedestalPluginRegistry. Unlike a generic industrial inventory pipe bus, plugins are statically declared per pedestal type during initialization and frozen before server start:

Pedestal VariantPrice Tag X OffsetPrice Tag Y OffsetPrice Tag Z Offset
Nomad Pedestal0.50.00.125
Deepslate Pedestal0.51.0050.125
Stone Pedestal0.51.0100.125
classDiagram
    class PedestalBlockEntity {
        -ItemStack displayItem
        -int stockCount
        -int listPrice
        -PluginSlotContainer augments
        +setItem(ItemStack)
        +setListPrice(int)
        +installPlugin(int slot, ItemStack)
    }

    class PedestalPlugin {
        <<interface>>
        +onInstalled(context)
        +onRemoved(context)
        +onLoaded(context)
        +validatesState()
    }

    class PriceTagPlugin {
        +onInstalled(context)
        +renderWorldPlate(poseStack, buffer)
    }

    PedestalBlockEntity --> PedestalPlugin : dispatches lifecycle
    PedestalPlugin <|.. PriceTagPlugin : implements
Pedestal Plugin GUI
Pedestal augment configuration interface, displaying module slots, active price tags, and inventory controls
Pedestals with Price Tags
Merchandise pedestals with floating price tags displaying compact formatted values (e.g. 1.2K, 500)

When a PriceTagPlugin item is placed into any augment slot, a polished deepslate price plate renders above the pedestal. The tag renders with a fixed 22.5 degree backwards tilt aligned to the pedestal facing direction. The numerical price is formatted into a maximum 4-character compact string (K, M, B) and centered along the plate width with an explicit normal offset to eliminate Z-fighting.

Pedestal Selling GUI
Server-authoritative pedestal management GUI, showing item slots, numerical price inputs, valuation ratings, and real-time block previews

Item taxonomy and customer generation

Items in EurekaBusiness map to a taxonomy of 37 elemental categories (such as earth, tool, metal, magic, greed, and flora) defined in Core.

Item Tooltip Value Overlay
In-game item tooltip showing Core elemental classification icons with tier subscripts and baseline valuation ranges
flowchart LR
    INV["Pedestal Displays\n(Stone, Iron Sword, Golden Apple)"] --> ELEM["Element Extraction\n(Earth, Metal, Tool, Magic)"]
    ELEM --> WEIGHT["ElementRarityAlgorithm\n(Compute Variant Spawn Weights)"]
    WEIGHT --> SPAWN["Spawn Customer NPC\n(Miner, Warrior, Alchemist, Mage)"]

The system uses these elements to compute customer spawning probabilities through ElementRarityAlgorithm:

  • Inventory sampling: When evaluating spawn weights, the server tallies all active elemental tags across stocked pedestals.
  • Variant affinities: Customer archetypes define positive or negative affinities for specific elements. A shop stocking raw ores and pickaxes attracts Miner NPCs, while potions and enchanted tomes attract Alchemists and Mages.
  • Continuous purchase sampling: After a customer selects an item, CustomerContinuePurchaseAlgorithm decides whether the NPC browses for additional goods. The calculation factors in base patience, current basket size (up to 5 items), and the elemental relevance of remaining merchandise.

Customer decision flow and reaction bubbles

When browsing, customer NPCs evaluate merchandise prices against a server-synchronized Value Catalog. The evaluation assigns prices into one of four reaction brackets:

List Price <= Cheap Ceiling       -> CHEAP (Fast purchase, low margin)
Cheap < Price <= Perfect Ceiling   -> PERFECT (Optimal margin, high customer satisfaction)
Perfect < Price <= Expensive Max   -> EXPENSIVE (Reluctant purchase, reduces continuous shopping odds)
Price > Astronomical Threshold    -> ASTRONOMICAL (Rejects item immediately, aborts shopping session)
Customer Browsing Reaction
Customer NPC examining a display pedestal with an in-world billboard reaction bubble indicating price evaluation

Upon evaluation, a billboarded ReactionBubble renders above the customer head, providing visual feedback directly in the world.

Queue zones and atomic checkout

Once customers complete browsing, they move to the store’s Queue Zone and wait for cashier processing.

Customer Queue
Customer NPCs maintaining ordered positions in the shop queue zone awaiting checkout

Cashier processing supports two interaction modes:

  • Cashier desk UI: Opening the cash register displays a portrait preview of the customer at the head of the line, their 5-item basket, and the attached value container slot.
  • Physical pull handle: Right-clicking the mechanical handle mounted to the right side of the register commits checkout in first person without opening a screen.
Cash Register GUI
Cash register interface displaying active customer queue portraits, the 5-item customer basket, and the attached value canister slot
Cashier Handle Pull
Physical cashier desk handle pulled down to commit an in-world checkout transaction

The checkout transaction executes as an atomic operation:

sequenceDiagram
    autonumber
    participant Player as Cashier (Player / Handle)
    participant Server as Cashier Checkout Service
    participant Pedestal as Pedestal BlockEntities
    participant Container as Value Container
    participant Obligation as Tip Obligation Journal

    Player->>Server: Pull Handle / Click Checkout
    Server->>Server: Verify Queue Head & Customer Basket
    Server->>Pedestal: Check Stock Availability (All 1..5 Items)
    alt Stock Missing or Price Mutated
        Server-->>Player: Transaction Rejected (No State Changed)
    else Stock Valid
        Server->>Pedestal: Deduct Stock Counts (Atomic Loop)
        Server->>Container: Inject Value Fluid (Direct Settlement)
        Server->>Obligation: Record Tip Obligations (State: PREPARED)
        Server-->>Player: Play Audio & Eject Customer to Exit
    end

If any item in the basket is missing, reserved by another customer, or has its price changed before checkout, the transaction aborts with zero state change.

Tip obligations and persistence

Customer variants can award tips upon checkout. Fluid tips deposit directly into the attached value container, while rare items (such as enchanted books from Mages or diamonds from Miners) create a persistent TipObligation:

[PREPARED] -> [FUNDED] -> [DELIVERED]
                         \-> [BLOCKED]

When an item tip is awarded, the server writes a persistent record containing the shop ID, customer variant, and generated item stack into world SavedData. The store routes the item into an in-world container marked with a TipLabel. If the labeled container is missing or full, the obligation transitions to BLOCKED rather than deleting the item. Upon world reload or container vacancy, the server attempts redelivery idempotently.

Label Configurator GUI
Label Configurator interface used to bind destination types and tip obligations to physical storage containers
In-world Container Labels
Storage chests with attached tip and inventory labels indicating valid output destinations

Client presentation and rendering pipeline

To maintain visual parity with modern technical mods like Create, EurekaBusiness includes an in-world presentation framework in Core/NeoForge/client/presentation.

BlockHighlightPresenter

Instead of drawing simple bounding boxes over coordinates, BlockHighlightPresenter extracts physics geometry from the target block VoxelShape:

  • Decomposes VoxelShape.toAabbs() into exact component prisms.
  • Renders dual-color pulsing outlines (such as emerald green for valid key devices and amber for configuration warnings).
  • Features a short time-to-live (TTL) frame cache. Outlines refresh per frame; when the player looks away or unequips the tool, the outline fades out naturally.

HologramModelPresenter

Rendering translucent items and blocks in Minecraft often conflicts with custom vertex pipelines. Wrapping a VertexConsumer to override alpha values can break Sodium fast-path vertex pipelines (does not support optimized vertex writing code paths), while writing custom GLSL core shaders risks compatibility failures with Iris and OptiFine shader packs.

HologramModelPresenter avoids these conflicts by operating directly on baked model geometry:

public static void renderHologram(
    PoseStack poseStack,
    MultiBufferSource bufferSource,
    BakedModel bakedModel,
    int tintColor,
    float alpha,
    int packedLight
) {
    VertexConsumer consumer = bufferSource.getBuffer(Sheets.translucentCullBlockSheet());
    RandomSource random = RandomSource.create();
    for (Direction direction : Direction.values()) {
        random.setSeed(42L);
        List<BakedQuad> quads = bakedModel.getQuads(null, direction, random);
        for (BakedQuad quad : quads) {
            putQuadBulkData(consumer, poseStack.last(), quad, tintColor, alpha, packedLight);
        }
    }
}

By extracting raw quad vertex arrays and writing them directly via putBulkData, the presenter preserves native memory alignment, maintains compatibility with Sodium, and renders translucent holograms without shader conflicts.

Performance optimizations

Maintaining 20 TPS on multiplayer servers requires resource limits on NPC navigation and updates:

SubsystemStrategyVerification
Shop boundariesCandidate chunk map lookup; zero coordinate scanningBenchmark tests with 250+ concurrent shop bounds
PathfindingFailure budget (3 failed paths triggers ABORTING fade-out)Stress test with obstructed shop entrances
Customer AITick throttling with active distance cullingProfiling with Spark on 32-player test server
SettlementCAS revision checks on store state storeGameTest suite (15/15 concurrent transaction tests passing)
RenderersDynamic quad vertex buffer injection; zero core shader overrideTested under Sodium and Iris shader environments