ItemGraph 0.4.0 (NeoForge 1.21.1)
Curse Maven Snippet
What's new
[0.4.0] — 2026-10-17
Added
- World and environmental causes (#55): both loaders now record confirmed
explosion, piston, fluid, fire, Enderman block-movement, and falling-block
state changes into ItemGraph-owned storage. Bounded snapshots preserve
confirmed deltas and emit classified unresolved evidence when coverage is
partial or callbacks fail. Blocked piston results are recorded as
PISTON_BLOCK_ATTEMPTattempts separately from direct state deltas, and fire/fluid writes that return without a block-type change persist asWORLD_EFFECT_ATTEMPTwithoutcome=UNCHANGED. Throws with no confirmed block delta remainWORLD_EFFECT_UNRESOLVED. Enderman rows carry typed actor identity (entity:<type> id=<uuid>) separate from owner-provenance status, and audit rows render world/entity actor kinds instead of an unknown-player label. Events never invent player actors or item quantities; sensitive-location rows enforceitemgraph.auditthrough the taxonomy-driven lookup gate. The sharedEventTaxonomyadvances to4.1.0for the new event contract. NeoForge stages world-event capture atServerAboutToStartEventand Fabric opens capture before database initialization inSERVER_STARTING, so startup world events stay queued until the persistence worker can flush them. - Entity, projectile, and item-entity lifecycle causes (#56): both loaders
now record fresh entity spawns (
ENTITY_SPAWN), genuine removals (ENTITY_DESPAWN), non-player and world-cause kills (ENTITY_KILL), projectile launches and impacts (PROJECTILE_LAUNCH,PROJECTILE_IMPACT), and item-entity join/removal rows carrying the exact observed stack count (ITEM_ENTITY_SPAWN,ITEM_ENTITY_DESPAWN,quantity=SIGNED_DELTA). NeoForge usesEntityJoinLevelEvent.loadedFromDisk()andRemovalReason.shouldDestroy()so chunk loads and unloads never record spawn/despawn endpoints; Fabric reaches the same boundaries throughaddFreshEntityand asetRemovedcapture because no Fabric events expose them. Projectile impact usesProjectileImpactEventon NeoForge and the game's ownhitTargetOrDeflectSelfdispatch on Fabric. Non-player death drops now flow through the same settle-confirmed pending path as player drops — the dead entity replaces the player actor rather than inventing one — and a canceled drop event emits unresolved evidence.ENTITY_LIFECYCLE_UNRESOLVEDpreserves queue rejections and callback failures with reason codes.EventTaxonomyadvances to4.2.0. - Item-processing and economy transformations (issue #57). Trades,
enchanting, brewing, smithing, grindstone, and loot-table generation now
record observed input/output endpoints through the shared
TransformationEvidencebuilder on both loaders. NeoForge uses the documentedTradeWithVillagerEvent,PlayerEnchantItemEvent, andGrindstoneEvent.OnTakeItem; brewing, smithing, and loot generation use justified shared mixin boundaries (BrewingStandBlockEntity.doBrew,SmithingMenu.onTake,LootTable.getRandomItemsRaw) because no documented event supplies the required position, inputs, or result. Loot rows carry the table id resolved throughRegistries.LOOT_TABLEplus context actor/block/ tool provenance. Cancelled operations persistoutcome=CANCELLEDwithTRANSFORMATION_CANCELLED; unobserved inputs and NeoForge's unobservable grindstone output stay explicit instead of guessed. A queue-rejected or failed callback emitsTRANSFORMATION_UNRESOLVEDwithTRANSFORMATION_EVIDENCE_NOT_SUBMITTED. Absent endpoints use the explicititemgraph:non_item_endpointsentinel — never a fabricated stack. - GriefLogger coexistence and automatic migration (issue #203). ItemGraph
and GriefLogger can now run on the same server: the SQLite driver ships as an
opaque
META-INF/iglibs/resource loaded in a private, platform-parented classloader (com.itemgraph.db.SqliteJdbc), so noorg.sqlite.*class sits on ItemGraph's module or classpath to collide with the copy inside the GriefLogger jar. NeoForge declares GriefLoggertype="optional"withordering="AFTER"; Fabric lists it undersuggests. When the GriefLogger mod is detected at server start, ItemGraph enables the read-only source bridge automatically (grieflogger_auto_migrate, defaulttrue), resolves the source to GriefLogger's realconfig/grieflogger/database.db, queues the historical import once, and keeps polling new rows on the existing 60-second ingestion worker — no restart-with-removed-jar dance required. Setgrieflogger_auto_migrate=falseto run both mods with the bridge off, or keepgrieflogger_integration_enabled=truefor a retained database after the mod is removed. A GriefLoggeruseMysql=trueconfiguration logs a warning since only the on-disk SQLite file is importable./ig statusreports the auto-detected migration state. Query cancellation no longer bindsorg.sqlite.*types — the dispatcher interrupts timed-out queries through JDBCStatement.cancel()plus a reflective connection interrupt. - Opt-in automation adapter contract (issue #34):
AutomationEndpointprovides restart-stable opaque inventory identities derived from mod, dimension, position, slot policy, and side, plus opt-in FabricFabricTransferStorageAdapterand NeoForgeNeoForgeItemHandlerAdapterimplementations. They preserve native transfer results, record exact committed quantities, and suppress simulations and rollbacks. Fabric overflow omits the whole transaction; source mod identity is checked against its registered handle, and caller-created endpoints cannot add a location to the opaque reference. Rejected transfers, zero movement, and impossible quantity claims emit no movement evidence. ExplicitQUEUE_FULLresponses retry the same event identity at most three times through a 128-entry bounded worker queue; other outcomes are not replayed. This is in-memory retry only, and pending retries can be interrupted by process shutdown. Vanilla hopper transfers emit bounded endpoint-unknown net deltas on both loaders (HOPPER_INSERT/HOPPER_EXTRACT); NeoForgeIItemHandlercapability deltas persist asCAPABILITY_INSERT/CAPABILITY_EXTRACTat the changed endpoint, and committed adapter-API transfers persist asTRANSFER_ITEM. All three automation action IDs are now classified inEventTaxonomyinstead of rendering unclassified. Both loaders record accepted default-behavior dispenser/dropper outputs (DISPENSER_DROP/DROPPER_DROP) with source, item, amount, ground endpoint, and entity UUID. Snapshot bounds use overflow-safe arithmetic, and unreadable prior hopper snapshots suppress paired deltas. NeoForge adapters accept mod-owned opaque inventory IDs for item-handler providers without deriving identity from anItemStack. Cross-loader test consumers move stacks through Fabric Transfer API and NeoForge item-capability adapters between portable and shulker-like storage and assert both persisted endpoint deltas. These are vanilla/API fixtures, not third-party backpack integrations. Arbitrary modded inventories still require an exercised supported adapter. Bounded queue saturation and concurrent rejection tests verify the 128-entry queue and visibleQUEUE_FULL. Native hopper/dispenser captures use an additional 1,024-entry bounded backpressure queue on the same database worker;/ig statusreports pending rows and explicit exhaustion, and shutdown flushes both queues. Saturation tests verify native capture retention and persistence after the primary 10,000-entry observation queue fills.
Changed
- Default
grieflogger_database_pathresolution. The default valuedatabase.dbnow resolves toconfig/grieflogger/database.db(where GriefLogger actually writes) whenever the mod is detected or the file exists, instead of<game directory>/database.db. Explicitly configured paths are unchanged. - Ingestion schema gate. No change: a GriefLogger source file that exists but lacks the supported schema continues to be skipped after one log line per disappearance.
This mod has no additional files