Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions CHANGES_26_2_BLOCK_MAPPINGS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Dynmap 26.2 block mapping fixes — changelog

**Context:** On Minecraft 26.2 (Paper), a number of blocks rendered as solid black on the Dynmap
web map, with no error at all in the server log. Root cause: Dynmap silently renders any block
state it has no `texture_1.txt` / `models_1.txt` mapping for as a blank/black tile — there is no
warning when a block is simply *missing* from the mapping data. This mostly affects blocks added
in Minecraft versions newer than the last time these two files were updated (last version tag
present before this fix: `[1.21.9-]`).

Method used: diffed `assets/minecraft/blockstates/*.json` between the Minecraft 1.21.7, 1.21.11,
and 26.2 client jars to get the exact list of new/changed blocks, then cross-checked every
existing mapped block's real state properties against the current 26.2 blockstate JSON to catch
silent property renames (not just brand-new blocks).

## Files changed

- `DynmapCore/src/main/resources/texture_1.txt`
- `DynmapCore/src/main/resources/models_1.txt`
- `DynmapCore/src/main/resources/texturepacks/standard/assets/minecraft/textures/block/*.png` (new texture files, extracted from the vanilla 26.2 client jar — Mojang assets, same as every other vanilla texture already bundled in this resource pack)

All new entries are tagged `[26.2-]` so they only apply on 26.2+ servers and cannot affect older versions.

## 1. New blocks added (introduced between 1.21.7 and 26.2), fully mapped

**Sulfur family** — full block, bricks, chiseled, polished, potent variant, plus stairs/slabs/walls
for the plain, brick, and polished forms (mapped the same way the existing `tuff`/`tuff_bricks`
family already was):
`sulfur`, `sulfur_bricks`, `chiseled_sulfur`, `polished_sulfur`, `potent_sulfur`,
`sulfur_stairs`, `sulfur_slab`, `sulfur_wall`,
`sulfur_brick_stairs`, `sulfur_brick_slab`, `sulfur_brick_wall`,
`polished_sulfur_stairs`, `polished_sulfur_slab`, `polished_sulfur_wall`

**Cinnabar family** — same treatment as sulfur:
`cinnabar`, `cinnabar_bricks`, `chiseled_cinnabar`, `polished_cinnabar`,
`cinnabar_stairs`, `cinnabar_slab`, `cinnabar_wall`,
`cinnabar_brick_stairs`, `cinnabar_brick_slab`, `cinnabar_brick_wall`,
`polished_cinnabar_stairs`, `polished_cinnabar_slab`, `polished_cinnabar_wall`

**Sulfur Spike** — `sulfur_spike` (10 model states: base/frustum/middle/tip/tip_merge × up/down).
Mapped as a billboard cross patch, the same technique Dynmap already uses for vanilla
`pointed_dripstone` (added a `sulfur_spike` line right next to the existing `pointed_dripstone`
patchblock definition in `models_1.txt`) — this is Dynmap's accepted approximation for this shape,
not a compromise specific to this fix.

**Flowers:**
`golden_dandelion` (mapped identically to the existing `dandelion` cross-plant patchblock) and
`potted_golden_dandelion` (mapped identically to `potted_dandelion`).

**Copper Golem Statue** (8 oxidation/wax variants: `copper_golem_statue`, `exposed_copper_golem_statue`,
`weathered_copper_golem_statue`, `oxidized_copper_golem_statue`, and their `waxed_*` counterparts):
this block has **no real block model** in vanilla — it's drawn entirely through a
BlockEntityRenderer, so there is no faithful patch-based geometry to map. Approximated as a plain
solid cube using the matching existing copper-oxidation-stage texture (`copper_block` /
`exposed_copper` / `weathered_copper` / `oxidized_copper`) so it at least shows up as
copper-colored on the map instead of invisible. **Flagged for anyone reviewing this: a real fix
would need a custom Java `CustomRenderer` class to approximate its actual statue shape.**

