Skip to content

CC2 Phase 1: deep-slot storage engine - #215

Merged
rubensworks merged 15 commits into
feature/1.21-lts-v2from
feature/1.21-lts-v2-phase1-storage
Oct 3, 2026
Merged

rubensworks merged 15 commits into
feature/1.21-lts-v2from
feature/1.21-lts-v2-phase1-storage

Conversation

@rubensworks

@rubensworks rubensworks commented Sep 28, 2026 •

Copy link
Copy Markdown
Member

Phase 1 of the Colossal Chests 2 plan (D-2, D-3). Phase 0 (#214) is merged, so the diff only contains Phase 1.

Summary

Everything is in org.cyclops.colossalchests2.storage. It uses ItemStack and components only, with no blocks, block entities or menus.

  • DeepSlot: an immutable slot holding a prototype (count 1), a long count, a locked flag and an optional compression form. The form is the item the player picked to extract in, stored as an item rather than an index into the compression family so it survives datapack changes to the family. It normalizes itself: a type with count 0 only survives while locked.
  • CapacityProfile: only resolved values: depth (stacks per slot), non-stackable capacity, technical cap maxItemsPerSlot (default Integer.MAX_VALUE) and acceptNonStackables. A Builder collects modifiers: the structure sets the base depth, and each installed upgrade applies its own (multiplyDepth, multiplyNonStackable, addNonStackableFactor). The profile knows nothing about upgrade types, so new upgrades, including third-party ones, need no changes here. The D-2 rules (stackables depth * maxStackSize, non-stackables 2^bundling * (1 + depthUpgrades)) are tested by expressing Depth and Bundling as those modifiers. All math saturates instead of overflowing.
  • ChestStorage:
    • Insert and extract, both per slot and by type. Inserting by type fills matching slots first, then free unlocked slots.
    • Locks: lockTo, lockAllFilled, clearLocks.
    • Extract-only is derived (count > capacity), never stored. That makes "re-formed smaller" and "config lowered" one code path, and loading can never delete items.
    • validateSlotCount / validateProfile return a ResizeResult listing the offending slots. setSlotCount / setProfile only change anything when that result is ok. forceProfile always applies and returns the slots that became extract-only.
    • Dirty-slot tracking (drainDirtySlots, markAllDirty) and change listeners (slot index, or -1 for layout or capacity changes).
    • getState(): a counter that changes on every mutation. This backs the CommonCapabilities IInventoryState capability you asked for; Phase 2 exposes it.
    • Sparse codec: only filled or reserved slots are written. Loading grows the slot count rather than dropping entries that lie beyond it. A slot that can't be decoded (for example, its item's mod was removed) is skipped instead of failing the whole storage, and an unknown compression form item loads as the default form.
  • CompressionFamily / CompressionFamilies: ordered forms with base units, conversion math (toBaseUnits, fromBaseUnits, remainderBaseUnits, convert) and lookup by item. Stacks with components never compress. There's no tag loading yet; tests use a hardcoded iron family.

Validation

