neoforge-1.21.1: runtime fixes and build improvements

- Fix ServerTickEvent: register listener for ServerTickEvent.Post instead of
  abstract ServerTickEvent base class (NeoForge 21.x requirement)
- Fix null BlockGetter: replace null with EmptyBlockGetter.INSTANCE in
  isSolidRender() and propagatesSkylightDown() calls (1.21+ actually uses param)
- Fix chunk iteration: replace direct visibleChunkMap field access with
  getChunks() iteration and getChunkToSend() with getLatestChunk() (1.21.1 API)
- Build: fix shadowJar config to properly bundle DynmapCore into fat jar
  (was producing 80KB hollow jar missing all core classes)
- Build: cap Gradle daemon and forked javac heap to prevent OOM on large hosts
- Tested: server starts cleanly, Dynmap web UI accessible, maps rendered
This commit is contained in:
Sulkta 2026-03-07 09:41:38 -08:00
parent 4fd6b31aa6
commit bd07616505
3 changed files with 26 additions and 11 deletions

View file

@ -30,10 +30,15 @@ minecraft {
} }
} }
configurations {
shadow
implementation.extendsFrom shadow
}
dependencies { dependencies {
implementation 'net.neoforged:neoforge:21.1.219' implementation 'net.neoforged:neoforge:21.1.219'
// DynmapCore and DynmapCoreAPI as local jars (build these separately) // DynmapCore bundled into the fat jar via shadow config
implementation fileTree(dir: 'libs', include: ['*.jar']) shadow fileTree(dir: 'libs', include: ['*.jar'])
} }
processResources { processResources {
@ -50,7 +55,13 @@ shadowJar {
mergeServiceFiles() mergeServiceFiles()
archiveBaseName = 'Dynmap' archiveBaseName = 'Dynmap'
archiveVersion = project.version
archiveClassifier = 'neoforge-1.21.1' archiveClassifier = 'neoforge-1.21.1'
// Exclude signature files that cause security exceptions
exclude 'META-INF/*.RSA'
exclude 'META-INF/*.SF'
exclude 'META-INF/*.DSA'
} }
tasks.jar { tasks.jar {
@ -66,4 +77,11 @@ tasks.jar {
} }
} }
build.dependsOn(jar) // Build the fat jar, not the thin one
build.dependsOn(shadowJar)
// Cap memory on forked javac process (prevents OOM on large NeoForge compile)
tasks.withType(JavaCompile).configureEach {
options.fork = true
options.forkOptions.memoryMaximumSize = '3g'
}

View file

@ -1,4 +1,4 @@
org.gradle.jvmargs=-Xmx4G org.gradle.jvmargs=-Xmx2G
org.gradle.daemon=false org.gradle.daemon=false
org.gradle.parallel=false org.gradle.parallel=false
# GitHub user and password (workaround for OSSRH beeing sunset - https://central.sonatype.org/pages/ossrh-eol/ # GitHub user and password (workaround for OSSRH beeing sunset - https://central.sonatype.org/pages/ossrh-eol/

View file

@ -245,7 +245,7 @@ public class DynmapPlugin
} }
int lightAtten = 15; int lightAtten = 15;
try { // Workaround for mods with broken block state logic... try { // Workaround for mods with broken block state logic...
lightAtten = bs.isSolidRender(null, BlockPos.ZERO) ? 15 : (bs.propagatesSkylightDown(null, BlockPos.ZERO) ? 0 : 1); lightAtten = bs.isSolidRender(net.minecraft.world.level.EmptyBlockGetter.INSTANCE, BlockPos.ZERO) ? 15 : (bs.propagatesSkylightDown(net.minecraft.world.level.EmptyBlockGetter.INSTANCE, BlockPos.ZERO) ? 0 : 1);
} catch (Exception x) { } catch (Exception x) {
Log.warning(String.format("Exception while checking lighting data for block state: %s[%s]", bn, statename)); Log.warning(String.format("Exception while checking lighting data for block state: %s[%s]", bn, statename));
Log.verboseinfo("Exception: " + x.toString()); Log.verboseinfo("Exception: " + x.toString());
@ -913,7 +913,7 @@ public class DynmapPlugin
} }
@SubscribeEvent @SubscribeEvent
public void tickEvent(ServerTickEvent event) { public void tickEvent(ServerTickEvent.Post event) {
cur_tick_starttime = System.nanoTime(); cur_tick_starttime = System.nanoTime();
long elapsed = cur_tick_starttime - lasttick; long elapsed = cur_tick_starttime - lasttick;
lasttick = cur_tick_starttime; lasttick = cur_tick_starttime;
@ -1850,13 +1850,10 @@ public class DynmapPlugin
for (ServerLevel world : server.getAllLevels()) { for (ServerLevel world : server.getAllLevels()) {
ForgeWorld fw = getWorld(world); ForgeWorld fw = getWorld(world);
if (fw == null) continue; if (fw == null) continue;
Long2ObjectLinkedOpenHashMap<ChunkHolder> chunks = world.getChunkSource().chunkMap.visibleChunkMap; for (ChunkHolder ch : world.getChunkSource().chunkMap.visibleChunkMap.values()) {
for (Entry<Long, ChunkHolder> k : chunks.long2ObjectEntrySet()) {
long key = k.getKey().longValue();
ChunkHolder ch = k.getValue();
ChunkAccess c = null; ChunkAccess c = null;
try { try {
c = ch.getChunkToSend(); c = ch.getLatestChunk();
} catch (Exception x) { } } catch (Exception x) { }
if (c == null) continue; if (c == null) continue;
ChunkStatus cs = c.getPersistedStatus(); ChunkStatus cs = c.getPersistedStatus();