The following blocks from the same 1.21.7→26.2 diff were checked and found **already correctly
mapped** before this fix (no changes needed): all wood `*_shelf` blocks (acacia, bamboo, birch,
cherry, crimson, dark_oak, jungle, mangrove, oak, pale_oak, spruce, warped), and the full copper
decor set (chains, bars, torches, wall torches, lanterns, lightning rods, chests — including all
`waxed_*` variants).

## 2. Bug fix: `creaking_heart` blockstate property renamed

`creaking_heart` was already mapped (added at `[1.21.4-]`), but only for its **old** state property
`active:true/false`. At some point before 26.2, Mojang renamed/expanded this to a 3-value property
`creaking_heart_state:awake/dormant/uprooted`. Since the property name itself changed, every
`creaking_heart` on a 26.2 server matched *zero* mapping entries and rendered black.

Fix: added 9 new `[26.2-]` mapping lines (3 axis × 3 `creaking_heart_state` values) using texture
files that were already bundled in the resource pack but never wired up
(`creaking_heart_awake`/`_dormant` + their `_top` variants). The old `[1.21.4-]` `active:true/false`
entries were **left in place untouched** — they're harmless dead weight on 26.2 (no block state
there has an `active` property anymore) but still needed for correctness on 1.21.4–1.21.11 servers.

## 3. Verification performed (no other issues found)

- Extracted **every** `assets/minecraft/blockstates/*.json` from the 26.2 client jar and
cross-referenced all ~550 distinct `(blockId, stateProperty)` pairs currently referenced anywhere
in `texture_1.txt`/`models_1.txt` against the real property names for that block in 26.2 (handling
both the flat `"axis=x,type=y"` variant-key format and the newer `"multipart"`/`"when"` format).
Only flagged mismatch: the intentionally-kept legacy `creaking_heart`/`active` entries described
above — nothing else.
- Confirmed all 92 blocks from the 1.21.7→26.2 blockstate diff are now covered.

## 4. Investigated, not a real bug (self-resolved)

A `copper_chest[facing=north,type=single,waterlogged=true] - not enough textures for faces (16 > 6)`
severe log line was observed once. Traced it as far as `TexturePack.java`'s integrity check
(`HDBlockStateTextureMap` / `HDBlockModels.getNeededTextureCount`) and `ChestStateRenderer.java`;
added temporary debug logging to investigate further, but on the next clean server restart (plus a
`/dynmap purgemap` + `radiusrender`) the error did not reproduce at all and the copper chest
rendered correctly. Likely a one-off load-order artifact at plugin boot. Debug logging was removed
again afterward — no net code change here, just noting it in case it resurfaces for someone else.

## Not covered / left as follow-up

- **Copper Golem Statue** real geometry (currently a flat-color cube approximation, see above).
- No other known gaps as of this writing (26.2, 2026-07-19).
33 changes: 26 additions & 7 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ Dynmap is a dynamic web mapping plugin/mod for Minecraft servers. It's a multi-p

## Build Commands

This project now spans **three separate Gradle builds**, split by Gradle-version compatibility:

```bash
# Build all platforms (requires JDK 21 as default)
# Main build: Spigot, all bukkit-helper-*, all fabric-*, DynmapCore/API (Gradle 9.5.1)
./gradlew setup build

# Build outputs go to /target directory
Expand All @@ -20,19 +22,36 @@ Dynmap is a dynamic web mapping plugin/mod for Minecraft servers. It's a multi-p
# Run unit tests (DynmapCore only — JUnit 4)
./gradlew :DynmapCore:test

# Modern Forge (1.14.4 - 1.21.11): separate build, Gradle 8.14 - ForgeGradle does not support Gradle 9
cd forge-build
./gradlew setup build

# Forge 1.12.2 (requires JDK 8 - set JAVA_HOME accordingly)
cd oldgradle
./gradlew setup build
```

**Why three builds:** ForgeGradle (used by `forge-1.14.4` through `forge-1.21.11`) hard-rejects Gradle 9
("Versions Gradle 9.0 and newer are not supported yet"), so those 10 modules live in `forge-build/`
on Gradle 8.14 (mirroring the existing `oldgradle`/`forge-1.12.2` split). The main build moved to
Gradle 9.5.1 specifically so `bukkit-helper-26-2` (Paper/Spigot 26.2 support) can use
`paperweight-userdev`, which requires Gradle 9+.

