All versions
BUG-C — DashboardManager:
BUG-D — BackupManager:
BUG-E — StressTestManager: always spawned at
BUG-F — WebDashboard: POST
BUG-G — AuditManager:
BUG-H — ProfileManager:
1.3.0
Release
NeuroLag 1.3.07 months ago 145
Compatibility
Minecraft: Java Edition
1.21.x
Platforms
Bukkit
Paper
Purpur
Spigot
Changes
[1.3.0] — 2026-03-15
🐛 Bug Fixes (9 fixes)
BUG-A — ZoneManager: world name dropped from cuboid zones (cross-world contamination)
- Root cause:
ZoneManager.initialize()parsed zone entries as"world x1 y1 z1 x2 y2 z2"(7 parts) but stored onlyparts[1]..parts[6]in a rawdouble[], silently discardingparts[0](the world name). - Effect: A zone defined in world
survivalwould also protect mobs at the same coordinates inworld_nether,world_the_end, and any other loaded world. Mobs in unintended worlds were immune to optimization. - Fix: Replaced
List<double[]>withList<CuboidZone>whereCuboidZoneis a JavarecordholdingString world+ 6 coordinates.isProtected()now checksmob.getWorld().getName().equals(z.world())before testing coordinates.
BUG-B — RegionOptimizer: negative chunk coordinates mapped to wrong region
- Root cause: Region index was computed as
chunkX / regionSizeusing Java integer division, which truncates toward zero. Chunk-1 / 16 = 0and chunk1 / 16 = 0, so chunks in the negative quadrant near spawn mapped to the same region keys as their positive counterparts. - Effect: The HOT region around coordinate origin (0,0) was effectively double-sized. Mobs at negative chunk coordinates near spawn were optimized even when a player was present nearby.
- Fix: Replaced
/withMath.floorDiv()in bothrefresh()andisHot().Math.floorDiv(-1, 16) = -1(correct),Math.floorDiv(15, 16) = 0(correct).
BUG-C — DashboardManager: type: HOLOGRAM silently did nothing
- Root cause:
DashboardManager.start()returned early withif (!dashboardType.equals("BOSSBAR")) return— no log message, no error. Admins who setdashboard.type: HOLOGRAMsaw no dashboard and received no indication why. - Fix: Added an explicit
plugin.getLogger().warning()message when a non-BOSSBAR type is configured, clearly stating that only BOSSBAR is currently supported.
BUG-D — BackupManager: creationTime() unreliable on Linux (ext4)
- Root cause: Backup rotation used
Files.readAttributes(...).creationTime()to sort backups oldest-first. The ext4 filesystem does not store inode creation time — it returnsInstant.EPOCH(1970-01-01) for all files, making sort order undefined. - Effect: On Linux servers, the "oldest" backup was selected randomly. The wrong backup could be deleted or restored.
- Fix: Replaced
creationTime()withFiles.getLastModifiedTime(), which is reliably stored on all filesystems. Also removed the now-unusedBasicFileAttributesimport.
BUG-E — StressTestManager: always spawned at world[0] spawn, ignoring admin's location
- Root cause:
start()hardcodedBukkit.getWorlds().get(0).getSpawnLocation()as the spawn center regardless of where the admin was or which world they were in. - Effect: Running
/nlag stresstestfrom the Nether or End spawned mobs in the Overworld at spawn, making the test meaningless for other worlds. - Fix:
start()now accepts aCommandSenderparameter. If the sender is aPlayer, mobs spawn centered on the player's current location in their current world. Console fallback remainsworld[0]spawn.
BUG-F — WebDashboard: POST /api/cmd blocked by CORS preflight (OPTIONS)
- Root cause: Browsers send an HTTP
OPTIONSpreflight request before a cross-originPOST. The server had no handler forOPTIONSand returned405 Method Not Allowed, causing the browser to abort the actualPOST. - Effect: The "send command" button in external web dashboards or REST clients always failed silently.
- Fix: Added
sendCorsOptions()helper that returns204 No ContentwithAccess-Control-Allow-Methods: GET, POST, OPTIONSandAccess-Control-Allow-Headers: Authorization, Content-Type. Both/api/statusand/api/cmdnow handleOPTIONSbefore auth checks.
BUG-G — AuditManager: getLivingEntities() / getLoadedChunks() called on main thread
- Root cause:
snapshot()iterated all entities and chunks synchronously on the main thread before handing off to async write. - Effect: On servers with many mobs or chunks, this could stall the main thread for several milliseconds during audit generation.
- Mitigation: Added an explicit code comment documenting this limitation and the reason it is required by the Bukkit API. A full async snapshot would require a thread-safe entity iterator not currently available in Paper's public API.
BUG-H — ProfileManager: clearProfile() required a full cfg.reload() to restore values
- Root cause:
switchProfile()overwrotecfg.tpsCritical,cfg.tpsMedium, andcfg.maxEntitiesdirectly.clearProfile()only setactiveProfile = nulland left a comment saying "caller is responsible for reloading", whichNeuroLag.javaimplemented by callingcfg.reload() + engine.restoreAll() + startMonitor()— a heavy operation that re-read disk and restarted all tasks. - Fix:
ProfileManagernow saves the three base values insaveBaseValues()(called in the constructor and after everycfg.reload()).clearProfile()restores them directly — no reload required./nlag profile clearno longer triggers a full config reload cycle.
BUG-I — LagEngine: "Animation Freezing" comment/name mismatched actual behavior
- Root cause: Feature F4 was named "Animation Freezing" throughout the codebase, but the implementation only called
mob.setVelocity(0,0,0). This stops movement drift but does not freeze animation frames — mobs still visibly idle-animate. - Fix: Renamed the in-code comment to "Velocity Freeze" and added a note explaining that true animation-frame control requires NMS/packet-level access not available through the standard Bukkit API.
🔧 Other Changes
plugin.yml: version bumped to 1.3.0; permission descriptions clarified;neurolag.stresstestdescription now mentions mobs spawn at your locationpom.xml: version bumped to 1.3.0StressTestManager.start()signature changed tostart(int count, int durationMinutes, CommandSender sender)— callers updated inNeuroLag.javaProfileManager.saveBaseValues()is now public soNeuroLag.javacan call it after/nlag reload
Projects on Modrinth are automatically available through a Maven repository for use with JVM build tools such as Gradle. To learn more about the Modrinth Maven API, click here.
Note: When available, you should use the creator's maven repo instead as it will have transitive dependency information that the Modrinth Maven API does not. You may also end up with duplicate dependencies if you use a mix of Modrinth and non-Modrinth Maven repositories for your dependencies, because the group identifier will be different when served through the Modrinth Maven API.
Maven coordinates:
Version ID:
build.gradle:
repositories {
exclusiveContent {
forRepository {
maven {
name = "Modrinth"
url = "https://api.modrinth.com/maven"
}
}
// forRepositories(fg.repository) // Uncomment when using ForgeGradle
filter {
includeGroup "maven.modrinth"
}
}
}
// Standard Gradle dependency
dependencies {
implementation "maven.modrinth:NQv4Mv8q:SZ23mbIE"
}
// Legacy Loom dependency
dependencies {
modImplementation "maven.modrinth:NQv4Mv8q:SZ23mbIE"
}

