From 534164e4e1a03c3b0e92043e64b823c5a6ea5f98 Mon Sep 17 00:00:00 2001 From: Leclowndu93150 Date: Fri, 24 Oct 2025 17:01:27 +0200 Subject: [PATCH 1/2] fix it not going down --- .../aspectslib/aether/AetherChunkData.java | 2 ++ .../corruption/CorruptionManager.java | 5 ++-- .../overgrown/aspectslib/data/AspectData.java | 14 +++++++--- .../aspectslib/data/BiomeAspectModifier.java | 28 +++++++++++++++---- .../aspectslib/data/BiomeAspectRegistry.java | 22 ++++++--------- 5 files changed, 46 insertions(+), 25 deletions(-) diff --git a/src/main/java/dev/overgrown/aspectslib/aether/AetherChunkData.java b/src/main/java/dev/overgrown/aspectslib/aether/AetherChunkData.java index 80c7e61..f96bf66 100644 --- a/src/main/java/dev/overgrown/aspectslib/aether/AetherChunkData.java +++ b/src/main/java/dev/overgrown/aspectslib/aether/AetherChunkData.java @@ -147,6 +147,8 @@ private void checkForDeadZone() { } public void recoverAether() { + if (world == null) return; + if (AetherManager.isDeadZone(world, chunkPos)) { recoverDeadZone(); return; diff --git a/src/main/java/dev/overgrown/aspectslib/corruption/CorruptionManager.java b/src/main/java/dev/overgrown/aspectslib/corruption/CorruptionManager.java index 99c2292..a54b95c 100644 --- a/src/main/java/dev/overgrown/aspectslib/corruption/CorruptionManager.java +++ b/src/main/java/dev/overgrown/aspectslib/corruption/CorruptionManager.java @@ -241,6 +241,7 @@ private static void consumeAspects(Identifier biomeId, AspectData currentAspects if (currentAmount > 0) { // Reduce target aspect by 1, increase Vitium by 1 + int previousVitiumAmount = currentAspects.getLevel(VITIUM_ID); BiomeAspectModifier.addBiomeModification(biomeId, targetAspect, -1); BiomeAspectModifier.addBiomeModification(biomeId, VITIUM_ID, 1); @@ -254,7 +255,7 @@ private static void consumeAspects(Identifier biomeId, AspectData currentAspects AspectsLib.LOGGER.info("Vitium consumed 1 {} from biome {}. {}: {} -> {}, Vitium: {} -> {}", targetAspect, biomeId, targetAspect, currentAmount, newAmount, - currentAspects.getLevel(VITIUM_ID), newVitiumAmount); + previousVitiumAmount, newVitiumAmount); // If aspect reaches 0, log it if (newAmount <= 0) { @@ -392,4 +393,4 @@ private static class CorruptionState { this.lastSeen = lastSeen; } } -} \ No newline at end of file +} diff --git a/src/main/java/dev/overgrown/aspectslib/data/AspectData.java b/src/main/java/dev/overgrown/aspectslib/data/AspectData.java index cd302dd..c9b9d94 100644 --- a/src/main/java/dev/overgrown/aspectslib/data/AspectData.java +++ b/src/main/java/dev/overgrown/aspectslib/data/AspectData.java @@ -244,8 +244,14 @@ public void setByName(String aspectName, int level) { * @param level The level to add. */ public void add(Identifier aspectId, int level) { - if (level > 0) { - this.aspects.merge(aspectId, level, Integer::sum); + if (level == 0) { + return; + } + int newLevel = this.aspects.getOrDefault(aspectId, 0) + level; + if (newLevel <= 0) { + this.aspects.removeInt(aspectId); + } else { + this.aspects.put(aspectId, newLevel); } } @@ -258,7 +264,7 @@ public void add(Identifier aspectId, int level) { public void addByName(String aspectName, int level) { Identifier aspectId = AspectManager.NAME_TO_ID.get(aspectName); if (aspectId != null) { - this.aspects.put(aspectId, this.aspects.getOrDefault(aspectId, 0) + level); + add(aspectId, level); } } @@ -296,4 +302,4 @@ public double calculateTotalRU() { public ResonanceCalculator.ResonanceResult calculateResonance() { return ResonanceCalculator.calculate(this); } -} \ No newline at end of file +} diff --git a/src/main/java/dev/overgrown/aspectslib/data/BiomeAspectModifier.java b/src/main/java/dev/overgrown/aspectslib/data/BiomeAspectModifier.java index ec6c954..3d15b3e 100644 --- a/src/main/java/dev/overgrown/aspectslib/data/BiomeAspectModifier.java +++ b/src/main/java/dev/overgrown/aspectslib/data/BiomeAspectModifier.java @@ -1,6 +1,7 @@ package dev.overgrown.aspectslib.data; import dev.overgrown.aspectslib.AspectsLib; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import net.minecraft.util.Identifier; import java.util.HashMap; import java.util.Map; @@ -9,10 +10,27 @@ public class BiomeAspectModifier { private static final Map biomeModifications = new HashMap<>(); public static void addBiomeModification(Identifier biomeId, Identifier aspectId, int amount) { - AspectData current = biomeModifications.getOrDefault(biomeId, AspectData.DEFAULT); - AspectData.Builder builder = new AspectData.Builder(current); - builder.add(aspectId, amount); - biomeModifications.put(biomeId, builder.build()); + if (amount == 0) { + return; + } + + AspectData current = biomeModifications.get(biomeId); + Object2IntOpenHashMap updatedMap = current != null + ? new Object2IntOpenHashMap<>(current.getMap()) + : new Object2IntOpenHashMap<>(); + + int newAmount = updatedMap.getOrDefault(aspectId, 0) + amount; + if (newAmount == 0) { + updatedMap.removeInt(aspectId); + } else { + updatedMap.put(aspectId, newAmount); + } + + if (updatedMap.isEmpty()) { + biomeModifications.remove(biomeId); + } else { + biomeModifications.put(biomeId, new AspectData(new Object2IntOpenHashMap<>(updatedMap))); + } } public static void drainAllAspects(Identifier biomeId, int amount) { @@ -102,4 +120,4 @@ public static void applyModificationsToRegistry() { AspectsLib.LOGGER.info("Applied biome aspect modifications to registry"); } -} \ No newline at end of file +} diff --git a/src/main/java/dev/overgrown/aspectslib/data/BiomeAspectRegistry.java b/src/main/java/dev/overgrown/aspectslib/data/BiomeAspectRegistry.java index 97ffe90..59f8919 100644 --- a/src/main/java/dev/overgrown/aspectslib/data/BiomeAspectRegistry.java +++ b/src/main/java/dev/overgrown/aspectslib/data/BiomeAspectRegistry.java @@ -15,8 +15,9 @@ public class BiomeAspectRegistry { public static AspectData register(RegistryKey key, AspectData aspect) { if(keyToAspect.containsKey(key)) { AspectData existing = keyToAspect.get(key); - existing.addAspect(aspect); - return aspect; + AspectData combined = existing.addAspect(aspect); + keyToAspect.put(key, combined); + return combined; } keyToAspect.put(key, aspect); return aspect; @@ -25,27 +26,20 @@ public static AspectData register(RegistryKey key, AspectData aspect) { public static AspectData register(Identifier id, AspectData aspect) { if(idToAspect.containsKey(id)) { AspectData existing = idToAspect.get(id); - existing.addAspect(aspect); - return aspect; + AspectData combined = existing.addAspect(aspect); + idToAspect.put(id, combined); + return combined; } idToAspect.put(id, aspect); return aspect; } public static void update(RegistryKey key, AspectData aspect) { - if(keyToAspect.containsKey(key)) { - AspectData old = keyToAspect.get(key); - keyToAspect.remove(key); - } - register(key, aspect); + keyToAspect.put(key, aspect); } public static void update(Identifier id, AspectData aspect) { - if(idToAspect.containsKey(id)) { - AspectData old = idToAspect.get(id); - idToAspect.remove(id); - } - register(id, aspect); + idToAspect.put(id, aspect); } protected static void remove(RegistryKey key) { From 6bc2e24b3774ae3b45e8150433509f476c436ee8 Mon Sep 17 00:00:00 2001 From: Leclowndu93150 Date: Fri, 24 Oct 2025 17:54:08 +0200 Subject: [PATCH 2/2] add corruption persistence --- .../aspectslib/corruption/CorruptionAPI.java | 25 +- .../corruption/CorruptionChunkData.java | 267 ++++++++++++++++++ .../corruption/CorruptionDataManager.java | 97 +++++++ .../corruption/CorruptionManager.java | 42 ++- .../corruption/CorruptionWorldState.java | 65 +++++ .../recipe/RecipeAspectCalculator.java | 1 + 6 files changed, 489 insertions(+), 8 deletions(-) create mode 100644 src/main/java/dev/overgrown/aspectslib/corruption/CorruptionChunkData.java create mode 100644 src/main/java/dev/overgrown/aspectslib/corruption/CorruptionDataManager.java create mode 100644 src/main/java/dev/overgrown/aspectslib/corruption/CorruptionWorldState.java diff --git a/src/main/java/dev/overgrown/aspectslib/corruption/CorruptionAPI.java b/src/main/java/dev/overgrown/aspectslib/corruption/CorruptionAPI.java index 439509a..1e320c3 100644 --- a/src/main/java/dev/overgrown/aspectslib/corruption/CorruptionAPI.java +++ b/src/main/java/dev/overgrown/aspectslib/corruption/CorruptionAPI.java @@ -3,7 +3,11 @@ import dev.overgrown.aspectslib.AspectsLib; import dev.overgrown.aspectslib.data.AspectData; import dev.overgrown.aspectslib.data.BiomeAspectModifier; +import net.minecraft.server.world.ServerWorld; import net.minecraft.util.Identifier; +import net.minecraft.util.math.ChunkPos; + +import java.util.Collection; public class CorruptionAPI { @@ -78,4 +82,23 @@ public static int getVitiumAmount(Identifier biomeId) { AspectData aspects = BiomeAspectModifier.getCombinedBiomeAspects(biomeId); return aspects.getLevel(vitiumId); } -} \ No newline at end of file + + /** + * Gets stored corruption tracking data for a chunk, if any has been recorded. + * @param world The server world the chunk belongs to + * @param chunkPos The chunk position + * @return The saved corruption data for the chunk, or {@code null} if none exists yet + */ + public static CorruptionChunkData getChunkData(ServerWorld world, ChunkPos chunkPos) { + return CorruptionDataManager.getChunkData(world, chunkPos); + } + + /** + * Gets a read-only view of all tracked corruption chunks for a world. + * @param world The server world to query + * @return Collection of chunk data entries; empty if nothing has been tracked yet + */ + public static Collection getTrackedChunks(ServerWorld world) { + return CorruptionDataManager.getAll(world); + } +} diff --git a/src/main/java/dev/overgrown/aspectslib/corruption/CorruptionChunkData.java b/src/main/java/dev/overgrown/aspectslib/corruption/CorruptionChunkData.java new file mode 100644 index 0000000..9bd640b --- /dev/null +++ b/src/main/java/dev/overgrown/aspectslib/corruption/CorruptionChunkData.java @@ -0,0 +1,267 @@ +package dev.overgrown.aspectslib.corruption; + +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.nbt.NbtList; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.ChunkPos; + +import java.util.Locale; + +public class CorruptionChunkData { + public enum Status { + PURE, + TAINTED, + CORRUPTED, + REGENERATING; + + public static Status fromOrdinal(int ordinal) { + Status[] values = values(); + if (ordinal < 0 || ordinal >= values.length) { + return PURE; + } + return values[ordinal]; + } + + public static Status fromString(String name) { + try { + return Status.valueOf(name.toUpperCase(Locale.ROOT)); + } catch (IllegalArgumentException ex) { + return PURE; + } + } + } + + private final ChunkPos chunkPos; + private Status status = Status.PURE; + private Identifier biomeId; + private long lastUpdatedTick; + private long lastCorruptedTick; + private long lastRegenerationTick; + private long lastCleanTick; + private int corruptionEvents; + private int regenerationEvents; + private int sculkPlacements; + private final Object2IntOpenHashMap aspectDeltas = new Object2IntOpenHashMap<>(); + private final Object2IntOpenHashMap aetherConsumed = new Object2IntOpenHashMap<>(); + + public CorruptionChunkData(ChunkPos chunkPos) { + this.chunkPos = chunkPos; + } + + public ChunkPos getChunkPos() { + return chunkPos; + } + + public Status getStatus() { + return status; + } + + public Identifier getBiomeId() { + return biomeId; + } + + public long getLastUpdatedTick() { + return lastUpdatedTick; + } + + public long getLastCorruptedTick() { + return lastCorruptedTick; + } + + public long getLastRegenerationTick() { + return lastRegenerationTick; + } + + public long getLastCleanTick() { + return lastCleanTick; + } + + public int getCorruptionEvents() { + return corruptionEvents; + } + + public int getRegenerationEvents() { + return regenerationEvents; + } + + public int getSculkPlacements() { + return sculkPlacements; + } + + public Object2IntOpenHashMap getAspectDeltas() { + return aspectDeltas; + } + + public Object2IntOpenHashMap getAetherConsumed() { + return aetherConsumed; + } + + public boolean setStatus(Status newStatus, Identifier biomeId, long tick) { + boolean changed = false; + if (biomeId != null) { + this.biomeId = biomeId; + } + + if (this.status != newStatus) { + Status previous = this.status; + this.status = newStatus; + changed = true; + + if (newStatus == Status.CORRUPTED) { + corruptionEvents++; + lastCorruptedTick = tick; + } + + if (previous == Status.CORRUPTED && newStatus != Status.CORRUPTED) { + regenerationEvents++; + lastRegenerationTick = tick; + } + + if (newStatus == Status.PURE) { + lastCleanTick = tick; + } + } + + lastUpdatedTick = tick; + return changed; + } + + public boolean recordAspectDelta(Identifier aspectId, int delta, Identifier biomeId, long tick) { + if (delta == 0) { + return false; + } + + boolean changed = false; + if (biomeId != null) { + this.biomeId = biomeId; + } + + int newAmount = aspectDeltas.getOrDefault(aspectId, 0) + delta; + if (newAmount == 0) { + changed = aspectDeltas.containsKey(aspectId); + aspectDeltas.removeInt(aspectId); + } else { + aspectDeltas.put(aspectId, newAmount); + changed = true; + } + + lastUpdatedTick = tick; + return changed; + } + + public boolean recordAetherConsumption(Identifier aspectId, int amount, Identifier biomeId, long tick) { + if (amount <= 0) { + return false; + } + + if (biomeId != null) { + this.biomeId = biomeId; + } + + aetherConsumed.addTo(aspectId, amount); + lastUpdatedTick = tick; + return true; + } + + public boolean recordSculkPlacement(int count, long tick) { + if (count <= 0) { + return false; + } + + sculkPlacements += count; + lastUpdatedTick = tick; + return true; + } + + public boolean isPrunable() { + return status == Status.PURE + && aspectDeltas.isEmpty() + && aetherConsumed.isEmpty() + && sculkPlacements == 0 + && corruptionEvents == 0 + && regenerationEvents == 0; + } + + public NbtCompound toNbt() { + NbtCompound nbt = new NbtCompound(); + nbt.putInt("State", status.ordinal()); + if (biomeId != null) { + nbt.putString("BiomeId", biomeId.toString()); + } + nbt.putLong("LastUpdated", lastUpdatedTick); + nbt.putLong("LastCorrupted", lastCorruptedTick); + nbt.putLong("LastRegeneration", lastRegenerationTick); + nbt.putLong("LastClean", lastCleanTick); + nbt.putInt("CorruptionEvents", corruptionEvents); + nbt.putInt("RegenerationEvents", regenerationEvents); + nbt.putInt("SculkPlacements", sculkPlacements); + + if (!aspectDeltas.isEmpty()) { + NbtList aspectList = new NbtList(); + for (Object2IntMap.Entry entry : aspectDeltas.object2IntEntrySet()) { + NbtCompound aspectNbt = new NbtCompound(); + aspectNbt.putString("Id", entry.getKey().toString()); + aspectNbt.putInt("Delta", entry.getIntValue()); + aspectList.add(aspectNbt); + } + nbt.put("AspectDeltas", aspectList); + } + + if (!aetherConsumed.isEmpty()) { + NbtList aetherList = new NbtList(); + for (Object2IntMap.Entry entry : aetherConsumed.object2IntEntrySet()) { + NbtCompound aetherNbt = new NbtCompound(); + aetherNbt.putString("Id", entry.getKey().toString()); + aetherNbt.putInt("Amount", entry.getIntValue()); + aetherList.add(aetherNbt); + } + nbt.put("AetherConsumed", aetherList); + } + + return nbt; + } + + public static CorruptionChunkData fromNbt(ChunkPos chunkPos, NbtCompound nbt) { + CorruptionChunkData data = new CorruptionChunkData(chunkPos); + data.status = Status.fromOrdinal(nbt.getInt("State")); + if (nbt.contains("BiomeId", NbtElement.STRING_TYPE)) { + data.biomeId = Identifier.tryParse(nbt.getString("BiomeId")); + } + data.lastUpdatedTick = nbt.getLong("LastUpdated"); + data.lastCorruptedTick = nbt.getLong("LastCorrupted"); + data.lastRegenerationTick = nbt.getLong("LastRegeneration"); + data.lastCleanTick = nbt.getLong("LastClean"); + data.corruptionEvents = nbt.getInt("CorruptionEvents"); + data.regenerationEvents = nbt.getInt("RegenerationEvents"); + data.sculkPlacements = nbt.getInt("SculkPlacements"); + + if (nbt.contains("AspectDeltas", NbtElement.LIST_TYPE)) { + NbtList aspectList = nbt.getList("AspectDeltas", NbtElement.COMPOUND_TYPE); + for (int i = 0; i < aspectList.size(); i++) { + NbtCompound aspectNbt = aspectList.getCompound(i); + Identifier aspectId = Identifier.tryParse(aspectNbt.getString("Id")); + int delta = aspectNbt.getInt("Delta"); + if (aspectId != null && delta != 0) { + data.aspectDeltas.put(aspectId, delta); + } + } + } + + if (nbt.contains("AetherConsumed", NbtElement.LIST_TYPE)) { + NbtList aetherList = nbt.getList("AetherConsumed", NbtElement.COMPOUND_TYPE); + for (int i = 0; i < aetherList.size(); i++) { + NbtCompound aetherNbt = aetherList.getCompound(i); + Identifier aspectId = Identifier.tryParse(aetherNbt.getString("Id")); + int amount = aetherNbt.getInt("Amount"); + if (aspectId != null && amount > 0) { + data.aetherConsumed.put(aspectId, amount); + } + } + } + + return data; + } +} diff --git a/src/main/java/dev/overgrown/aspectslib/corruption/CorruptionDataManager.java b/src/main/java/dev/overgrown/aspectslib/corruption/CorruptionDataManager.java new file mode 100644 index 0000000..92641bb --- /dev/null +++ b/src/main/java/dev/overgrown/aspectslib/corruption/CorruptionDataManager.java @@ -0,0 +1,97 @@ +package dev.overgrown.aspectslib.corruption; + +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.world.PersistentStateManager; + +import java.util.Collection; +import java.util.List; + +public final class CorruptionDataManager { + private static final String CORRUPTION_STATE_KEY = "aspectslib_corruption"; + + private CorruptionDataManager() { + } + + public static CorruptionWorldState getWorldState(ServerWorld world) { + PersistentStateManager manager = world.getPersistentStateManager(); + return manager.getOrCreate( + CorruptionWorldState::fromNbt, + CorruptionWorldState::new, + CORRUPTION_STATE_KEY + ); + } + + public static void updateChunkStatus(ServerWorld world, ChunkPos chunkPos, Identifier biomeId, + CorruptionChunkData.Status status, long tick) { + CorruptionWorldState state = getWorldState(world); + CorruptionChunkData data = state.getOrCreate(chunkPos); + boolean changed = data.setStatus(status, biomeId, tick); + if (changed) { + state.markDirty(); + } + if (data.isPrunable()) { + state.pruneIfClean(chunkPos); + } + } + + public static void recordAspectDelta(ServerWorld world, ChunkPos chunkPos, Identifier biomeId, + Identifier aspectId, int delta, long tick) { + if (delta == 0) { + return; + } + + CorruptionWorldState state = getWorldState(world); + CorruptionChunkData data = state.getOrCreate(chunkPos); + boolean changed = data.recordAspectDelta(aspectId, delta, biomeId, tick); + if (changed) { + state.markDirty(); + } + if (data.isPrunable()) { + state.pruneIfClean(chunkPos); + } + } + + public static void recordBulkAspectDelta(ServerWorld world, Collection chunkPositions, + Identifier biomeId, Identifier aspectId, int delta, long tick) { + for (ChunkPos chunkPos : chunkPositions) { + recordAspectDelta(world, chunkPos, biomeId, aspectId, delta, tick); + } + } + + public static void recordAetherConsumption(ServerWorld world, ChunkPos chunkPos, Identifier biomeId, + Identifier aspectId, int amount, long tick) { + if (amount <= 0) { + return; + } + + CorruptionWorldState state = getWorldState(world); + CorruptionChunkData data = state.getOrCreate(chunkPos); + boolean changed = data.recordAetherConsumption(aspectId, amount, biomeId, tick); + if (changed) { + state.markDirty(); + } + } + + public static void recordSculkPlacement(ServerWorld world, ChunkPos chunkPos, int count, long tick) { + if (count <= 0) { + return; + } + + CorruptionWorldState state = getWorldState(world); + CorruptionChunkData data = state.getOrCreate(chunkPos); + boolean changed = data.recordSculkPlacement(count, tick); + if (changed) { + state.markDirty(); + } + } + + public static Collection getAll(ServerWorld world) { + return List.copyOf(getWorldState(world).getAll()); + } + + public static CorruptionChunkData getChunkData(ServerWorld world, ChunkPos chunkPos) { + return getWorldState(world).get(chunkPos); + } +} diff --git a/src/main/java/dev/overgrown/aspectslib/corruption/CorruptionManager.java b/src/main/java/dev/overgrown/aspectslib/corruption/CorruptionManager.java index a54b95c..bd7ed0c 100644 --- a/src/main/java/dev/overgrown/aspectslib/corruption/CorruptionManager.java +++ b/src/main/java/dev/overgrown/aspectslib/corruption/CorruptionManager.java @@ -24,6 +24,10 @@ public class CorruptionManager { private static final Map CORRUPTION_STATES = new HashMap<>(); private static final Random RANDOM = new Random(); + private record AspectConsumptionResult(Identifier aspectId, int previousAmount, + int newAmount, int vitiumBefore, int vitiumAfter) { + } + // Configuration private static final int CORRUPTION_CHECK_INTERVAL = 200; // 10 seconds private static final int ASPECT_CONSUMPTION_INTERVAL = 400; // 20 seconds @@ -97,6 +101,7 @@ private static void processBiomeCorruption(ServerWorld world, Identifier biomeId int currentVitiumAmount = currentBiomeAspects.getLevel(VITIUM_ID); if (currentVitiumAmount == 0) { + updateChunkStatuses(world, biomesChunks, biomeId, CorruptionChunkData.Status.PURE, currentTime); // Pure biome - no Vitium, clear corruption state updateCorruptionState(biomeId, CorruptionState.PURE, world.getTime()); return; @@ -124,6 +129,7 @@ private static void processBiomeCorruption(ServerWorld world, Identifier biomeId // Corruption occurs when Vitium is GREATER THAN the total of other aspects // So for 15 total other aspects, you need 16 or more Vitium to corrupt if (currentVitiumAmount > baseTotalForComparison) { + updateChunkStatuses(world, biomesChunks, biomeId, CorruptionChunkData.Status.CORRUPTED, currentTime); // Corrupted biome CorruptionState state = CORRUPTION_STATES.computeIfAbsent(biomeId, id -> new CorruptionState(CorruptionState.CORRUPTED, world.getTime())); @@ -136,7 +142,7 @@ private static void processBiomeCorruption(ServerWorld world, Identifier biomeId state.lastSeen = world.getTime(); // Process corruption effects for this biome's chunks - processCorruption(world, biomesChunks, biomeId, currentBiomeAspects, currentTime); + processCorruption(world, biomesChunks, biomeId, representativeChunk, currentBiomeAspects, currentTime); // Check if only Vitium remains - start consuming aether if (currentTotalOtherAspects == 0 && currentVitiumAmount > 0) { @@ -145,6 +151,7 @@ private static void processBiomeCorruption(ServerWorld world, Identifier biomeId } } } else { + updateChunkStatuses(world, biomesChunks, biomeId, CorruptionChunkData.Status.TAINTED, currentTime); // Tainted biome (has Vitium but not enough to corrupt) updateCorruptionState(biomeId, CorruptionState.TAINTED, world.getTime()); @@ -153,6 +160,13 @@ private static void processBiomeCorruption(ServerWorld world, Identifier biomeId } } + private static void updateChunkStatuses(ServerWorld world, List chunkPositions, + Identifier biomeId, CorruptionChunkData.Status status, long tick) { + for (ChunkPos chunkPos : chunkPositions) { + CorruptionDataManager.updateChunkStatus(world, chunkPos, biomeId, status, tick); + } + } + private static int calculateTotalOtherAspects(AspectData aspects) { int total = 0; for (Identifier aspectId : aspects.getAspectIds()) { @@ -164,21 +178,27 @@ private static int calculateTotalOtherAspects(AspectData aspects) { } private static void processCorruption(ServerWorld world, List biomesChunks, - Identifier biomeId, AspectData currentAspects, + Identifier biomeId, ChunkPos representativeChunk, AspectData currentAspects, long currentTime) { // Spread sculk in random chunks if (RANDOM.nextInt(100) < SCULK_SPREAD_CHANCE) { ChunkPos randomChunk = biomesChunks.get(RANDOM.nextInt(biomesChunks.size())); - spreadSculk(world, randomChunk); + spreadSculk(world, randomChunk, currentTime); } // Consume aspects ONCE per biome (not per chunk!) if (currentTime % ASPECT_CONSUMPTION_INTERVAL == 0) { - consumeAspects(biomeId, currentAspects); + consumeAspects(biomeId, currentAspects).ifPresent(result -> { + CorruptionDataManager.recordAspectDelta(world, representativeChunk, biomeId, result.aspectId(), -1, currentTime); + int vitiumDelta = result.vitiumAfter() - result.vitiumBefore(); + if (vitiumDelta != 0) { + CorruptionDataManager.recordAspectDelta(world, representativeChunk, biomeId, VITIUM_ID, vitiumDelta, currentTime); + } + }); } } - private static void spreadSculk(ServerWorld world, ChunkPos chunkPos) { + private static void spreadSculk(ServerWorld world, ChunkPos chunkPos, long currentTime) { int sculkCount = 0; for (int i = 0; i < 3; i++) { // Try 3 times to place sculk @@ -192,6 +212,7 @@ private static void spreadSculk(ServerWorld world, ChunkPos chunkPos) { world.getBlockState(pos.down()).isOpaque()) { world.setBlockState(pos, Blocks.SCULK.getDefaultState()); sculkCount++; + CorruptionDataManager.recordSculkPlacement(world, chunkPos, 1, currentTime); // Play sculk spread sound effect world.playSound( @@ -220,7 +241,7 @@ private static BlockPos findSurfacePosition(World world, BlockPos pos) { return null; } - private static void consumeAspects(Identifier biomeId, AspectData currentAspects) { + private static Optional consumeAspects(Identifier biomeId, AspectData currentAspects) { List nonVitiumAspects = new ArrayList<>(); // Find all non-Vitium aspects with positive amounts @@ -232,7 +253,7 @@ private static void consumeAspects(Identifier biomeId, AspectData currentAspects if (nonVitiumAspects.isEmpty()) { AspectsLib.LOGGER.debug("No non-Vitium aspects left to consume in biome {}", biomeId); - return; + return Optional.empty(); } // Pick a random aspect to consume @@ -262,7 +283,11 @@ private static void consumeAspects(Identifier biomeId, AspectData currentAspects AspectsLib.LOGGER.info("Aspect {} completely consumed in biome {}! Moving to next aspect.", targetAspect, biomeId); } + + return Optional.of(new AspectConsumptionResult(targetAspect, currentAmount, newAmount, previousVitiumAmount, newVitiumAmount)); } + + return Optional.empty(); } private static void processAetherConsumption(ServerWorld world, ChunkPos chunkPos, @@ -293,6 +318,8 @@ private static void processAetherConsumption(ServerWorld world, ChunkPos chunkPo BiomeAspectModifier.addBiomeModification(biomeId, VITIUM_ID, 1); // Apply modifications to registry BiomeAspectModifier.applyModificationsToRegistry(); + CorruptionDataManager.recordAetherConsumption(world, chunkPos, biomeId, targetAspect, 1, currentTime); + CorruptionDataManager.recordAspectDelta(world, chunkPos, biomeId, VITIUM_ID, 1, currentTime); AspectsLib.LOGGER.info("Consumed 1 {} Aether from chunk {}, total aether remaining: {}", targetAspect, chunkPos, totalAether - 1); @@ -307,6 +334,7 @@ private static void processAetherConsumption(ServerWorld world, ChunkPos chunkPo // Erase all aspects from the biome eraseAllAspects(biomeId); + CorruptionDataManager.updateChunkStatus(world, chunkPos, biomeId, CorruptionChunkData.Status.REGENERATING, currentTime); AspectsLib.LOGGER.info("Created {} dead zone at {} in biome {}", permanent ? "permanent" : "temporary", chunkPos, biomeId); diff --git a/src/main/java/dev/overgrown/aspectslib/corruption/CorruptionWorldState.java b/src/main/java/dev/overgrown/aspectslib/corruption/CorruptionWorldState.java new file mode 100644 index 0000000..d228572 --- /dev/null +++ b/src/main/java/dev/overgrown/aspectslib/corruption/CorruptionWorldState.java @@ -0,0 +1,65 @@ +package dev.overgrown.aspectslib.corruption; + +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.nbt.NbtList; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.world.PersistentState; + +import java.util.Collection; + +public class CorruptionWorldState extends PersistentState { + private final Long2ObjectMap chunkData = new Long2ObjectOpenHashMap<>(); + + public CorruptionChunkData getOrCreate(ChunkPos chunkPos) { + return chunkData.computeIfAbsent(chunkPos.toLong(), pos -> { + markDirty(); + return new CorruptionChunkData(chunkPos); + }); + } + + public CorruptionChunkData get(ChunkPos chunkPos) { + return chunkData.get(chunkPos.toLong()); + } + + public void pruneIfClean(ChunkPos chunkPos) { + long key = chunkPos.toLong(); + CorruptionChunkData data = chunkData.get(key); + if (data != null && data.isPrunable()) { + chunkData.remove(key); + markDirty(); + } + } + + public Collection getAll() { + return chunkData.values(); + } + + @Override + public NbtCompound writeNbt(NbtCompound nbt) { + NbtList chunkList = new NbtList(); + for (Long2ObjectMap.Entry entry : chunkData.long2ObjectEntrySet()) { + NbtCompound chunkNbt = entry.getValue().toNbt(); + chunkNbt.putLong("Pos", entry.getLongKey()); + chunkList.add(chunkNbt); + } + nbt.put("Chunks", chunkList); + return nbt; + } + + public static CorruptionWorldState fromNbt(NbtCompound nbt) { + CorruptionWorldState state = new CorruptionWorldState(); + if (nbt.contains("Chunks", NbtElement.LIST_TYPE)) { + NbtList chunkList = nbt.getList("Chunks", NbtElement.COMPOUND_TYPE); + for (int i = 0; i < chunkList.size(); i++) { + NbtCompound chunkNbt = chunkList.getCompound(i); + ChunkPos chunkPos = new ChunkPos(chunkNbt.getLong("Pos")); + CorruptionChunkData data = CorruptionChunkData.fromNbt(chunkPos, chunkNbt); + state.chunkData.put(chunkPos.toLong(), data); + } + } + return state; + } +} diff --git a/src/main/java/dev/overgrown/aspectslib/recipe/RecipeAspectCalculator.java b/src/main/java/dev/overgrown/aspectslib/recipe/RecipeAspectCalculator.java index aa80308..49247cb 100644 --- a/src/main/java/dev/overgrown/aspectslib/recipe/RecipeAspectCalculator.java +++ b/src/main/java/dev/overgrown/aspectslib/recipe/RecipeAspectCalculator.java @@ -18,6 +18,7 @@ import net.minecraft.text.Text; import net.minecraft.util.Identifier; import net.minecraft.util.collection.DefaultedList; +import net.minecraft.world.PersistentState; import java.util.*; import java.util.concurrent.ConcurrentHashMap;