**JDK Requirements:**
- Default: JDK 21
- Default: JDK 21 — run the main build's Gradle daemon on JDK 21, not JDK 25. Confirmed by testing: on JDK 25, `spigot:compileJava` fails with `cannot access NonNull ... class file for org.checkerframework.checker.nullness.qual.NonNull not found` (an unrelated LuckPerms-dependency/`-source 8` cross-compilation quirk specific to JDK 25's javac) - JDK 21 compiles this cleanly.
- `bukkit-helper-26-2` (Paper/Spigot 26.2): requires **JDK 25** to compile against real (unobfuscated) Mojang-mapped classes. This is handled automatically by that module's own Gradle toolchain block (`java { toolchain { languageVersion = JavaLanguageVersion.of(25) } }`) as long as a JDK 25 install is present for Gradle to auto-detect (e.g. under `Program Files\Java`) - no manual `JAVA_HOME` juggling needed once both JDK 21 and JDK 25 are installed.
- `forge-build`: Gradle 8.14 cannot run its daemon on JDK 25 either - use JDK 21 (or any JDK ≤ ~23) there too.
- `fabric-26.2`: unlike `bukkit-helper-26-2`, Fabric Loom needs the Gradle **daemon itself** on JDK 25 for this specific module (`Minecraft 26.2 requires Java 25 but Gradle is using 21`) - a per-module toolchain isn't enough. Run `:fabric-26.2:*` tasks with JDK 25 as the default `java`/`JAVA_HOME`; other tasks in the same invocation (spigot, bukkit-helper-*) still need JDK 21 as noted above, so build them in separate invocations if you need both.
- Forge 1.12.2 (oldgradle): JDK 8 strictly required
- Runtime targets: JDK 8 (1.16-), JDK 16 (1.17.x), JDK 17 (1.18-1.20.4), JDK 21 (1.20.5+)
- Runtime targets: JDK 8 (1.16-), JDK 16 (1.17.x), JDK 17 (1.18-1.20.4), JDK 21 (1.20.5-1.21.11), JDK 25 (26.2+)

