fix(generator): fix 6 code safety bugs

- Fix ProfileValue NFE - add isEmpty guards in asInt/asDouble
- Fix GeneratorProfileManager - catch IllegalArgumentException on createBlockData
- Fix GeneratorState - replace synchronized LinkedList with ArrayList
- Fix BlockSelector - guard against empty styles list
- Fix multiplayer runaway generation - cap blocks/tick, remove redundant generate
- Fix subList.clear() on LinkedList - resolved by ArrayList conversion
This commit is contained in:
loki5512344 2026-06-08 14:34:03 +02:00
parent 0b6409397b
commit ba421dc211
6 changed files with 24 additions and 9 deletions

View file

@ -130,7 +130,12 @@ public class GeneratorProfileManager {
String materialName = Config.GENERATION.getString(materialPath); String materialName = Config.GENERATION.getString(materialPath);
int chance = Config.GENERATION.getInt(chancePath, 1); int chance = Config.GENERATION.getInt(chancePath, 1);
BlockData blockData = org.bukkit.Bukkit.createBlockData(materialName); BlockData blockData;
try {
blockData = org.bukkit.Bukkit.createBlockData(materialName);
} catch (IllegalArgumentException e) {
continue; // skip invalid material names
}
generator.state.specialChances.put(blockData, (double) chance); generator.state.specialChances.put(blockData, (double) chance);
} }

View file

@ -31,8 +31,9 @@ public class GeneratorState {
// Time state // Time state
public Instant start; public Instant start;
// Block history (sync list: all mutations occur on the main thread; avoids accidental CME if extended later) // Block history — all access is main-thread-only (Bukkit scheduler runTimer), so no sync wrapper needed.
public final List<Block> history = Collections.synchronizedList(new LinkedList<>()); // Iteration is safe without external synchronization as all mutations happen on the same thread.
public final List<Block> history = new ArrayList<>();
public int lastPositionIndexPlayer = -1; public int lastPositionIndexPlayer = -1;
// Schematic state // Schematic state

View file

@ -45,16 +45,18 @@ public class Profile {
} }
/** /**
* @return The value as a double. * @return The value as a double. Returns 0.0 if the value is empty.
*/ */
public double asDouble() { public double asDouble() {
if (value.isEmpty()) return 0.0;
return Double.parseDouble(value); return Double.parseDouble(value);
} }
/** /**
* @return The value as an int. * @return The value as an int. Returns 0 if the value is empty.
*/ */
public int asInt() { public int asInt() {
if (value.isEmpty()) return 0;
return Integer.parseInt(value); return Integer.parseInt(value);
} }
} }

View file

@ -9,6 +9,8 @@ import org.bukkit.block.data.BlockData;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.List;
/** /**
* Handles selection of block types and materials for parkour generation. * Handles selection of block types and materials for parkour generation.
*/ */
@ -29,7 +31,11 @@ public class BlockSelector {
public BlockData selectBlockData() { public BlockData selectBlockData() {
Style style = Registry.getStyle(generator.profile.get("style").value()); Style style = Registry.getStyle(generator.profile.get("style").value());
if (style == null) { if (style == null) {
generator.profile.set("style", Registry.getStyles().stream().findFirst().orElseThrow().getName()); List<Style> styles = Registry.getStyles();
if (styles.isEmpty()) {
return Material.STONE.createBlockData();
}
generator.profile.set("style", styles.stream().findFirst().orElseThrow().getName());
return selectBlockData(); return selectBlockData();
} }
for (int i = 0; i < MAX_STYLE_PICKS; i++) { for (int i = 0; i < MAX_STYLE_PICKS; i++) {

View file

@ -92,7 +92,8 @@ public class LifecycleTickManager {
if (currentBlocks < targetBlocks) { if (currentBlocks < targetBlocks) {
int needed = targetBlocks - currentBlocks; int needed = targetBlocks - currentBlocks;
generator.generate(needed); // Cap generation per tick to prevent runaway generation in multiplayer
generator.generate(Math.min(needed, 5));
} }
} }
} }

View file

@ -82,8 +82,8 @@ public class PlayerInteractionHandler {
} }
} }
// Generate more blocks after scoring // Block generation is handled by LifecycleTickManager.maintainBlockLead()
generator.generate(1); // to avoid runaway generation in multiplayer (N players = N generates per tick otherwise).
} }
/** /**