diff --git a/src/main/java/carpet/fakes/ThreadedAnvilChunkStorageInterface.java b/src/main/java/carpet/fakes/ThreadedAnvilChunkStorageInterface.java index 08d070ad..d2fff722 100644 --- a/src/main/java/carpet/fakes/ThreadedAnvilChunkStorageInterface.java +++ b/src/main/java/carpet/fakes/ThreadedAnvilChunkStorageInterface.java @@ -3,8 +3,9 @@ package carpet.fakes; import net.minecraft.util.math.ChunkPos; import java.util.List; +import java.util.Map; public interface ThreadedAnvilChunkStorageInterface { - void regenerateChunkRegion(List requestedChunks); + Map regenerateChunkRegion(List requestedChunks); } diff --git a/src/main/java/carpet/mixins/ThreadedAnvilChunkStorage_scarpetChunkCreationMixin.java b/src/main/java/carpet/mixins/ThreadedAnvilChunkStorage_scarpetChunkCreationMixin.java index f833b8e2..988f10ad 100644 --- a/src/main/java/carpet/mixins/ThreadedAnvilChunkStorage_scarpetChunkCreationMixin.java +++ b/src/main/java/carpet/mixins/ThreadedAnvilChunkStorage_scarpetChunkCreationMixin.java @@ -18,6 +18,7 @@ import net.minecraft.util.thread.ThreadExecutor; import net.minecraft.world.chunk.Chunk; import net.minecraft.world.chunk.ChunkStatus; import net.minecraft.world.chunk.WorldChunk; +import net.minecraft.world.storage.RegionFile; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -25,6 +26,9 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -84,10 +88,40 @@ public abstract class ThreadedAnvilChunkStorage_scarpetChunkCreationMixin implem world.getServer().execute( () -> CHUNK_GENERATED.onChunkGenerated(world, chunk) ); } - @Override - public void regenerateChunkRegion(List requestedChunks) + private boolean chunkExists(ChunkPos chpos, Map regionCache) { + String currentRegionName = "r." + chpos.getRegionX() + "." + chpos.getRegionZ() + ".mca"; + if (regionCache != null && regionCache.containsKey(currentRegionName)) + { + RegionFile region = regionCache.get(currentRegionName); + if (region == null) return false; + return region.hasChunk(chpos); + } + Path regionPath = world.getDimension().getType().getSaveDirectory( + world.getSaveHandler().getWorldDir() + ).toPath().resolve("region"); + Path regionFilePath = regionPath.resolve(currentRegionName); + File regionFile = regionFilePath.toFile(); + if (!regionFile.exists()) + { + if (regionCache != null) regionCache.put(currentRegionName, null); + return false; + } + try + { + RegionFile region = new RegionFile(regionFile, regionPath.toFile()); + if (regionCache != null) regionCache.put(currentRegionName, region); + return region.hasChunk(chpos); + } + catch (IOException ignored) { } + return true; + } + @Override + public Map regenerateChunkRegion(List requestedChunks) + { + Map report = new HashMap<>(); + report.put("requested_chunks",requestedChunks.size()); //flushning all tasks on the thread executor so all futures are ready - we don't want any deadlocks mainThreadExecutor.runTasks(() -> mainThreadExecutor.getTaskCount() == 0); // save all pending chunks @@ -104,20 +138,21 @@ public abstract class ThreadedAnvilChunkStorage_scarpetChunkCreationMixin implem if (chunk != null && chunk.getStatus() != ChunkStatus.EMPTY) loadedChunks.add(pos); } - CarpetSettings.LOG.error("Area has "+requestedChunks.size()+" chunks"); - CarpetSettings.LOG.error("Area has "+loadedChunks.size()+" loaded chunks"); + report.put("loaded_chunks", loadedChunks.size()); Map targetStatus = new HashMap<>(); + Map regionCache = new HashMap<>(); for (ChunkPos pos: requestedChunks) { - + if (!chunkExists(pos, regionCache)) continue; Chunk chunk = world.getChunk(pos.x, pos.z, ChunkStatus.EMPTY, true); if (chunk == null || chunk.getStatus() == ChunkStatus.EMPTY) continue; //unloaded areas can have unknown skirt chunk area targetStatus.put(pos, loadedChunks.contains(pos)?chunk.getStatus():ChunkStatus.STRUCTURE_STARTS); } - CarpetSettings.LOG.error("Area has "+targetStatus.size()+" chunks for removal"); + regionCache.clear(); + report.put("affected_chunks", targetStatus.size()); for (ChunkPos chpos : targetStatus.keySet()) @@ -128,8 +163,7 @@ public abstract class ThreadedAnvilChunkStorage_scarpetChunkCreationMixin implem int currentLevel = ThreadedAnvilChunkStorage.MAX_LEVEL; if (chunkHolder != null) { - //CarpetSettings.LOG.error("current_level: "+chunkHolder.getLevel()); - currentLevel = chunkHolder.getLevel(); + currentLevel = chunkHolder.getLevel()-1; //method_20458(id, chunkHolder); // saving chunk // skipping entities etc Chunk chunk = null; try @@ -184,34 +218,25 @@ public abstract class ThreadedAnvilChunkStorage_scarpetChunkCreationMixin implem for (ChunkStatus status : ChunkStatus.createOrderedList()) { - CarpetSettings.LOG.error("Creating layer: " + status.getId() + " for "+targetStatus.size()+" chunks"); + report.put("layer_count_"+status.getId(),targetStatus.size()); + long start = System.currentTimeMillis(); if (status == ChunkStatus.LIGHT) { - //break; serverLightingProvider.setTaskBatchSize(targetStatus.size() + 20); } for (ChunkPos chpos : new ArrayList<>(targetStatus.keySet())) { if (targetStatus.get(chpos) == status) targetStatus.remove(chpos); - //if (status==ChunkStatus.LIGHT) - //{ - // serverLightingProvider.tick(); - //} - //CarpetSettings.LOG.error("Creating layer: " + status.getId()); world.getChunk(chpos.x, chpos.z, status); if (status == ChunkStatus.FULL) convertToFullChunk(currentChunkHolders.get(chpos.toLong())); - //CarpetSettings.LOG.error(" .. done"); - //if (status==ChunkStatus.LIGHT) - //{ - // serverLightingProvider.tick(); - //} } if (status == ChunkStatus.LIGHT) - { + { // default task batch size after initial generation is done serverLightingProvider.setTaskBatchSize(5); } + report.put("layer_time_"+status.getId(), (int) (System.currentTimeMillis()-start)); } mainThreadExecutor.runTasks(() -> mainThreadExecutor.getTaskCount() == 0); @@ -222,5 +247,6 @@ public abstract class ThreadedAnvilChunkStorage_scarpetChunkCreationMixin implem { runnable.run(); } + return report; } } diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java index d1f4d0f7..e00f827b 100644 --- a/src/main/java/carpet/script/CarpetExpression.java +++ b/src/main/java/carpet/script/CarpetExpression.java @@ -1249,14 +1249,24 @@ public class CarpetExpression ServerWorld world = cc.s.getWorld(); + Value [] result = new Value[]{Value.NULL}; + ((CarpetContext)c).s.getMinecraftServer().submitAndJoin( () -> { - ((ThreadedAnvilChunkStorageInterface) world.getChunkManager().threadedAnvilChunkStorage).regenerateChunkRegion(requestedChunks); + Map report = ((ThreadedAnvilChunkStorageInterface) world.getChunkManager().threadedAnvilChunkStorage).regenerateChunkRegion(requestedChunks); for (ChunkPos chpos: requestedChunks) + { if (world.getChunk(chpos.x, chpos.z, ChunkStatus.FULL, false) != null) + { this.forceChunkUpdate(chpos.getCenterBlockPos(), world); + } + } + result[0] = MapValue.wrap(report.entrySet().stream().collect(Collectors.toMap( + e -> new StringValue((String)((Map.Entry) e).getKey()), + e -> new NumericValue((Integer)((Map.Entry) e).getValue()) + ))); }); - return LazyValue.NULL; + return (_c, _t) -> result[0]; }); }