**Build notes:**
- `gradle.properties` sets `org.gradle.parallel=false` and `org.gradle.daemon=false` — do not change these
- `snakeyaml` is pinned at 1.23 intentionally — newer versions break on Windows-encoded config files
- Shadow plugin is `com.gradleup.shadow` (main build, Gradle 9) — the older `io.github.goooler.shadow` is kept only in `forge-build/` (Gradle 8), since it crashes under Gradle 9's embedded Groovy 4
- **Shadow include/exclude gotcha (found via real server testing, 2026-07-19):** `com.gradleup.shadow`'s `dependency(...)` notation silently drops the old `'group::'` wildcard (empty artifact name AND version) instead of matching everything in that group - it just matches nothing, with no build error. This broke `DynmapCore`'s bundling of Jetty/javax.servlet/jakarta.xml.bind entirely (the whole embedded web server was missing from the jar) and `spigot`'s bundling of bstats, both silently, both only caught by actually loading the plugin on a real server (`NoClassDefFoundError`). Fixed by spelling out each artifact explicitly with a trailing colon (`'group:artifact:'`, empty version only - that form still works). **If you add a new `include(dependency('group::'))` anywhere in the main build, don't - it will silently do nothing; enumerate the actual artifacts instead**, and verify with `jar tf` on the built output that the relocated package (e.g. `org/dynmap/jetty/...`) actually has classes in it.
- As of Minecraft 26.1+, Mojang ships the server unobfuscated — there is no more "Spigot mappings" reobf layer, so `bukkit-helper-26-2` is written directly against real `net.minecraft.*` class/method names (see that module's source header comment) instead of the obfuscated calls seen in older `bukkit-helper-*` modules
- Fabric follows the same shift: Yarn mappings were discontinued as of 26.1 (nothing left to remap), so `fabric-26.2` uses plugin id `net.fabricmc.fabric-loom` (not `fabric-loom`), has **no `mappings` dependency at all**, uses plain `implementation`/`compileOnly` instead of `modImplementation`/`modCompileOnly`, and has no `remapJar` task - the plain `jar` task is the final artifact. Its own `com.gradleup.shadow`-provided `shadowJar` task is disabled (see build.gradle comment) since it isn't used and would otherwise blow past the 65535-zip-entry limit by bundling the full Fabric/Minecraft runtime classpath.

## Architecture

Expand All @@ -44,10 +63,10 @@ cd oldgradle
- `dynmap-api/` - Bukkit-specific public API

**Platform Implementations:**
- `spigot/` - Bukkit/PaperMC implementation (`DynmapPlugin.java`)
- `bukkit-helper-*` - Version-specific NMS code (one per MC version: 1.13-1.21)
- `fabric-*` - Fabric mod implementations (1.14.4-1.21.x)
- `forge-*` - Forge mod implementations (1.14.4-1.21.x); `forge-1.12.2` lives in `oldgradle/`
- `spigot/` - Bukkit/PaperMC implementation (`DynmapPlugin.java`); dispatches to the matching `bukkit-helper-*` at runtime via reflection (`Helper.java`) based on `Server#getVersion()` (legacy `1.x` servers) or `Server#getMinecraftVersion()` (26.x+ servers, called reflectively since it postdates spigot's own ancient compile-time Bukkit API)
- `bukkit-helper-*` - Version-specific NMS code (one per MC version: 1.13-1.21, plus `26-2` for Paper/Spigot 26.2)
- `fabric-*` - Fabric mod implementations (1.14.4-1.21.11, plus `26.2`)
- `forge-*` - Forge mod implementations (1.14.4-1.21.x), built from `forge-build/` (separate Gradle 8.14 build - see Build Commands); `forge-1.12.2` lives in `oldgradle/`/`oldbuilds/` instead

### Dependency Flow
```
Expand Down
20 changes: 15 additions & 5 deletions DynmapCore/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ eclipse {
}
}

sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8' // Need this here so eclipse task generates correctly.
java {
sourceCompatibility = JavaVersion.toVersion('1.8')
targetCompatibility = JavaVersion.toVersion('1.8')
}
compileJava.sourceCompatibility = '1.8'
compileJava.targetCompatibility = '1.8' // Need this here so eclipse task generates correctly.

dependencies {
implementation project(':DynmapCoreAPI')
Expand Down Expand Up @@ -68,17 +73,22 @@ shadowJar {
include(dependency('com.googlecode.json-simple:json-simple:'))
include(dependency('org.yaml:snakeyaml:'))
include(dependency('com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizer:'))
include(dependency('javax.servlet::'))
include(dependency('org.eclipse.jetty::'))
include(dependency('javax.servlet:javax.servlet-api:'))
include(dependency('org.eclipse.jetty:jetty-server:'))
include(dependency('org.eclipse.jetty:jetty-servlet:'))
include(dependency('org.eclipse.jetty:jetty-http:'))
include(dependency('org.eclipse.jetty:jetty-io:'))
include(dependency('org.eclipse.jetty:jetty-util:'))
include(dependency('org.eclipse.jetty:jetty-security:'))
include(dependency('org.eclipse.jetty.orbit:javax.servlet:'))
include(dependency('org.postgresql:postgresql:'))
include(dependency('io.github.linktosriram.s3lite:core:'))
include(dependency('io.github.linktosriram.s3lite:api:'))
include(dependency('io.github.linktosriram.s3lite:http-client-url-connection:'))
include(dependency('io.github.linktosriram.s3lite:http-client-spi:'))
include(dependency('io.github.linktosriram.s3lite:util:'))
include(dependency('jakarta.xml.bind::'))
include(dependency('com.sun.xml.bind::'))
include(dependency('jakarta.xml.bind:jakarta.xml.bind-api:'))
include(dependency('com.sun.xml.bind:jaxb-impl:'))
include(dependency(':DynmapCoreAPI'))
exclude("META-INF/maven/**")
exclude("META-INF/services/**")
Expand Down
Loading