# Changelog — Dynmap NeoForge 1.21.1 Port All changes relative to the upstream `neoforge-1.20.6` module. --- ## [2026-03-07] — Final clean state (pre-PR) ### Reverted - `touchChunk()` helper method extraction — inlined back to match 1.20.6 structure - Was purely cosmetic; upstream CONTRIBUTING.md prohibits refactoring in PRs - No behavior change, no runtime impact ### Fixed — whitespace - `ForgeMapChunkCache.java` — formatting-only diffs corrected to match 1.20.6 style --- ## [2026-03-07] — canOcclude() deadlock fix ### Changed - `DynmapPlugin.initializeBlockStates()` (lines ~248, ~259): - `isSolidRender(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)` → `canOcclude()` - `propagatesSkylightDown(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)` → `isAir()` - `isSolid()` → `canOcclude()` ### Why `isSolidRender()` / `propagatesSkylightDown()` / `isSolid()` all trigger a Guava `LoadingCache` lookup in ModernFix's `reduce_blockstate_cache_rebuilds` mixin. During world load with modernfix + ferritecore (present in ATM10), this causes a `BlockState$Cache.` deadlock — lazy/deferred block state initialization enters an infinite loop in VoxelShape calculation for certain complex mod block states. The watchdog kills the server after ~60 seconds. `canOcclude()` and `isAir()` read precomputed booleans directly from the block state with no cache involvement. Functionally equivalent for Dynmap's purposes, safe with all mods including modernfix and ferritecore. ### Test result - ✅ ATM10 5.5, 445 mods, NeoForge 21.1.219 — 3/3 clean boots - ✅ `[Dynmap] version 3.7-SNAPSHOT-Dev is enabled` - ✅ 17 worlds loaded, web server started on 0.0.0.0:8123 --- ## [2026-03-07] — Runtime fixes + build improvements ### Fixed — API changes (1.21.1) - `DynmapPlugin`: `ServerTickEvent` listener updated to `ServerTickEvent.Post` - NeoForge 21.x made `ServerTickEvent` abstract; registering the base class no longer works - `DynmapPlugin.initializeBlockStates()`: replaced `isSolidRender(null, ...)` with `isSolidRender(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)` - 1.21.1 actually uses the BlockGetter parameter; passing null causes NPE - (Superseded by canOcclude() fix above — kept here for history) - `DynmapPlugin`: `getLastAvailable()` → `getLatestChunk()` — API renamed - `DynmapPlugin`: `getStatus()` → `getPersistedStatus()` — API renamed - `DynmapPlugin`: `ChatHandler` inner class → direct method with `addListener()` - NeoForge 1.21.1 changed event registration; inner class pattern no longer supported - `NBT.java`: `contains()` reimplemented with manual type checking (behavior changed in 1.21.1) - `NBT.java`: `getAsString()` null safety added (behavior changed in 1.21.1) ### Fixed — Build - `build.gradle`: userdev plugin 7.0.133 → 7.1.20 (NeoForge 21.1.x requirement) - `build.gradle`: NeoForge dep 20.6.62-beta → 21.1.219 - `gradle-wrapper.properties`: 8.7 → 8.14 (required for userdev 7.1.20) - All `fabric-*/build.gradle`: loom 1.6.11 → 1.8.13 (required for Gradle 8.14 compat) - `neoforge.mods.toml`: loader/version ranges updated for 1.21.1 - Access transformer: SRG names → mojmap names - Heap caps: Gradle daemon `-Xmx2g`, forked javac `-Xmx3g` ### ⚠️ Known discrepancy (from earlier commit message) - An early commit message claimed `visibleChunkMap` was replaced with `getChunks()` - **This was inaccurate.** `visibleChunkMap` is still in the code and works fine in NeoForge 1.21.1 --- ## [2026-03-07] — Initial 1.21.1 compile fix pass ### Fixed — Compile errors (43 total) - All compile errors resolved from `neoforge-1.20.6` → `neoforge-1.21.1` baseline: - `net.minecraftforge.*` → `net.neoforged.*` (full package migration) - `MinecraftForge.EVENT_BUS` → `NeoForge.EVENT_BUS` - Access transformer updated for 1.21.1 private field patterns --- ## Format note Entries are dated by session (not release), since this is a port-in-progress.