From 646be6298e61079ea77738d191bdf87f42e98bef Mon Sep 17 00:00:00 2001 From: gnembon Date: Sat, 4 Apr 2020 01:32:46 -0300 Subject: [PATCH 1/4] trying to flush the toileight --- .../ServerLightingProviderInterface.java | 9 ++++ ...ingProvider_scarpetChunkCreationMixin.java | 54 +++++++++++++++++++ ...hunkStorage_scarpetChunkCreationMixin.java | 15 +++--- src/main/resources/carpet.mixins.json | 1 + 4 files changed, 73 insertions(+), 6 deletions(-) create mode 100644 src/main/java/carpet/fakes/ServerLightingProviderInterface.java create mode 100644 src/main/java/carpet/mixins/ServerLightingProvider_scarpetChunkCreationMixin.java diff --git a/src/main/java/carpet/fakes/ServerLightingProviderInterface.java b/src/main/java/carpet/fakes/ServerLightingProviderInterface.java new file mode 100644 index 00000000..42aff898 --- /dev/null +++ b/src/main/java/carpet/fakes/ServerLightingProviderInterface.java @@ -0,0 +1,9 @@ +package carpet.fakes; + +import net.minecraft.util.math.ChunkPos; + +public interface ServerLightingProviderInterface +{ + void publicUpdateChunkStatus(ChunkPos pos); + void flush(); +} diff --git a/src/main/java/carpet/mixins/ServerLightingProvider_scarpetChunkCreationMixin.java b/src/main/java/carpet/mixins/ServerLightingProvider_scarpetChunkCreationMixin.java new file mode 100644 index 00000000..1bc4795e --- /dev/null +++ b/src/main/java/carpet/mixins/ServerLightingProvider_scarpetChunkCreationMixin.java @@ -0,0 +1,54 @@ +package carpet.mixins; + +import carpet.fakes.ServerLightingProviderInterface; +import it.unimi.dsi.fastutil.objects.ObjectList; +import net.minecraft.server.world.ServerLightingProvider; +import net.minecraft.util.math.ChunkPos; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.concurrent.atomic.AtomicBoolean; + +import static java.lang.Thread.yield; + +@Mixin(ServerLightingProvider.class) +public abstract class ServerLightingProvider_scarpetChunkCreationMixin implements ServerLightingProviderInterface +{ + + @Shadow protected abstract void updateChunkStatus(ChunkPos pos); + + @Shadow private volatile int taskBatchSize; + + @Shadow public abstract void setTaskBatchSize(int taskBatchSize); + + @Shadow @Final private AtomicBoolean field_18812; + + @Shadow @Final private ObjectList pendingTasks; + + @Shadow protected abstract void runTasks(); + + @Override + public void publicUpdateChunkStatus(ChunkPos pos) + { + updateChunkStatus(pos); + } + + @Override + public void flush() + { + try + { + while (!field_18812.compareAndSet(false, true)) yield(); + if (pendingTasks.size() == 0) return; + int prevTaskBatchSize = taskBatchSize; + setTaskBatchSize(pendingTasks.size()); + runTasks(); + setTaskBatchSize(prevTaskBatchSize); + } + finally + { + this.field_18812.set(false); + } + } +} diff --git a/src/main/java/carpet/mixins/ThreadedAnvilChunkStorage_scarpetChunkCreationMixin.java b/src/main/java/carpet/mixins/ThreadedAnvilChunkStorage_scarpetChunkCreationMixin.java index bad3a35d..e5daf3dc 100644 --- a/src/main/java/carpet/mixins/ThreadedAnvilChunkStorage_scarpetChunkCreationMixin.java +++ b/src/main/java/carpet/mixins/ThreadedAnvilChunkStorage_scarpetChunkCreationMixin.java @@ -1,6 +1,7 @@ package carpet.mixins; import carpet.fakes.ChunkHolderInterface; +import carpet.fakes.ServerLightingProviderInterface; import carpet.fakes.ThreadedAnvilChunkStorageInterface; import carpet.script.utils.WorldTools; import com.mojang.datafixers.util.Either; @@ -155,7 +156,7 @@ public abstract class ThreadedAnvilChunkStorage_scarpetChunkCreationMixin implem //apparently that doesn't remove them for the client this.world.unloadEntities(worldChunk); } - //this.serverLightingProvider.updateChunkStatus(chunk.getPos()); // we might need that as some point + ((ServerLightingProviderInterface)this.serverLightingProvider).publicUpdateChunkStatus(chunk.getPos()); // we might need that as some point serverLightingProvider.tick(); worldGenerationProgressListener.setChunkStatus(chunk.getPos(), (ChunkStatus) null); } @@ -175,6 +176,7 @@ public abstract class ThreadedAnvilChunkStorage_scarpetChunkCreationMixin implem this.chunkHolderListDirty = true; // not needed right now mainThreadExecutor.runTasks(() -> mainThreadExecutor.getTaskCount() == 0); + ((ServerLightingProviderInterface)this.serverLightingProvider).flush(); //worldgenExecutor. // save all pending chunks / potentially only one max - ours // hopefully that would flush all chunk writes, including current chink @@ -187,10 +189,10 @@ public abstract class ThreadedAnvilChunkStorage_scarpetChunkCreationMixin implem { report.put("layer_count_"+status.getId(),targetStatus.size()); long start = System.currentTimeMillis(); - if (status == ChunkStatus.LIGHT) - { - serverLightingProvider.setTaskBatchSize(targetStatus.size() + 20); - } + //if (status == ChunkStatus.LIGHT) + //{ + // serverLightingProvider.setTaskBatchSize(targetStatus.size() + 20); + //} for (ChunkPos chpos : new ArrayList<>(targetStatus.keySet())) { if (targetStatus.get(chpos) == status) @@ -201,7 +203,8 @@ public abstract class ThreadedAnvilChunkStorage_scarpetChunkCreationMixin implem } if (status == ChunkStatus.LIGHT) { // default task batch size after initial generation is done - serverLightingProvider.setTaskBatchSize(5); + //serverLightingProvider.setTaskBatchSize(5); + ((ServerLightingProviderInterface)this.serverLightingProvider).flush(); } report.put("layer_time_"+status.getId(), (int) (System.currentTimeMillis()-start)); } diff --git a/src/main/resources/carpet.mixins.json b/src/main/resources/carpet.mixins.json index 25c2356e..1ecd3f9b 100644 --- a/src/main/resources/carpet.mixins.json +++ b/src/main/resources/carpet.mixins.json @@ -28,6 +28,7 @@ "ThreadedAnvilChunkStorage_scarpetChunkCreationMixin", "ChunkHolder_scarpetChunkCreationMixin", + "ServerLightingProvider_scarpetChunkCreationMixin", "MobEntityMixin", "ServerWorld_unloadedEntityMixin", From d9aa6f15adb8dcb8fd99934f1e3bd665da0614b0 Mon Sep 17 00:00:00 2001 From: gnembon Date: Fri, 12 Jun 2020 00:24:46 -0300 Subject: [PATCH 2/4] relight --- .../ServerLightingProviderInterface.java | 1 + .../ServerLightingProvider_scarpetMixin.java | 49 +++++++++++++++++++ .../java/carpet/script/CarpetExpression.java | 11 +++++ src/main/resources/carpet.mixins.json | 4 ++ 4 files changed, 65 insertions(+) create mode 100644 src/main/java/carpet/mixins/ServerLightingProvider_scarpetMixin.java diff --git a/src/main/java/carpet/fakes/ServerLightingProviderInterface.java b/src/main/java/carpet/fakes/ServerLightingProviderInterface.java index 42aff898..c6858f9e 100644 --- a/src/main/java/carpet/fakes/ServerLightingProviderInterface.java +++ b/src/main/java/carpet/fakes/ServerLightingProviderInterface.java @@ -6,4 +6,5 @@ public interface ServerLightingProviderInterface { void publicUpdateChunkStatus(ChunkPos pos); void flush(); + void resetLight(ChunkPos pos); } diff --git a/src/main/java/carpet/mixins/ServerLightingProvider_scarpetMixin.java b/src/main/java/carpet/mixins/ServerLightingProvider_scarpetMixin.java new file mode 100644 index 00000000..fadf44ad --- /dev/null +++ b/src/main/java/carpet/mixins/ServerLightingProvider_scarpetMixin.java @@ -0,0 +1,49 @@ +package carpet.mixins; + +import carpet.CarpetSettings; +import carpet.fakes.ServerLightingProviderInterface; +import net.minecraft.server.world.ServerLightingProvider; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.util.math.ChunkSectionPos; +import net.minecraft.world.LightType; +import net.minecraft.world.chunk.ChunkNibbleArray; +import net.minecraft.world.chunk.ChunkProvider; +import net.minecraft.world.chunk.light.LightingProvider; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(ServerLightingProvider.class) +public abstract class ServerLightingProvider_scarpetMixin extends LightingProvider implements ServerLightingProviderInterface +{ + + @Shadow protected abstract void updateChunkStatus(ChunkPos pos); + + + @Shadow public abstract void checkBlock(BlockPos pos); + + public ServerLightingProvider_scarpetMixin(ChunkProvider chunkProvider, boolean hasBlockLight, boolean hasSkyLight) + { + super(chunkProvider, hasBlockLight, hasSkyLight); + } + + @Override + public void resetLight(ChunkPos chpos) + { + //super.setRetainData(pos, false); + //super.setLightEnabled(pos, false); + for (int x = chpos.x-1; x <= chpos.x+1; x++ ) + for (int z = chpos.z-1; z <= chpos.z+1; z++ ) + { + ChunkPos pos = new ChunkPos(x, z); + int j; + for(j = -1; j < 17; ++j) { + super.queueData(LightType.BLOCK, ChunkSectionPos.from(pos, j), new ChunkNibbleArray()); + super.queueData(LightType.SKY, ChunkSectionPos.from(pos, j), new ChunkNibbleArray()); + } + for(j = 0; j < 16; ++j) { + super.updateSectionStatus(ChunkSectionPos.from(pos, j), true); + } + } + } +} diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java index 711d07d6..eefc7875 100644 --- a/src/main/java/carpet/script/CarpetExpression.java +++ b/src/main/java/carpet/script/CarpetExpression.java @@ -5,6 +5,7 @@ import carpet.fakes.ChunkTicketManagerInterface; import carpet.fakes.MinecraftServerInterface; import carpet.fakes.BiomeArrayInterface; import carpet.fakes.ServerChunkManagerInterface; +import carpet.fakes.ServerLightingProviderInterface; import carpet.fakes.StatTypeInterface; import carpet.fakes.ThreadedAnvilChunkStorageInterface; import carpet.helpers.FeatureGenerator; @@ -2948,6 +2949,16 @@ public class CarpetExpression return (cc, tt) -> ret; }); + this.expr.addLazyFunction("relight", -1, (c, t, lv) -> + { + CarpetContext cc = (CarpetContext) c; + BlockArgument locator = BlockArgument.findIn(cc, lv, 0); + BlockPos pos = locator.block.getPos(); + ServerWorld world = cc.s.getWorld(); + ((ServerLightingProviderInterface)world.getChunkManager().getLightingProvider()).resetLight(new ChunkPos(pos)); //light(world.getChunk(pos), false); + return LazyValue.TRUE; + }); + this.expr.addLazyFunction("current_dimension", 0, (c, t, lv) -> { ServerCommandSource s = ((CarpetContext)c).s; Value retval = new StringValue(NBTSerializableValue.nameFromRegistryId(Registry.DIMENSION_TYPE.getId(s.getWorld().dimension.getType()))); diff --git a/src/main/resources/carpet.mixins.json b/src/main/resources/carpet.mixins.json index c527a733..7a8ee568 100644 --- a/src/main/resources/carpet.mixins.json +++ b/src/main/resources/carpet.mixins.json @@ -32,6 +32,10 @@ "ThreadedAnvilChunkStorage_scarpetChunkCreationMixin", "ChunkHolder_scarpetChunkCreationMixin", + "ServerLightingProvider_scarpetMixin", + + "ChunkTicketManager_spawnChunksMixin", + "ServerWorld_spawnChunksMixin", "ServerLightingProvider_scarpetChunkCreationMixin", "ChunkTicketManager_spawnChunksMixin", From b9f2e68ddb879529af11a53e68a0c1a7c2684fa4 Mon Sep 17 00:00:00 2001 From: gnembon Date: Fri, 12 Jun 2020 00:55:48 -0300 Subject: [PATCH 3/4] trying to readd all lights --- .../ServerLightingProviderInterface.java | 3 ++- .../ServerLightingProvider_scarpetMixin.java | 21 +++++++++++++++---- .../java/carpet/script/CarpetExpression.java | 4 +++- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/main/java/carpet/fakes/ServerLightingProviderInterface.java b/src/main/java/carpet/fakes/ServerLightingProviderInterface.java index c6858f9e..d8eb5ba4 100644 --- a/src/main/java/carpet/fakes/ServerLightingProviderInterface.java +++ b/src/main/java/carpet/fakes/ServerLightingProviderInterface.java @@ -1,10 +1,11 @@ package carpet.fakes; import net.minecraft.util.math.ChunkPos; +import net.minecraft.world.chunk.Chunk; public interface ServerLightingProviderInterface { void publicUpdateChunkStatus(ChunkPos pos); void flush(); - void resetLight(ChunkPos pos); + void resetLight(Chunk chunk, ChunkPos pos); } diff --git a/src/main/java/carpet/mixins/ServerLightingProvider_scarpetMixin.java b/src/main/java/carpet/mixins/ServerLightingProvider_scarpetMixin.java index fadf44ad..b55e2236 100644 --- a/src/main/java/carpet/mixins/ServerLightingProvider_scarpetMixin.java +++ b/src/main/java/carpet/mixins/ServerLightingProvider_scarpetMixin.java @@ -6,7 +6,9 @@ import net.minecraft.server.world.ServerLightingProvider; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.ChunkPos; import net.minecraft.util.math.ChunkSectionPos; +import net.minecraft.world.IWorld; import net.minecraft.world.LightType; +import net.minecraft.world.chunk.Chunk; import net.minecraft.world.chunk.ChunkNibbleArray; import net.minecraft.world.chunk.ChunkProvider; import net.minecraft.world.chunk.light.LightingProvider; @@ -28,14 +30,14 @@ public abstract class ServerLightingProvider_scarpetMixin extends LightingProvid } @Override - public void resetLight(ChunkPos chpos) + public void resetLight(Chunk chunk, ChunkPos pos) { //super.setRetainData(pos, false); //super.setLightEnabled(pos, false); - for (int x = chpos.x-1; x <= chpos.x+1; x++ ) - for (int z = chpos.z-1; z <= chpos.z+1; z++ ) + //for (int x = chpos.x-1; x <= chpos.x+1; x++ ) + // for (int z = chpos.z-1; z <= chpos.z+1; z++ ) { - ChunkPos pos = new ChunkPos(x, z); + //ChunkPos pos = new ChunkPos(x, z); int j; for(j = -1; j < 17; ++j) { super.queueData(LightType.BLOCK, ChunkSectionPos.from(pos, j), new ChunkNibbleArray()); @@ -44,6 +46,17 @@ public abstract class ServerLightingProvider_scarpetMixin extends LightingProvid for(j = 0; j < 16; ++j) { super.updateSectionStatus(ChunkSectionPos.from(pos, j), true); } + + super.setLightEnabled(pos, true); + + chunk.getLightSourcesStream().forEach((blockPos) -> { + super.addLightSource(blockPos, chunk.getLuminance(blockPos)); + }); + } + + + + } } diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java index eefc7875..7a244270 100644 --- a/src/main/java/carpet/script/CarpetExpression.java +++ b/src/main/java/carpet/script/CarpetExpression.java @@ -2955,7 +2955,9 @@ public class CarpetExpression BlockArgument locator = BlockArgument.findIn(cc, lv, 0); BlockPos pos = locator.block.getPos(); ServerWorld world = cc.s.getWorld(); - ((ServerLightingProviderInterface)world.getChunkManager().getLightingProvider()).resetLight(new ChunkPos(pos)); //light(world.getChunk(pos), false); + ((ServerLightingProviderInterface)world.getChunkManager().getLightingProvider()).resetLight(world.getChunk(pos), new ChunkPos(pos)); //light(world.getChunk(pos), false); + forceChunkUpdate(pos, world); + world.getChunkManager().getLightingProvider().light(world.getChunk(pos), false); return LazyValue.TRUE; }); From 0c1dba015724b6f0736390c6d7d7872017c1f5ed Mon Sep 17 00:00:00 2001 From: PhiPro95 Date: Mon, 29 Jun 2020 00:44:58 +0200 Subject: [PATCH 4/4] Rewrite regenerateChunkRegion --- build.gradle | 1 + .../fakes/ChunkLightProviderInterface.java | 8 + .../fakes/ChunkTicketManagerInterface.java | 4 + .../carpet/fakes/LightStorageInterface.java | 10 + ...ighting_scarpetChunkCreationInterface.java | 8 + .../ServerLightingProviderInterface.java | 10 +- .../ThreadedAnvilChunkStorageInterface.java | 6 +- ...ightStorage_scarpetChunkCreationMixin.java | 65 +++ ...ghtProvider_scarpetChunkCreationMixin.java | 36 ++ ...cketManager_scarpetChunkCreationMixin.java | 26 + .../mixins/LevelPropagatorInterface.java | 16 + ...ightStorage_scarpetChunkCreationMixin.java | 115 ++++ ...ingProvider_scarpetChunkCreationMixin.java | 41 ++ ...ingProvider_scarpetChunkCreationMixin.java | 98 ++-- ...ightStorage_scarpetChunkCreationMixin.java | 109 ++++ ...hunkStorage_scarpetChunkCreationMixin.java | 518 ++++++++++++------ src/main/resources/carpet.accesswidener | 4 + src/main/resources/carpet.mixins.json | 7 + src/main/resources/fabric.mod.json | 2 +- 19 files changed, 876 insertions(+), 208 deletions(-) create mode 100644 src/main/java/carpet/fakes/ChunkLightProviderInterface.java create mode 100644 src/main/java/carpet/fakes/LightStorageInterface.java create mode 100644 src/main/java/carpet/fakes/Lighting_scarpetChunkCreationInterface.java create mode 100644 src/main/java/carpet/mixins/BlockLightStorage_scarpetChunkCreationMixin.java create mode 100644 src/main/java/carpet/mixins/ChunkLightProvider_scarpetChunkCreationMixin.java create mode 100644 src/main/java/carpet/mixins/ChunkTicketManager_scarpetChunkCreationMixin.java create mode 100644 src/main/java/carpet/mixins/LevelPropagatorInterface.java create mode 100644 src/main/java/carpet/mixins/LightStorage_scarpetChunkCreationMixin.java create mode 100644 src/main/java/carpet/mixins/LightingProvider_scarpetChunkCreationMixin.java create mode 100644 src/main/java/carpet/mixins/SkyLightStorage_scarpetChunkCreationMixin.java create mode 100644 src/main/resources/carpet.accesswidener diff --git a/build.gradle b/build.gradle index 2dc45583..c9cfacd7 100644 --- a/build.gradle +++ b/build.gradle @@ -11,6 +11,7 @@ version = project.minecraft_version+'-'+project.mod_version group = project.maven_group minecraft { + accessWidener "src/main/resources/carpet.accesswidener" } dependencies { diff --git a/src/main/java/carpet/fakes/ChunkLightProviderInterface.java b/src/main/java/carpet/fakes/ChunkLightProviderInterface.java new file mode 100644 index 00000000..af46235c --- /dev/null +++ b/src/main/java/carpet/fakes/ChunkLightProviderInterface.java @@ -0,0 +1,8 @@ +package carpet.fakes; + +import net.minecraft.world.chunk.ChunkNibbleArray; + +public interface ChunkLightProviderInterface +{ + int callGetCurrentLevelFromArray(ChunkNibbleArray array, long blockPos); +} diff --git a/src/main/java/carpet/fakes/ChunkTicketManagerInterface.java b/src/main/java/carpet/fakes/ChunkTicketManagerInterface.java index 37c4ec65..f8bdc248 100644 --- a/src/main/java/carpet/fakes/ChunkTicketManagerInterface.java +++ b/src/main/java/carpet/fakes/ChunkTicketManagerInterface.java @@ -1,6 +1,7 @@ package carpet.fakes; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import net.minecraft.server.world.ChunkHolder; import net.minecraft.server.world.ChunkTicket; import net.minecraft.util.SortedArraySet; import net.minecraft.util.math.ChunkPos; @@ -8,5 +9,8 @@ import net.minecraft.util.math.ChunkPos; public interface ChunkTicketManagerInterface { void changeSpawnChunks(ChunkPos pos, int distance); + Long2ObjectOpenHashMap>> getTicketsByPosition(); + + void replaceHolder(ChunkHolder oldHolder, ChunkHolder newHolder); } diff --git a/src/main/java/carpet/fakes/LightStorageInterface.java b/src/main/java/carpet/fakes/LightStorageInterface.java new file mode 100644 index 00000000..3d8ae2da --- /dev/null +++ b/src/main/java/carpet/fakes/LightStorageInterface.java @@ -0,0 +1,10 @@ +package carpet.fakes; + +import net.minecraft.world.chunk.light.ChunkLightProvider; + +public interface LightStorageInterface extends Lighting_scarpetChunkCreationInterface +{ + void processRemoveLightData(long pos); + + void processRelight(ChunkLightProvider lightProvider, long pos); +} diff --git a/src/main/java/carpet/fakes/Lighting_scarpetChunkCreationInterface.java b/src/main/java/carpet/fakes/Lighting_scarpetChunkCreationInterface.java new file mode 100644 index 00000000..5c72fc59 --- /dev/null +++ b/src/main/java/carpet/fakes/Lighting_scarpetChunkCreationInterface.java @@ -0,0 +1,8 @@ +package carpet.fakes; + +public interface Lighting_scarpetChunkCreationInterface +{ + void removeLightData(long pos); + + void relight(long pos); +} diff --git a/src/main/java/carpet/fakes/ServerLightingProviderInterface.java b/src/main/java/carpet/fakes/ServerLightingProviderInterface.java index d8eb5ba4..78ec5018 100644 --- a/src/main/java/carpet/fakes/ServerLightingProviderInterface.java +++ b/src/main/java/carpet/fakes/ServerLightingProviderInterface.java @@ -1,11 +1,17 @@ package carpet.fakes; +import java.util.concurrent.CompletableFuture; + import net.minecraft.util.math.ChunkPos; import net.minecraft.world.chunk.Chunk; public interface ServerLightingProviderInterface { - void publicUpdateChunkStatus(ChunkPos pos); - void flush(); + void invokeUpdateChunkStatus(ChunkPos pos); + + void removeLightData(Chunk chunk); + + CompletableFuture relight(Chunk chunk); + void resetLight(Chunk chunk, ChunkPos pos); } diff --git a/src/main/java/carpet/fakes/ThreadedAnvilChunkStorageInterface.java b/src/main/java/carpet/fakes/ThreadedAnvilChunkStorageInterface.java index d2fff722..d99e9fe4 100644 --- a/src/main/java/carpet/fakes/ThreadedAnvilChunkStorageInterface.java +++ b/src/main/java/carpet/fakes/ThreadedAnvilChunkStorageInterface.java @@ -1,11 +1,13 @@ package carpet.fakes; -import net.minecraft.util.math.ChunkPos; - import java.util.List; import java.util.Map; +import net.minecraft.util.math.ChunkPos; + public interface ThreadedAnvilChunkStorageInterface { Map regenerateChunkRegion(List requestedChunks); + + void releaseRelightTicket(ChunkPos pos); } diff --git a/src/main/java/carpet/mixins/BlockLightStorage_scarpetChunkCreationMixin.java b/src/main/java/carpet/mixins/BlockLightStorage_scarpetChunkCreationMixin.java new file mode 100644 index 00000000..3b3b8e86 --- /dev/null +++ b/src/main/java/carpet/mixins/BlockLightStorage_scarpetChunkCreationMixin.java @@ -0,0 +1,65 @@ +package carpet.mixins; + +import org.spongepowered.asm.mixin.Mixin; + +import carpet.fakes.ChunkLightProviderInterface; +import carpet.fakes.LightStorageInterface; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkSectionPos; +import net.minecraft.util.math.Direction; +import net.minecraft.world.LightType; +import net.minecraft.world.chunk.ChunkNibbleArray; +import net.minecraft.world.chunk.ChunkProvider; +import net.minecraft.world.chunk.light.BlockLightStorage; +import net.minecraft.world.chunk.light.BlockLightStorage.Data; +import net.minecraft.world.chunk.light.ChunkLightProvider; +import net.minecraft.world.chunk.light.LightStorage; + +@Mixin(BlockLightStorage.class) +public abstract class BlockLightStorage_scarpetChunkCreationMixin extends LightStorage implements LightStorageInterface +{ + private BlockLightStorage_scarpetChunkCreationMixin(final LightType lightType, final ChunkProvider chunkProvider, final Data lightData) + { + super(lightType, chunkProvider, lightData); + } + + @Override + public void processRelight(final ChunkLightProvider lightProvider, final long cPos) + { + final LevelPropagatorInterface levelPropagator = (LevelPropagatorInterface) lightProvider; + + for (int y = -1; y < 17; ++y) + { + final long sectionPos = ChunkSectionPos.asLong(ChunkSectionPos.getX(cPos), y, ChunkSectionPos.getZ(cPos)); + final long pos = BlockPos.asLong(ChunkSectionPos.getWorldCoord(ChunkSectionPos.getX(sectionPos)), ChunkSectionPos.getWorldCoord(y), ChunkSectionPos.getWorldCoord(ChunkSectionPos.getZ(sectionPos))); + + if (!this.hasLight(sectionPos)) + continue; + + for (final Direction dir : Direction.Type.HORIZONTAL) + { + final ChunkNibbleArray neighborLightArray = this.getLightArray(ChunkSectionPos.offset(sectionPos, dir)); + + if (neighborLightArray == null) + continue; + + final int ox = 15 * Math.max(dir.getOffsetX(), 0); + final int oz = 15 * Math.max(dir.getOffsetZ(), 0); + + final int dx = Math.abs(dir.getOffsetZ()); + final int dz = Math.abs(dir.getOffsetX()); + + for (int t = 0; t < 16; ++t) + for (int dy = 0; dy < 16; ++dy) + { + final long dst = BlockPos.add(pos, ox + t * dx, dy, oz + t * dz); + final long src = BlockPos.offset(dst, dir); + + final int srcLevel = ((ChunkLightProviderInterface) lightProvider).callGetCurrentLevelFromArray(neighborLightArray, src); + + levelPropagator.invokeUpdateLevel(src, dst, levelPropagator.callGetPropagatedLevel(src, dst, srcLevel), true); + } + } + } + } +} diff --git a/src/main/java/carpet/mixins/ChunkLightProvider_scarpetChunkCreationMixin.java b/src/main/java/carpet/mixins/ChunkLightProvider_scarpetChunkCreationMixin.java new file mode 100644 index 00000000..7af3a279 --- /dev/null +++ b/src/main/java/carpet/mixins/ChunkLightProvider_scarpetChunkCreationMixin.java @@ -0,0 +1,36 @@ +package carpet.mixins; + +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.gen.Invoker; + +import carpet.fakes.ChunkLightProviderInterface; +import carpet.fakes.Lighting_scarpetChunkCreationInterface; +import net.minecraft.world.chunk.ChunkNibbleArray; +import net.minecraft.world.chunk.light.ChunkLightProvider; +import net.minecraft.world.chunk.light.LightStorage; + +@Mixin(ChunkLightProvider.class) +public abstract class ChunkLightProvider_scarpetChunkCreationMixin implements Lighting_scarpetChunkCreationInterface, ChunkLightProviderInterface +{ + @Shadow + @Final + protected LightStorage lightStorage; + + @Override + public void removeLightData(final long pos) + { + ((Lighting_scarpetChunkCreationInterface) this.lightStorage).removeLightData(pos); + } + + @Override + public void relight(final long pos) + { + ((Lighting_scarpetChunkCreationInterface) this.lightStorage).relight(pos); + } + + @Override + @Invoker("getCurrentLevelFromArray") + public abstract int callGetCurrentLevelFromArray(ChunkNibbleArray array, long blockPos); +} diff --git a/src/main/java/carpet/mixins/ChunkTicketManager_scarpetChunkCreationMixin.java b/src/main/java/carpet/mixins/ChunkTicketManager_scarpetChunkCreationMixin.java new file mode 100644 index 00000000..f5aaf552 --- /dev/null +++ b/src/main/java/carpet/mixins/ChunkTicketManager_scarpetChunkCreationMixin.java @@ -0,0 +1,26 @@ +package carpet.mixins; + +import java.util.Set; + +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import carpet.fakes.ChunkTicketManagerInterface; +import net.minecraft.server.world.ChunkHolder; +import net.minecraft.server.world.ChunkTicketManager; + +@Mixin(ChunkTicketManager.class) +public abstract class ChunkTicketManager_scarpetChunkCreationMixin implements ChunkTicketManagerInterface +{ + @Shadow + @Final + private Set chunkHolders; + + @Override + public void replaceHolder(final ChunkHolder oldHolder, final ChunkHolder newHolder) + { + this.chunkHolders.remove(oldHolder); + this.chunkHolders.add(newHolder); + } +} diff --git a/src/main/java/carpet/mixins/LevelPropagatorInterface.java b/src/main/java/carpet/mixins/LevelPropagatorInterface.java new file mode 100644 index 00000000..3808bd10 --- /dev/null +++ b/src/main/java/carpet/mixins/LevelPropagatorInterface.java @@ -0,0 +1,16 @@ +package carpet.mixins; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +import net.minecraft.world.chunk.light.LevelPropagator; + +@Mixin(LevelPropagator.class) +public interface LevelPropagatorInterface +{ + @Invoker("updateLevel") + void invokeUpdateLevel(long sourceId, long id, int level, boolean decrease); + + @Invoker("getPropagatedLevel") + int callGetPropagatedLevel(long sourceId, long targetId, int level); +} diff --git a/src/main/java/carpet/mixins/LightStorage_scarpetChunkCreationMixin.java b/src/main/java/carpet/mixins/LightStorage_scarpetChunkCreationMixin.java new file mode 100644 index 00000000..b4a0c2c0 --- /dev/null +++ b/src/main/java/carpet/mixins/LightStorage_scarpetChunkCreationMixin.java @@ -0,0 +1,115 @@ +package carpet.mixins; + +import java.util.Arrays; + +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import carpet.fakes.LightStorageInterface; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +import it.unimi.dsi.fastutil.longs.LongSet; +import net.minecraft.util.math.ChunkSectionPos; +import net.minecraft.world.chunk.ChunkNibbleArray; +import net.minecraft.world.chunk.ChunkToNibbleArrayMap; +import net.minecraft.world.chunk.light.ChunkLightProvider; +import net.minecraft.world.chunk.light.LightStorage; + +@Mixin(LightStorage.class) +public abstract class LightStorage_scarpetChunkCreationMixin implements LightStorageInterface +{ + @Shadow + protected abstract ChunkNibbleArray getLightArray(final long sectionPos, final boolean cached); + + @Shadow + protected abstract void removeChunkData(final ChunkLightProvider storage, final long blockChunkPos); + + @Shadow + @Final + protected LongSet field_15802; + + @Shadow + @Final + protected ChunkToNibbleArrayMap lightArrays; + + @Shadow protected abstract boolean hasLight(final long sectionPos); + + @Shadow + @Final + protected Long2ObjectMap lightArraysToAdd; + + @Shadow protected abstract void setLightEnabled(final long l, final boolean bl); + + @Unique + private final LongSet removedChunks = new LongOpenHashSet(); + + @Unique + private final LongSet relightChunks = new LongOpenHashSet(); + + @Override + public void removeLightData(final long pos) + { + this.removedChunks.add(pos); + } + + @Override + public void relight(final long pos) + { + this.relightChunks.add(pos); + } + + @Inject( + method = "updateLightArrays(Lnet/minecraft/world/chunk/light/ChunkLightProvider;ZZ)V", + at = @At("HEAD") + ) + private void processData(final ChunkLightProvider lightProvider, final boolean doSkylight, final boolean skipEdgeLightPropagation, final CallbackInfo ci) + { + // Process light removal + + for (final long cPos : this.removedChunks) + { + for (int y = -1; y < 17; ++y) + { + final long sectionPos = ChunkSectionPos.asLong(ChunkSectionPos.getX(cPos), y, ChunkSectionPos.getZ(cPos)); + + this.lightArraysToAdd.remove(sectionPos); + + if (this.hasLight(sectionPos)) + { + this.removeChunkData(lightProvider, sectionPos); + + if (this.field_15802.add(sectionPos)) + this.lightArrays.replaceWithCopy(sectionPos); + + Arrays.fill(this.getLightArray(sectionPos, true).asByteArray(), (byte) 0); + } + } + + this.processRemoveLightData(cPos); + } + + this.removedChunks.clear(); + + // Process relighting + + for (final long cPos : this.relightChunks) + this.processRelight(lightProvider, cPos); + + this.relightChunks.clear(); + } + + @Override + public void processRemoveLightData(final long pos) + { + } + + @Override + public void processRelight(final ChunkLightProvider lightProvider, final long pos) + { + } +} diff --git a/src/main/java/carpet/mixins/LightingProvider_scarpetChunkCreationMixin.java b/src/main/java/carpet/mixins/LightingProvider_scarpetChunkCreationMixin.java new file mode 100644 index 00000000..06054551 --- /dev/null +++ b/src/main/java/carpet/mixins/LightingProvider_scarpetChunkCreationMixin.java @@ -0,0 +1,41 @@ +package carpet.mixins; + +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import carpet.fakes.Lighting_scarpetChunkCreationInterface; +import net.minecraft.world.chunk.light.ChunkLightProvider; +import net.minecraft.world.chunk.light.LightingProvider; + +@Mixin(LightingProvider.class) +public abstract class LightingProvider_scarpetChunkCreationMixin implements Lighting_scarpetChunkCreationInterface +{ + @Shadow + @Final + private ChunkLightProvider blockLightProvider; + + @Shadow + @Final + private ChunkLightProvider skyLightProvider; + + @Override + public void removeLightData(final long pos) + { + if (this.blockLightProvider != null) + ((Lighting_scarpetChunkCreationInterface) this.blockLightProvider).removeLightData(pos); + + if (this.skyLightProvider != null) + ((Lighting_scarpetChunkCreationInterface) this.skyLightProvider).removeLightData(pos); + } + + @Override + public void relight(final long pos) + { + if (this.blockLightProvider != null) + ((Lighting_scarpetChunkCreationInterface) this.blockLightProvider).relight(pos); + + if (this.skyLightProvider != null) + ((Lighting_scarpetChunkCreationInterface) this.skyLightProvider).relight(pos); + } +} diff --git a/src/main/java/carpet/mixins/ServerLightingProvider_scarpetChunkCreationMixin.java b/src/main/java/carpet/mixins/ServerLightingProvider_scarpetChunkCreationMixin.java index 1bc4795e..4bfcedf6 100644 --- a/src/main/java/carpet/mixins/ServerLightingProvider_scarpetChunkCreationMixin.java +++ b/src/main/java/carpet/mixins/ServerLightingProvider_scarpetChunkCreationMixin.java @@ -1,54 +1,84 @@ package carpet.mixins; -import carpet.fakes.ServerLightingProviderInterface; -import it.unimi.dsi.fastutil.objects.ObjectList; -import net.minecraft.server.world.ServerLightingProvider; -import net.minecraft.util.math.ChunkPos; +import java.util.concurrent.CompletableFuture; +import java.util.function.IntSupplier; + import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.gen.Invoker; -import java.util.concurrent.atomic.AtomicBoolean; - -import static java.lang.Thread.yield; +import carpet.fakes.Lighting_scarpetChunkCreationInterface; +import carpet.fakes.ServerLightingProviderInterface; +import carpet.fakes.ThreadedAnvilChunkStorageInterface; +import net.minecraft.server.world.ServerLightingProvider; +import net.minecraft.server.world.ServerLightingProvider.Stage; +import net.minecraft.server.world.ThreadedAnvilChunkStorage; +import net.minecraft.util.Util; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.util.math.ChunkSectionPos; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.ChunkProvider; +import net.minecraft.world.chunk.light.LightingProvider; @Mixin(ServerLightingProvider.class) -public abstract class ServerLightingProvider_scarpetChunkCreationMixin implements ServerLightingProviderInterface +public abstract class ServerLightingProvider_scarpetChunkCreationMixin extends LightingProvider implements ServerLightingProviderInterface { + private ServerLightingProvider_scarpetChunkCreationMixin(final ChunkProvider chunkProvider, final boolean hasBlockLight, final boolean hasSkyLight) + { + super(chunkProvider, hasBlockLight, hasSkyLight); + } - @Shadow protected abstract void updateChunkStatus(ChunkPos pos); + @Shadow + protected abstract void enqueue(final int x, final int z, final IntSupplier completedLevelSupplier, final Stage stage, final Runnable task); - @Shadow private volatile int taskBatchSize; - - @Shadow public abstract void setTaskBatchSize(int taskBatchSize); - - @Shadow @Final private AtomicBoolean field_18812; - - @Shadow @Final private ObjectList pendingTasks; - - @Shadow protected abstract void runTasks(); + @Shadow + @Final + private ThreadedAnvilChunkStorage chunkStorage; @Override - public void publicUpdateChunkStatus(ChunkPos pos) + @Invoker("updateChunkStatus") + public abstract void invokeUpdateChunkStatus(ChunkPos pos); + + @Override + public void removeLightData(final Chunk chunk) { - updateChunkStatus(pos); + final ChunkPos pos = chunk.getPos(); + chunk.setLightOn(false); + + this.enqueue(pos.x, pos.z, () -> 0, ServerLightingProvider.Stage.PRE_UPDATE, Util.debugRunnable(() -> { + super.setLightEnabled(pos, false); + ((Lighting_scarpetChunkCreationInterface) this).removeLightData(ChunkSectionPos.withZeroZ(ChunkSectionPos.asLong(pos.x, 0, pos.z))); + }, + () -> "Remove light data " + pos + )); } @Override - public void flush() + public CompletableFuture relight(final Chunk chunk) { - try - { - while (!field_18812.compareAndSet(false, true)) yield(); - if (pendingTasks.size() == 0) return; - int prevTaskBatchSize = taskBatchSize; - setTaskBatchSize(pendingTasks.size()); - runTasks(); - setTaskBatchSize(prevTaskBatchSize); - } - finally - { - this.field_18812.set(false); - } + final ChunkPos pos = chunk.getPos(); + + this.enqueue(pos.x, pos.z, () -> 0, ServerLightingProvider.Stage.PRE_UPDATE, Util.debugRunnable(() -> { + super.setLightEnabled(pos, true); + + chunk.getLightSourcesStream().forEach( + blockPos -> super.addLightSource(blockPos, chunk.getLuminance(blockPos)) + ); + + ((Lighting_scarpetChunkCreationInterface) this).relight(ChunkSectionPos.withZeroZ(ChunkSectionPos.asLong(pos.x, 0, pos.z))); + }, + () -> "Relight chunk " + pos + )); + + return CompletableFuture.runAsync( + Util.debugRunnable(() -> { + chunk.setLightOn(true); + ((ThreadedAnvilChunkStorageInterface) this.chunkStorage).releaseRelightTicket(pos); + }, + () -> "Release relight ticket " + pos + ), + runnable -> this.enqueue(pos.x, pos.z, () -> 0, ServerLightingProvider.Stage.POST_UPDATE, runnable) + ); } } diff --git a/src/main/java/carpet/mixins/SkyLightStorage_scarpetChunkCreationMixin.java b/src/main/java/carpet/mixins/SkyLightStorage_scarpetChunkCreationMixin.java new file mode 100644 index 00000000..8e926022 --- /dev/null +++ b/src/main/java/carpet/mixins/SkyLightStorage_scarpetChunkCreationMixin.java @@ -0,0 +1,109 @@ +package carpet.mixins; + +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import carpet.fakes.ChunkLightProviderInterface; +import carpet.fakes.LightStorageInterface; +import it.unimi.dsi.fastutil.longs.LongSet; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkSectionPos; +import net.minecraft.util.math.Direction; +import net.minecraft.world.LightType; +import net.minecraft.world.chunk.ChunkNibbleArray; +import net.minecraft.world.chunk.ChunkProvider; +import net.minecraft.world.chunk.light.BlockLightStorage.Data; +import net.minecraft.world.chunk.light.ChunkLightProvider; +import net.minecraft.world.chunk.light.LightStorage; +import net.minecraft.world.chunk.light.SkyLightStorage; + +@Mixin(SkyLightStorage.class) +public abstract class SkyLightStorage_scarpetChunkCreationMixin extends LightStorage implements LightStorageInterface +{ + protected SkyLightStorage_scarpetChunkCreationMixin(final LightType lightType, final ChunkProvider chunkProvider, final Data lightData) + { + super(lightType, chunkProvider, lightData); + } + + @Shadow + @Final + private LongSet pendingSkylightUpdates; + + @Shadow + @Final + private LongSet field_15816; + + @Shadow + @Final + private LongSet field_15820; + + @Shadow protected abstract boolean isAboveTopmostLightArray(final long pos); + + @Shadow protected abstract boolean isLightEnabled(final long sectionPos); + + @Override + public void processRemoveLightData(final long cPos) + { + for (int y = -1; y < 17; ++y) + { + final long sectionPos = ChunkSectionPos.asLong(ChunkSectionPos.getX(cPos), y, ChunkSectionPos.getZ(cPos)); + + this.pendingSkylightUpdates.remove(sectionPos); + this.field_15816.remove(sectionPos); + + this.field_15820.remove(sectionPos); + } + } + + @Override + public void processRelight(final ChunkLightProvider lightProvider, final long cPos) + { + final LevelPropagatorInterface levelPropagator = (LevelPropagatorInterface) lightProvider; + + for (int y = -1; y < 17; ++y) + { + final long sectionPos = ChunkSectionPos.asLong(ChunkSectionPos.getX(cPos), y, ChunkSectionPos.getZ(cPos)); + final long pos = BlockPos.asLong(ChunkSectionPos.getWorldCoord(ChunkSectionPos.getX(sectionPos)), ChunkSectionPos.getWorldCoord(y), ChunkSectionPos.getWorldCoord(ChunkSectionPos.getZ(sectionPos))); + + if (!this.hasLight(sectionPos)) + continue; + + for (final Direction dir : Direction.Type.HORIZONTAL) + { + long neighborCeilingSectionPos = ChunkSectionPos.offset(sectionPos, dir); + final ChunkNibbleArray neighborLightArray = this.getLightArray(neighborCeilingSectionPos); + + ChunkNibbleArray neighborCeilingLightArray = neighborLightArray; + + while (neighborCeilingLightArray == null && !this.isAboveTopmostLightArray(neighborCeilingSectionPos)) + { + neighborCeilingSectionPos = ChunkSectionPos.offset(neighborCeilingSectionPos, Direction.UP); + neighborCeilingLightArray = this.getLightArray(neighborCeilingSectionPos); + } + + final int ox = 15 * Math.max(dir.getOffsetX(), 0); + final int oz = 15 * Math.max(dir.getOffsetZ(), 0); + + final int dx = Math.abs(dir.getOffsetZ()); + final int dz = Math.abs(dir.getOffsetX()); + + for (int t = 0; t < 16; ++t) + for (int dy = 0; dy < 16; ++dy) + { + final long dst = BlockPos.add(pos, ox + t * dx, dy, oz + t * dz); + long src = BlockPos.offset(dst, dir); + + if (neighborLightArray == null) + src = BlockPos.asLong(BlockPos.unpackLongX(src), ChunkSectionPos.getWorldCoord(ChunkSectionPos.getY(neighborCeilingSectionPos)), BlockPos.unpackLongZ(src)); + + final int srcLevel = neighborCeilingLightArray != null ? + ((ChunkLightProviderInterface) lightProvider).callGetCurrentLevelFromArray(neighborCeilingLightArray, src) + : this.isLightEnabled(ChunkSectionPos.withZeroZ(neighborCeilingSectionPos)) ? 0 : 15; + + levelPropagator.invokeUpdateLevel(src, dst, levelPropagator.callGetPropagatedLevel(src, dst, srcLevel), true); + } + } + } + } +} diff --git a/src/main/java/carpet/mixins/ThreadedAnvilChunkStorage_scarpetChunkCreationMixin.java b/src/main/java/carpet/mixins/ThreadedAnvilChunkStorage_scarpetChunkCreationMixin.java index e5daf3dc..6dd944be 100644 --- a/src/main/java/carpet/mixins/ThreadedAnvilChunkStorage_scarpetChunkCreationMixin.java +++ b/src/main/java/carpet/mixins/ThreadedAnvilChunkStorage_scarpetChunkCreationMixin.java @@ -1,222 +1,402 @@ package carpet.mixins; -import carpet.fakes.ChunkHolderInterface; -import carpet.fakes.ServerLightingProviderInterface; -import carpet.fakes.ThreadedAnvilChunkStorageInterface; -import carpet.script.utils.WorldTools; -import com.mojang.datafixers.util.Either; -import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; -import it.unimi.dsi.fastutil.longs.LongSet; -import net.minecraft.server.WorldGenerationProgressListener; -import net.minecraft.server.world.ChunkHolder; -import net.minecraft.server.world.ChunkTaskPrioritySystem; -import net.minecraft.server.world.ServerLightingProvider; -import net.minecraft.server.world.ServerWorld; -import net.minecraft.server.world.ThreadedAnvilChunkStorage; -import net.minecraft.util.math.ChunkPos; -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; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Queue; +import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.function.IntFunction; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.tuple.Pair; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import com.mojang.datafixers.util.Either; + +import carpet.fakes.ChunkHolderInterface; +import carpet.fakes.ChunkTicketManagerInterface; +import carpet.fakes.ServerLightingProviderInterface; +import carpet.fakes.ThreadedAnvilChunkStorageInterface; +import carpet.script.utils.WorldTools; +import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; +import it.unimi.dsi.fastutil.longs.LongSet; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import net.minecraft.server.WorldGenerationProgressListener; +import net.minecraft.server.world.ChunkHolder; +import net.minecraft.server.world.ChunkHolder.Unloaded; +import net.minecraft.server.world.ChunkTaskPrioritySystem; +import net.minecraft.server.world.ChunkTicketType; +import net.minecraft.server.world.ServerLightingProvider; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.server.world.ThreadedAnvilChunkStorage; +import net.minecraft.server.world.ThreadedAnvilChunkStorage.TicketManager; +import net.minecraft.util.Util; +import net.minecraft.util.math.ChunkPos; +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 static carpet.script.CarpetEventServer.Event.CHUNK_GENERATED; @Mixin(ThreadedAnvilChunkStorage.class) public abstract class ThreadedAnvilChunkStorage_scarpetChunkCreationMixin implements ThreadedAnvilChunkStorageInterface { - @Shadow @Final private ServerWorld world; + @Shadow + @Final + private ServerWorld world; - @Shadow @Final private LongSet loadedChunks; + @Shadow + @Final + private LongSet loadedChunks; - @Shadow @Final private LongSet unloadedChunks; + @Shadow + @Final + private Long2ObjectLinkedOpenHashMap currentChunkHolders; - @Shadow @Final private Long2ObjectLinkedOpenHashMap currentChunkHolders; + @Shadow + private boolean chunkHolderListDirty; - @Shadow @Final private Long2ObjectLinkedOpenHashMap field_18807; + @Shadow + @Final + private ServerLightingProvider serverLightingProvider; - @Shadow private boolean chunkHolderListDirty; + @Shadow + @Final + private ChunkTaskPrioritySystem chunkTaskPrioritySystem; - @Shadow protected abstract void method_20458(long l, ChunkHolder chunkHolder); + @Shadow + @Final + private ThreadExecutor mainThreadExecutor; - @Shadow @Final private Queue field_19343; + @Shadow + @Final + private WorldGenerationProgressListener worldGenerationProgressListener; - @Shadow @Final private ServerLightingProvider serverLightingProvider; + @Shadow + @Final + private TicketManager ticketManager; - @Shadow @Final private ChunkTaskPrioritySystem chunkTaskPrioritySystem; + @Shadow + protected abstract boolean updateHolderMap(); - @Shadow protected abstract CompletableFuture> convertToFullChunk(ChunkHolder chunkHolder); - - @Shadow public abstract CompletableFuture> createChunkFuture(ChunkHolder chunkHolder, ChunkStatus chunkStatus); - - @Shadow @Final private ThreadExecutor mainThreadExecutor; - - @Shadow protected abstract boolean save(Chunk chunk); - - @Shadow @Final private WorldGenerationProgressListener worldGenerationProgressListener; + @Shadow + protected abstract CompletableFuture, Unloaded>> createChunkRegionFuture(final ChunkPos centerChunk, final int margin, final IntFunction distanceToStatus); //in method_20617 //method_19534(Lnet/minecraft/server/world/ChunkHolder;Lnet/minecraft/world/chunk/Chunk;)Ljava/util/concurrent/CompletableFuture; // incmopatibility with optifine makes this mixin fail. @Inject(method = "method_19534", require = 0, at = @At( - value = "INVOKE", - target = "Lnet/minecraft/server/world/ThreadedAnvilChunkStorage;convertToFullChunk(Lnet/minecraft/server/world/ChunkHolder;)Ljava/util/concurrent/CompletableFuture;", - shift = At.Shift.AFTER + value = "INVOKE", + target = "Lnet/minecraft/server/world/ThreadedAnvilChunkStorage;convertToFullChunk(Lnet/minecraft/server/world/ChunkHolder;)Ljava/util/concurrent/CompletableFuture;", + shift = At.Shift.AFTER )) - private void onChunkGenerated(ChunkHolder chunkHolder, Chunk chunk, CallbackInfoReturnable cir) + private void onChunkGenerated(final ChunkHolder chunkHolder, final Chunk chunk, final CallbackInfoReturnable cir) { if (CHUNK_GENERATED.isNeeded()) - world.getServer().execute( () -> CHUNK_GENERATED.onChunkGenerated(world, chunk) ); + this.world.getServer().execute(() -> CHUNK_GENERATED.onChunkGenerated(this.world, chunk)); + } + + @Unique + private void addTicket(final ChunkPos pos, final ChunkStatus status) + { + this.ticketManager.addTicketWithLevel(ChunkTicketType.UNKNOWN, pos, 33 + ChunkStatus.getTargetGenerationRadius(status), pos); + } + + @Unique + private void addTicket(final ChunkPos pos) + { + this.addTicket(pos, ChunkStatus.EMPTY); + } + + @Unique + private void addRelightTicket(final ChunkPos pos) + { + this.ticketManager.addTicket(ChunkTicketType.LIGHT, pos, 1, pos); } @Override - public Map regenerateChunkRegion(List requestedChunks) + public void releaseRelightTicket(final ChunkPos pos) { - 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 - // hopefully that would flush all chunk writes, including current chunk, if exists - Runnable runnable; - while((runnable = field_19343.poll()) != null) { - runnable.run(); - } + this.mainThreadExecutor.send(Util.debugRunnable( + () -> this.ticketManager.removeTicket(ChunkTicketType.LIGHT, pos, 1, pos), + () -> "release relight ticket " + pos + )); + } - Set loadedChunks = new HashSet<>(); - for (ChunkPos pos: requestedChunks) + @Unique + private void tickTicketManager() + { + this.ticketManager.tick((ThreadedAnvilChunkStorage) (Object) this); + } + + @Unique + private Set getExistingChunks(final Set requestedChunks) + { + final Map regionCache = new HashMap<>(); + final Set ret = new HashSet<>(); + + for (final ChunkPos pos : requestedChunks) + if (WorldTools.canHasChunk(this.world, pos, regionCache, true)) + ret.add(pos); + + return ret; + } + + @Unique + private Set loadExistingChunksFromDisk(final Set requestedChunks) + { + final Set existingChunks = this.getExistingChunks(requestedChunks); + for (final ChunkPos pos : existingChunks) + this.currentChunkHolders.get(pos.toLong()).createFuture(ChunkStatus.EMPTY, (ThreadedAnvilChunkStorage) (Object) this); + + return existingChunks; + } + + @Unique + private Set loadExistingChunks(final Set requestedChunks, final Object2IntMap report) + { + if (report != null) + report.put("requested_chunks", requestedChunks.size()); + + // Load all relevant ChunkHolders into this.currentChunkHolders + // This will not trigger loading from disk yet + + for (final ChunkPos pos : requestedChunks) + this.addTicket(pos); + + this.tickTicketManager(); + + // Fetch all currently loaded chunks + + final Set loadedChunks = requestedChunks.stream().filter( + pos -> this.currentChunkHolders.get(pos.toLong()).getCompletedChunk() != null // all relevant ChunkHolders exist + ).collect(Collectors.toSet()); + + if (report != null) + report.put("loaded_chunks", loadedChunks.size()); + + // Load remaining chunks from disk + + final Set unloadedChunks = new HashSet<>(requestedChunks); + unloadedChunks.removeAll(loadedChunks); + + final Set existingChunks = this.loadExistingChunksFromDisk(unloadedChunks); + + existingChunks.addAll(loadedChunks); + + return existingChunks; + } + + @Unique + private Set loadExistingChunks(final Set requestedChunks) + { + return this.loadExistingChunks(requestedChunks, null); + } + + @Unique + private void waitFor(final Future future) + { + this.mainThreadExecutor.runTasks(future::isDone); + } + + @Unique + private void waitFor(final List> futures) + { + this.waitFor(Util.combine(futures)); + } + + @Unique + private Chunk getCurrentChunk(final ChunkPos pos) + { + final CompletableFuture future = this.currentChunkHolders.get(pos.toLong()).getFuture(); + this.waitFor(future); + + return future.join(); + } + + @Override + public Map regenerateChunkRegion(final List requestedChunksList) + { + final Object2IntMap report = new Object2IntOpenHashMap<>(); + final Set requestedChunks = new HashSet<>(requestedChunksList); + + // Load requested chunks + + final Set existingChunks = this.loadExistingChunks(requestedChunks, report); + + // Finish pending generation stages + // This ensures that no generation events will be put back on the main thread after the chunks have been deleted + + final Set affectedChunks = new HashSet<>(); + + for (final ChunkPos pos : existingChunks) + affectedChunks.add(this.getCurrentChunk(pos)); + + report.put("affected_chunks", affectedChunks.size()); + + // Load neighbors for light removal + + final Set neighbors = new HashSet<>(); + + for (final Chunk chunk : affectedChunks) { - Chunk chunk = world.getChunk(pos.x, pos.z, ChunkStatus.FULL, false); - if (chunk != null && chunk.getStatus() != ChunkStatus.EMPTY) - loadedChunks.add(pos); - } - report.put("loaded_chunks", loadedChunks.size()); + final ChunkPos pos = chunk.getPos(); - Map targetStatus = new HashMap<>(); - Map regionCache = new HashMap<>(); - for (ChunkPos pos: requestedChunks) - { - if (!WorldTools.canHasChunk(world, pos, regionCache, true)) 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); - } - regionCache.clear(); - report.put("affected_chunks", targetStatus.size()); - - - for (ChunkPos chpos : targetStatus.keySet()) - { - Long id = chpos.toLong(); - //chunk is currently in memory in some shape or form / loaded / unloaded / queued / cached - ChunkHolder chunkHolder = currentChunkHolders.remove(id); - int currentLevel = ThreadedAnvilChunkStorage.MAX_LEVEL; - if (chunkHolder != null) - { - currentLevel = chunkHolder.getLevel()-1; - //method_20458(id, chunkHolder); // saving chunk // skipping entities etc - Chunk chunk = null; - try - { - // fingers crossed - chunk = chunkHolder.getFuture().get(); - } - catch (InterruptedException | ExecutionException e) - { - } - // if (field_18807.remove(id, chunkHolder) && chunk != null) { - if (chunk != null) - { - if (chunk instanceof WorldChunk) + for (int x = -1; x <= 1; ++x) + for (int z = -1; z <= 1; ++z) + if (x != 0 || z != 0) { - ((WorldChunk) chunk).setLoadedToWorld(false); + final ChunkPos nPos = new ChunkPos(pos.x + x, pos.z + z); + if (!requestedChunks.contains(nPos)) + neighbors.add(nPos); } - save(chunk); - if (this.loadedChunks.remove(id) && chunk instanceof WorldChunk) - { - WorldChunk worldChunk = (WorldChunk) chunk; - //apparently that doesn't remove them for the client - this.world.unloadEntities(worldChunk); - } - ((ServerLightingProviderInterface)this.serverLightingProvider).publicUpdateChunkStatus(chunk.getPos()); // we might need that as some point - serverLightingProvider.tick(); - worldGenerationProgressListener.setChunkStatus(chunk.getPos(), (ChunkStatus) null); - } - } - unloadedChunks.remove(id); - loadedChunks.remove(id); - field_18807.remove(id); - ChunkHolder newHolder = new ChunkHolder(chpos, currentLevel,serverLightingProvider, chunkTaskPrioritySystem, (ChunkHolder.PlayersWatchingChunkProvider) this); - ((ChunkHolderInterface)newHolder).setDefaultProtoChunk(chpos, mainThreadExecutor); - this.currentChunkHolders.put(id, newHolder); - //removed holders - field_18807.put(id, newHolder); - //loadedChunks.add(id); - } - //getting rid of all signs + + this.loadExistingChunks(neighbors); + + // Determine affected neighbors + + final Set affectedNeighbors = new HashSet<>(); + + for (final ChunkPos pos : neighbors) + { + final Chunk chunk = this.getCurrentChunk(pos); + + if (chunk.getStatus().isAtLeast(ChunkStatus.LIGHT.getPrevious())) + affectedNeighbors.add(chunk); + } + + // Unload affected chunks + + for (final Chunk chunk : affectedChunks) + { + final ChunkPos pos = chunk.getPos(); + + if (chunk instanceof WorldChunk) + ((WorldChunk) chunk).setLoadedToWorld(false); + + if (this.loadedChunks.remove(pos.toLong()) && chunk instanceof WorldChunk) + this.world.unloadEntities((WorldChunk) chunk); + + ((ServerLightingProviderInterface) this.serverLightingProvider).invokeUpdateChunkStatus(pos); + ((ServerLightingProviderInterface) this.serverLightingProvider).removeLightData(chunk); + + this.worldGenerationProgressListener.setChunkStatus(pos, null); + } + + // Replace ChunkHolders + + for (final Chunk chunk : affectedChunks) + { + final ChunkPos cPos = chunk.getPos(); + final long pos = cPos.toLong(); + + final ChunkHolder oldHolder = this.currentChunkHolders.remove(pos); + final ChunkHolder newHolder = new ChunkHolder(cPos, oldHolder.getLevel(), this.serverLightingProvider, this.chunkTaskPrioritySystem, (ChunkHolder.PlayersWatchingChunkProvider) this); + ((ChunkHolderInterface) newHolder).setDefaultProtoChunk(cPos, this.mainThreadExecutor); + this.currentChunkHolders.put(pos, newHolder); + + ((ChunkTicketManagerInterface) this.ticketManager).replaceHolder(oldHolder, newHolder); + } + this.chunkHolderListDirty = true; - // not needed right now - mainThreadExecutor.runTasks(() -> mainThreadExecutor.getTaskCount() == 0); - ((ServerLightingProviderInterface)this.serverLightingProvider).flush(); - //worldgenExecutor. - // save all pending chunks / potentially only one max - ours - // hopefully that would flush all chunk writes, including current chink - while ((runnable = field_19343.poll()) != null) + this.updateHolderMap(); + + // Remove light for affected neighbors + + for (final Chunk chunk : affectedNeighbors) + ((ServerLightingProviderInterface) this.serverLightingProvider).removeLightData(chunk); + + // Schedule relighting of neighbors + + for (final Chunk chunk : affectedNeighbors) + this.addRelightTicket(chunk.getPos()); + + this.tickTicketManager(); + + final List> lightFutures = new ArrayList<>(); + + for (final Chunk chunk : affectedNeighbors) { - runnable.run(); + final ChunkPos pos = chunk.getPos(); + + lightFutures.add(this.createChunkRegionFuture(pos, 1, (pos_) -> ChunkStatus.LIGHT).thenCompose( + either -> either.map( + list -> ((ServerLightingProviderInterface) this.serverLightingProvider).relight(chunk), + unloaded -> { + this.releaseRelightTicket(pos); + return CompletableFuture.completedFuture(null); + } + ) + )); } - for (ChunkStatus status : ChunkStatus.createOrderedList()) + // Force generation to previous states + // This ensures that the world is in a consistent state after this method + // Also, this is needed to ensure chunks are saved to disk + + final Map targetGenerationStatus = affectedChunks.stream().collect( + Collectors.toMap(Chunk::getPos, Chunk::getStatus) + ); + + for (final Entry entry : targetGenerationStatus.entrySet()) + this.addTicket(entry.getKey(), entry.getValue()); + + this.tickTicketManager(); + + final List>> targetGenerationFutures = new ArrayList<>(); + + for (final Entry entry : targetGenerationStatus.entrySet()) + targetGenerationFutures.add(Pair.of( + entry.getValue(), + this.currentChunkHolders.get(entry.getKey().toLong()).createFuture(entry.getValue(), (ThreadedAnvilChunkStorage) (Object) this) + )); + + final Map>> targetGenerationFuturesGrouped = targetGenerationFutures.stream().collect( + Collectors.groupingBy( + Pair::getKey, + Collectors.mapping( + Entry::getValue, + Collectors.toList() + ) + ) + ); + + for (final ChunkStatus status : ChunkStatus.createOrderedList()) { - report.put("layer_count_"+status.getId(),targetStatus.size()); - long start = System.currentTimeMillis(); - //if (status == ChunkStatus.LIGHT) - //{ - // serverLightingProvider.setTaskBatchSize(targetStatus.size() + 20); - //} - for (ChunkPos chpos : new ArrayList<>(targetStatus.keySet())) - { - if (targetStatus.get(chpos) == status) - targetStatus.remove(chpos); - world.getChunk(chpos.x, chpos.z, status); - if (status == ChunkStatus.FULL) - convertToFullChunk(currentChunkHolders.get(chpos.toLong())); - } - if (status == ChunkStatus.LIGHT) - { // default task batch size after initial generation is done - //serverLightingProvider.setTaskBatchSize(5); - ((ServerLightingProviderInterface)this.serverLightingProvider).flush(); - } - report.put("layer_time_"+status.getId(), (int) (System.currentTimeMillis()-start)); + final List> futures = targetGenerationFuturesGrouped.get(status); + + if (futures == null) + continue; + + report.put("layer_count_" + status.getId(), futures.size()); + final long start = System.currentTimeMillis(); + + this.waitFor(futures); + + report.put("layer_time_" + status.getId(), (int) (System.currentTimeMillis() - start)); } - mainThreadExecutor.runTasks(() -> mainThreadExecutor.getTaskCount() == 0); - //worldgenExecutor. - // save all pending chunks / potentially only one max - ours - // hopefully that would flush all chunk writes, including current chink - while ((runnable = field_19343.poll()) != null) - { - runnable.run(); - } + report.put("relight_count", lightFutures.size()); + final long relightStart = System.currentTimeMillis(); + + this.waitFor(lightFutures); + + report.put("relight_time", (int) (System.currentTimeMillis() - relightStart)); + return report; } } diff --git a/src/main/resources/carpet.accesswidener b/src/main/resources/carpet.accesswidener new file mode 100644 index 00000000..901a39b9 --- /dev/null +++ b/src/main/resources/carpet.accesswidener @@ -0,0 +1,4 @@ +accessWidener v1 named + +accessible class net/minecraft/server/world/ThreadedAnvilChunkStorage$TicketManager +accessible class net/minecraft/server/world/ServerLightingProvider$Stage diff --git a/src/main/resources/carpet.mixins.json b/src/main/resources/carpet.mixins.json index 7a8ee568..a82720f7 100644 --- a/src/main/resources/carpet.mixins.json +++ b/src/main/resources/carpet.mixins.json @@ -33,6 +33,13 @@ "ThreadedAnvilChunkStorage_scarpetChunkCreationMixin", "ChunkHolder_scarpetChunkCreationMixin", "ServerLightingProvider_scarpetMixin", + "LevelPropagatorInterface", + "ChunkTicketManager_scarpetChunkCreationMixin", + "LightingProvider_scarpetChunkCreationMixin", + "ChunkLightProvider_scarpetChunkCreationMixin", + "LightStorage_scarpetChunkCreationMixin", + "BlockLightStorage_scarpetChunkCreationMixin", + "SkyLightStorage_scarpetChunkCreationMixin", "ChunkTicketManager_spawnChunksMixin", "ServerWorld_spawnChunksMixin", diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index b31f6f4c..07954291 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -24,7 +24,7 @@ "mixins": [ "carpet.mixins.json" ], - + "accesswidener": "carpet.accesswidener", "depends": { "minecraft": "1.15.x", "fabricloader": ">=0.7.1"