All unit tests are in loader-common/src/test/.../storage (94 tests including Phase 0's). JaCoCo reports 100% line coverage and every method covered for the storage package.

Plan test Covered by
Insert into empty, matching, full, locked, mismatched TestChestStorage.testInsertInto*, testInsertPartiallyFits, testInsertDistinctComponentsAreDistinctTypes
Extract partial, full, more than present, from extract-only TestChestStorage.testExtract*
Non-stackable capacity with and without bundling and depth upgrades TestCapacityProfile.testCapacityNonStackable, testNonStackableCapacityIgnoresBaseDepth, testNonStackableSaturates, TestChestStorage.testNonStackableCapacityWithUpgrades
Sparse round trip with a slot at Integer.MAX_VALUE and one above it in extract-only mode TestChestStorage.testSerializationRoundTrip (NBT), testSerializationJson, testContentsAreSparse, testLoadContentsNeverDropsEntries, testUndecodableEntrySkipped, testUnknownCompressionFormLoadsAsDefault
Resize validation, both directions testValidateSlotCount, testSetSlotCount*, testValidateProfileBothDirections, testSetProfile, testForceProfile
Compression conversion across three forms TestCompressionFamily.testConversionAcrossThreeForms

./gradlew build and ./gradlew runGameTestServer pass on all three loaders. There are no new game tests or clientdevbridge checks because nothing touches a level or GUI yet.

Design deviations

  • Plan says: "Extract-only mode for over-capacity slots." I made it a derived state, not a flag, for the reasons above. The behaviour matches D-3.
  • Compressed counts will be stored in base units (the smallest form), because D-6 needs both "1,234 blocks = 11,106 ingots" and mixed nugget/ingot/block inserts. The math is here; the storage integration and the D-3 "budget counted in the largest form" rule land in Phase 6.

🤖 Generated with Claude Code

https://claude.ai/code/session_01RSz5r6eowhjNuANBjZE2v6


Generated by Claude Code


Generated by Claude Code

Rename the mod to colossalchests2 (mod id, package, resources, group,
update URLs) and remove all CC1 gameplay content so the branch builds
with only the loader entrypoints, proxies and Cyclops Core wiring.

- Enable JUnit in the common module against vanilla classes.
- Add the data-driven table schema (materials, depth by size, upgrade
  values) with shipped defaults and a loader stub.
- Keep one game test per loader via the shared GameTestsCommon.
- Add dev/clientdevbridge with launch instructions and a mod list check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RSz5r6eowhjNuANBjZE2v6
Pure-logic storage model with no block, block entity or menu references.

- DeepSlot: immutable slot record (prototype, long count, locked,
  compression form).
- CapacityProfile: uniform depth with Depth upgrades, non-stackable
  capacity from Bundling and Depth, technical per-slot cap and the
  acceptNonStackables switch, all with saturating math.
- ChestStorage: insert/extract per slot and by type, locks, derived
  extract-only state, non-mutating resize validation, forced profile
  changes for re-forming smaller, dirty-slot tracking, change listeners,
  a state counter for inventory-state capabilities, and sparse codec
  serialization that never drops entries on load.
- CompressionFamily/CompressionFamilies: ordered forms with base-unit
  conversion math and lookup by item.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RSz5r6eowhjNuANBjZE2v6
Drop the dev/clientdevbridge files, keep the CC1 project IDs, display
and update URLs, restore the README and .gitignore, and use the
suggested mod description.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RSz5r6eowhjNuANBjZE2v6
@rubensworks
rubensworks marked this pull request as draft September 28, 2026 17:42
claude added 10 commits October 3, 2026 13:41
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RSz5r6eowhjNuANBjZE2v6
Each material is now its own file under
data/<namespace>/colossalchests2/material/, so mods and datapacks can
add materials without overwriting each other. Chest-wide values move to
data/colossalchests2/colossalchests2/chest.json. Every field is optional
with a default, so adding fields later never breaks existing datapacks.

The global upgrade table and max_depth_upgrades are dropped: upgrade
values and per-material limits will be declared by each upgrade in
data/<namespace>/colossalchests2/upgrade/ when upgrades are added.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RSz5r6eowhjNuANBjZE2v6
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RSz5r6eowhjNuANBjZE2v6
The profile now only holds resolved values (depth, non-stackable
capacity, technical cap, acceptNonStackables). A builder collects
modifiers: the structure sets the base depth and each installed upgrade
applies its own (multiply depth, multiply or add non-stackable factors),
so new upgrades, including from other mods, need no profile changes.
The D-2 rules are covered by tests expressing Depth and Bundling as
modifiers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RSz5r6eowhjNuANBjZE2v6
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RSz5r6eowhjNuANBjZE2v6
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RSz5r6eowhjNuANBjZE2v6
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RSz5r6eowhjNuANBjZE2v6
Base slots, max slots and stacks per slot by structure size are now
config options instead of a datapack file. They are a fixed set owned by
this mod, so per-entry data files are not needed; materials stay
data-driven because other mods add entries. Getters clamp the values,
since config ranges are not enforced on load.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RSz5r6eowhjNuANBjZE2v6
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RSz5r6eowhjNuANBjZE2v6
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RSz5r6eowhjNuANBjZE2v6
@rubensworks
rubensworks marked this pull request as ready for review October 3, 2026 14:53
Comment thread loader-common/src/main/java/org/cyclops/colossalchests2/storage/ChestStorage.java Outdated
- Store a slot's compression form as an item instead of an index into
  the compression family, so it survives datapack changes to the
  family. Unknown form items load as the default form.
- Skip storage entries that cannot be decoded, such as items of a
  removed mod, instead of failing to load the whole storage.
- Move nonStackableCapacity to the last CapacityProfile component.
- Reword the ChestStorage javadoc.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RSz5r6eowhjNuANBjZE2v6
@rubensworks
rubensworks merged commit 4a131f7 into feature/1.21-lts-v2 Oct 3, 2026
5 checks passed
@rubensworks
rubensworks deleted the feature/1.21-lts-v2-phase1-storage branch October 3, 2026 18:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants