Merge pull request #359 from PhiPro95/light
Rewrite regenerateChunkRegion
This commit is contained in:
19 files changed
+876
-208
No files matched your search
@@ -11,6 +11,7 @@ version = project.minecraft_version+'-'+project.mod_version
|
|||||||
group = project.maven_group
|
group = project.maven_group
|
||||||
|
|
||||||
minecraft {
|
minecraft {
|
||||||
|
accessWidener "src/main/resources/carpet.accesswidener"
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package carpet.fakes;
|
||||||
|
|
||||||
|
import net.minecraft.world.chunk.ChunkNibbleArray;
|
||||||
|
|
||||||
|
public interface ChunkLightProviderInterface
|
||||||
|
{
|
||||||
|
int callGetCurrentLevelFromArray(ChunkNibbleArray array, long blockPos);
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package carpet.fakes;
|
package carpet.fakes;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||||
|
import net.minecraft.server.world.ChunkHolder;
|
||||||
import net.minecraft.server.world.ChunkTicket;
|
import net.minecraft.server.world.ChunkTicket;
|
||||||
import net.minecraft.util.SortedArraySet;
|
import net.minecraft.util.SortedArraySet;
|
||||||
import net.minecraft.util.math.ChunkPos;
|
import net.minecraft.util.math.ChunkPos;
|
||||||
@@ -8,5 +9,8 @@ import net.minecraft.util.math.ChunkPos;
|
|||||||
public interface ChunkTicketManagerInterface
|
public interface ChunkTicketManagerInterface
|
||||||
{
|
{
|
||||||
void changeSpawnChunks(ChunkPos pos, int distance);
|
void changeSpawnChunks(ChunkPos pos, int distance);
|
||||||
|
|
||||||
Long2ObjectOpenHashMap<SortedArraySet<ChunkTicket<?>>> getTicketsByPosition();
|
Long2ObjectOpenHashMap<SortedArraySet<ChunkTicket<?>>> getTicketsByPosition();
|
||||||
|
|
||||||
|
void replaceHolder(ChunkHolder oldHolder, ChunkHolder newHolder);
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package carpet.fakes;
|
||||||
|
|
||||||
|
public interface Lighting_scarpetChunkCreationInterface
|
||||||
|
{
|
||||||
|
void removeLightData(long pos);
|
||||||
|
|
||||||
|
void relight(long pos);
|
||||||
|
}
|
||||||
@@ -1,11 +1,17 @@
|
|||||||
package carpet.fakes;
|
package carpet.fakes;
|
||||||
|
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
import net.minecraft.util.math.ChunkPos;
|
import net.minecraft.util.math.ChunkPos;
|
||||||
import net.minecraft.world.chunk.Chunk;
|
import net.minecraft.world.chunk.Chunk;
|
||||||
|
|
||||||
public interface ServerLightingProviderInterface
|
public interface ServerLightingProviderInterface
|
||||||
{
|
{
|
||||||
void publicUpdateChunkStatus(ChunkPos pos);
|
void invokeUpdateChunkStatus(ChunkPos pos);
|
||||||
void flush();
|
|
||||||
|
void removeLightData(Chunk chunk);
|
||||||
|
|
||||||
|
CompletableFuture<Void> relight(Chunk chunk);
|
||||||
|
|
||||||
void resetLight(Chunk chunk, ChunkPos pos);
|
void resetLight(Chunk chunk, ChunkPos pos);
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,13 @@
|
|||||||
package carpet.fakes;
|
package carpet.fakes;
|
||||||
|
|
||||||
import net.minecraft.util.math.ChunkPos;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import net.minecraft.util.math.ChunkPos;
|
||||||
|
|
||||||
public interface ThreadedAnvilChunkStorageInterface
|
public interface ThreadedAnvilChunkStorageInterface
|
||||||
{
|
{
|
||||||
Map<String, Integer> regenerateChunkRegion(List<ChunkPos> requestedChunks);
|
Map<String, Integer> regenerateChunkRegion(List<ChunkPos> requestedChunks);
|
||||||
|
|
||||||
|
void releaseRelightTicket(ChunkPos pos);
|
||||||
}
|
}
|
||||||
@@ -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<Data> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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<ChunkHolder> chunkHolders;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void replaceHolder(final ChunkHolder oldHolder, final ChunkHolder newHolder)
|
||||||
|
{
|
||||||
|
this.chunkHolders.remove(oldHolder);
|
||||||
|
this.chunkHolders.add(newHolder);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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<ChunkNibbleArray> 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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,54 +1,84 @@
|
|||||||
package carpet.mixins;
|
package carpet.mixins;
|
||||||
|
|
||||||
import carpet.fakes.ServerLightingProviderInterface;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectList;
|
import java.util.function.IntSupplier;
|
||||||
import net.minecraft.server.world.ServerLightingProvider;
|
|
||||||
import net.minecraft.util.math.ChunkPos;
|
|
||||||
import org.spongepowered.asm.mixin.Final;
|
import org.spongepowered.asm.mixin.Final;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import carpet.fakes.Lighting_scarpetChunkCreationInterface;
|
||||||
|
import carpet.fakes.ServerLightingProviderInterface;
|
||||||
import static java.lang.Thread.yield;
|
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)
|
@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
|
||||||
|
@Final
|
||||||
@Shadow public abstract void setTaskBatchSize(int taskBatchSize);
|
private ThreadedAnvilChunkStorage chunkStorage;
|
||||||
|
|
||||||
@Shadow @Final private AtomicBoolean field_18812;
|
|
||||||
|
|
||||||
@Shadow @Final private ObjectList pendingTasks;
|
|
||||||
|
|
||||||
@Shadow protected abstract void runTasks();
|
|
||||||
|
|
||||||
@Override
|
@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
|
@Override
|
||||||
public void flush()
|
public CompletableFuture<Void> relight(final Chunk chunk)
|
||||||
{
|
{
|
||||||
try
|
final ChunkPos pos = chunk.getPos();
|
||||||
{
|
|
||||||
while (!field_18812.compareAndSet(false, true)) yield();
|
this.enqueue(pos.x, pos.z, () -> 0, ServerLightingProvider.Stage.PRE_UPDATE, Util.debugRunnable(() -> {
|
||||||
if (pendingTasks.size() == 0) return;
|
super.setLightEnabled(pos, true);
|
||||||
int prevTaskBatchSize = taskBatchSize;
|
|
||||||
setTaskBatchSize(pendingTasks.size());
|
chunk.getLightSourcesStream().forEach(
|
||||||
runTasks();
|
blockPos -> super.addLightSource(blockPos, chunk.getLuminance(blockPos))
|
||||||
setTaskBatchSize(prevTaskBatchSize);
|
);
|
||||||
}
|
|
||||||
finally
|
((Lighting_scarpetChunkCreationInterface) this).relight(ChunkSectionPos.withZeroZ(ChunkSectionPos.asLong(pos.x, 0, pos.z)));
|
||||||
{
|
},
|
||||||
this.field_18812.set(false);
|
() -> "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)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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<Data> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+349
-169
@@ -1,222 +1,402 @@
|
|||||||
package carpet.mixins;
|
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.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Queue;
|
import java.util.Map.Entry;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CompletableFuture;
|
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;
|
import static carpet.script.CarpetEventServer.Event.CHUNK_GENERATED;
|
||||||
|
|
||||||
@Mixin(ThreadedAnvilChunkStorage.class)
|
@Mixin(ThreadedAnvilChunkStorage.class)
|
||||||
public abstract class ThreadedAnvilChunkStorage_scarpetChunkCreationMixin implements ThreadedAnvilChunkStorageInterface
|
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<ChunkHolder> currentChunkHolders;
|
||||||
|
|
||||||
@Shadow @Final private Long2ObjectLinkedOpenHashMap<ChunkHolder> currentChunkHolders;
|
@Shadow
|
||||||
|
private boolean chunkHolderListDirty;
|
||||||
|
|
||||||
@Shadow @Final private Long2ObjectLinkedOpenHashMap<ChunkHolder> 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<Runnable> mainThreadExecutor;
|
||||||
|
|
||||||
@Shadow @Final private Queue<Runnable> 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<Either<Chunk, ChunkHolder.Unloaded>> convertToFullChunk(ChunkHolder chunkHolder);
|
@Shadow
|
||||||
|
protected abstract CompletableFuture<Either<List<Chunk>, Unloaded>> createChunkRegionFuture(final ChunkPos centerChunk, final int margin, final IntFunction<ChunkStatus> distanceToStatus);
|
||||||
@Shadow public abstract CompletableFuture<Either<Chunk, ChunkHolder.Unloaded>> createChunkFuture(ChunkHolder chunkHolder, ChunkStatus chunkStatus);
|
|
||||||
|
|
||||||
@Shadow @Final private ThreadExecutor<Runnable> mainThreadExecutor;
|
|
||||||
|
|
||||||
@Shadow protected abstract boolean save(Chunk chunk);
|
|
||||||
|
|
||||||
@Shadow @Final private WorldGenerationProgressListener worldGenerationProgressListener;
|
|
||||||
|
|
||||||
//in method_20617
|
//in method_20617
|
||||||
//method_19534(Lnet/minecraft/server/world/ChunkHolder;Lnet/minecraft/world/chunk/Chunk;)Ljava/util/concurrent/CompletableFuture;
|
//method_19534(Lnet/minecraft/server/world/ChunkHolder;Lnet/minecraft/world/chunk/Chunk;)Ljava/util/concurrent/CompletableFuture;
|
||||||
// incmopatibility with optifine makes this mixin fail.
|
// incmopatibility with optifine makes this mixin fail.
|
||||||
@Inject(method = "method_19534", require = 0, at = @At(
|
@Inject(method = "method_19534", require = 0, at = @At(
|
||||||
value = "INVOKE",
|
value = "INVOKE",
|
||||||
target = "Lnet/minecraft/server/world/ThreadedAnvilChunkStorage;convertToFullChunk(Lnet/minecraft/server/world/ChunkHolder;)Ljava/util/concurrent/CompletableFuture;",
|
target = "Lnet/minecraft/server/world/ThreadedAnvilChunkStorage;convertToFullChunk(Lnet/minecraft/server/world/ChunkHolder;)Ljava/util/concurrent/CompletableFuture;",
|
||||||
shift = At.Shift.AFTER
|
shift = At.Shift.AFTER
|
||||||
))
|
))
|
||||||
private void onChunkGenerated(ChunkHolder chunkHolder, Chunk chunk, CallbackInfoReturnable<CompletableFuture> cir)
|
private void onChunkGenerated(final ChunkHolder chunkHolder, final Chunk chunk, final CallbackInfoReturnable<CompletableFuture> cir)
|
||||||
{
|
{
|
||||||
if (CHUNK_GENERATED.isNeeded())
|
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
|
@Override
|
||||||
public Map<String, Integer> regenerateChunkRegion(List<ChunkPos> requestedChunks)
|
public void releaseRelightTicket(final ChunkPos pos)
|
||||||
{
|
{
|
||||||
Map<String, Integer> report = new HashMap<>();
|
this.mainThreadExecutor.send(Util.debugRunnable(
|
||||||
report.put("requested_chunks",requestedChunks.size());
|
() -> this.ticketManager.removeTicket(ChunkTicketType.LIGHT, pos, 1, pos),
|
||||||
//flushning all tasks on the thread executor so all futures are ready - we don't want any deadlocks
|
() -> "release relight ticket " + pos
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<ChunkPos> loadedChunks = new HashSet<>();
|
@Unique
|
||||||
for (ChunkPos pos: requestedChunks)
|
private void tickTicketManager()
|
||||||
|
{
|
||||||
|
this.ticketManager.tick((ThreadedAnvilChunkStorage) (Object) this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private Set<ChunkPos> getExistingChunks(final Set<ChunkPos> requestedChunks)
|
||||||
|
{
|
||||||
|
final Map<String, RegionFile> regionCache = new HashMap<>();
|
||||||
|
final Set<ChunkPos> ret = new HashSet<>();
|
||||||
|
|
||||||
|
for (final ChunkPos pos : requestedChunks)
|
||||||
|
if (WorldTools.canHasChunk(this.world, pos, regionCache, true))
|
||||||
|
ret.add(pos);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private Set<ChunkPos> loadExistingChunksFromDisk(final Set<ChunkPos> requestedChunks)
|
||||||
|
{
|
||||||
|
final Set<ChunkPos> 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<ChunkPos> loadExistingChunks(final Set<ChunkPos> requestedChunks, final Object2IntMap<String> 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<ChunkPos> 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<ChunkPos> unloadedChunks = new HashSet<>(requestedChunks);
|
||||||
|
unloadedChunks.removeAll(loadedChunks);
|
||||||
|
|
||||||
|
final Set<ChunkPos> existingChunks = this.loadExistingChunksFromDisk(unloadedChunks);
|
||||||
|
|
||||||
|
existingChunks.addAll(loadedChunks);
|
||||||
|
|
||||||
|
return existingChunks;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private Set<ChunkPos> loadExistingChunks(final Set<ChunkPos> requestedChunks)
|
||||||
|
{
|
||||||
|
return this.loadExistingChunks(requestedChunks, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private void waitFor(final Future<?> future)
|
||||||
|
{
|
||||||
|
this.mainThreadExecutor.runTasks(future::isDone);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private void waitFor(final List<? extends CompletableFuture<?>> futures)
|
||||||
|
{
|
||||||
|
this.waitFor(Util.combine(futures));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private Chunk getCurrentChunk(final ChunkPos pos)
|
||||||
|
{
|
||||||
|
final CompletableFuture<Chunk> future = this.currentChunkHolders.get(pos.toLong()).getFuture();
|
||||||
|
this.waitFor(future);
|
||||||
|
|
||||||
|
return future.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Integer> regenerateChunkRegion(final List<ChunkPos> requestedChunksList)
|
||||||
|
{
|
||||||
|
final Object2IntMap<String> report = new Object2IntOpenHashMap<>();
|
||||||
|
final Set<ChunkPos> requestedChunks = new HashSet<>(requestedChunksList);
|
||||||
|
|
||||||
|
// Load requested chunks
|
||||||
|
|
||||||
|
final Set<ChunkPos> 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<Chunk> 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<ChunkPos> neighbors = new HashSet<>();
|
||||||
|
|
||||||
|
for (final Chunk chunk : affectedChunks)
|
||||||
{
|
{
|
||||||
Chunk chunk = world.getChunk(pos.x, pos.z, ChunkStatus.FULL, false);
|
final ChunkPos pos = chunk.getPos();
|
||||||
if (chunk != null && chunk.getStatus() != ChunkStatus.EMPTY)
|
|
||||||
loadedChunks.add(pos);
|
|
||||||
}
|
|
||||||
report.put("loaded_chunks", loadedChunks.size());
|
|
||||||
|
|
||||||
Map<ChunkPos,ChunkStatus> targetStatus = new HashMap<>();
|
for (int x = -1; x <= 1; ++x)
|
||||||
Map<String, RegionFile> regionCache = new HashMap<>();
|
for (int z = -1; z <= 1; ++z)
|
||||||
for (ChunkPos pos: requestedChunks)
|
if (x != 0 || z != 0)
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
((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<Chunk> 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;
|
this.chunkHolderListDirty = true;
|
||||||
// not needed right now
|
this.updateHolderMap();
|
||||||
mainThreadExecutor.runTasks(() -> mainThreadExecutor.getTaskCount() == 0);
|
|
||||||
((ServerLightingProviderInterface)this.serverLightingProvider).flush();
|
// Remove light for affected neighbors
|
||||||
//worldgenExecutor.
|
|
||||||
// save all pending chunks / potentially only one max - ours
|
for (final Chunk chunk : affectedNeighbors)
|
||||||
// hopefully that would flush all chunk writes, including current chink
|
((ServerLightingProviderInterface) this.serverLightingProvider).removeLightData(chunk);
|
||||||
while ((runnable = field_19343.poll()) != null)
|
|
||||||
|
// Schedule relighting of neighbors
|
||||||
|
|
||||||
|
for (final Chunk chunk : affectedNeighbors)
|
||||||
|
this.addRelightTicket(chunk.getPos());
|
||||||
|
|
||||||
|
this.tickTicketManager();
|
||||||
|
|
||||||
|
final List<CompletableFuture<?>> 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<ChunkPos, ChunkStatus> targetGenerationStatus = affectedChunks.stream().collect(
|
||||||
|
Collectors.toMap(Chunk::getPos, Chunk::getStatus)
|
||||||
|
);
|
||||||
|
|
||||||
|
for (final Entry<ChunkPos, ChunkStatus> entry : targetGenerationStatus.entrySet())
|
||||||
|
this.addTicket(entry.getKey(), entry.getValue());
|
||||||
|
|
||||||
|
this.tickTicketManager();
|
||||||
|
|
||||||
|
final List<Pair<ChunkStatus, CompletableFuture<?>>> targetGenerationFutures = new ArrayList<>();
|
||||||
|
|
||||||
|
for (final Entry<ChunkPos, ChunkStatus> entry : targetGenerationStatus.entrySet())
|
||||||
|
targetGenerationFutures.add(Pair.of(
|
||||||
|
entry.getValue(),
|
||||||
|
this.currentChunkHolders.get(entry.getKey().toLong()).createFuture(entry.getValue(), (ThreadedAnvilChunkStorage) (Object) this)
|
||||||
|
));
|
||||||
|
|
||||||
|
final Map<ChunkStatus, List<CompletableFuture<?>>> 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());
|
final List<CompletableFuture<?>> futures = targetGenerationFuturesGrouped.get(status);
|
||||||
long start = System.currentTimeMillis();
|
|
||||||
//if (status == ChunkStatus.LIGHT)
|
if (futures == null)
|
||||||
//{
|
continue;
|
||||||
// serverLightingProvider.setTaskBatchSize(targetStatus.size() + 20);
|
|
||||||
//}
|
report.put("layer_count_" + status.getId(), futures.size());
|
||||||
for (ChunkPos chpos : new ArrayList<>(targetStatus.keySet()))
|
final long start = System.currentTimeMillis();
|
||||||
{
|
|
||||||
if (targetStatus.get(chpos) == status)
|
this.waitFor(futures);
|
||||||
targetStatus.remove(chpos);
|
|
||||||
world.getChunk(chpos.x, chpos.z, status);
|
report.put("layer_time_" + status.getId(), (int) (System.currentTimeMillis() - start));
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mainThreadExecutor.runTasks(() -> mainThreadExecutor.getTaskCount() == 0);
|
report.put("relight_count", lightFutures.size());
|
||||||
//worldgenExecutor.
|
final long relightStart = System.currentTimeMillis();
|
||||||
// save all pending chunks / potentially only one max - ours
|
|
||||||
// hopefully that would flush all chunk writes, including current chink
|
this.waitFor(lightFutures);
|
||||||
while ((runnable = field_19343.poll()) != null)
|
|
||||||
{
|
report.put("relight_time", (int) (System.currentTimeMillis() - relightStart));
|
||||||
runnable.run();
|
|
||||||
}
|
|
||||||
return report;
|
return report;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
accessWidener v1 named
|
||||||
|
|
||||||
|
accessible class net/minecraft/server/world/ThreadedAnvilChunkStorage$TicketManager
|
||||||
|
accessible class net/minecraft/server/world/ServerLightingProvider$Stage
|
||||||
@@ -33,6 +33,13 @@
|
|||||||
"ThreadedAnvilChunkStorage_scarpetChunkCreationMixin",
|
"ThreadedAnvilChunkStorage_scarpetChunkCreationMixin",
|
||||||
"ChunkHolder_scarpetChunkCreationMixin",
|
"ChunkHolder_scarpetChunkCreationMixin",
|
||||||
"ServerLightingProvider_scarpetMixin",
|
"ServerLightingProvider_scarpetMixin",
|
||||||
|
"LevelPropagatorInterface",
|
||||||
|
"ChunkTicketManager_scarpetChunkCreationMixin",
|
||||||
|
"LightingProvider_scarpetChunkCreationMixin",
|
||||||
|
"ChunkLightProvider_scarpetChunkCreationMixin",
|
||||||
|
"LightStorage_scarpetChunkCreationMixin",
|
||||||
|
"BlockLightStorage_scarpetChunkCreationMixin",
|
||||||
|
"SkyLightStorage_scarpetChunkCreationMixin",
|
||||||
|
|
||||||
"ChunkTicketManager_spawnChunksMixin",
|
"ChunkTicketManager_spawnChunksMixin",
|
||||||
"ServerWorld_spawnChunksMixin",
|
"ServerWorld_spawnChunksMixin",
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
"mixins": [
|
"mixins": [
|
||||||
"carpet.mixins.json"
|
"carpet.mixins.json"
|
||||||
],
|
],
|
||||||
|
"accesswidener": "carpet.accesswidener",
|
||||||
"depends": {
|
"depends": {
|
||||||
"minecraft": "1.15.x",
|
"minecraft": "1.15.x",
|
||||||
"fabricloader": ">=0.7.1"
|
"fabricloader": ">=0.7.1"
|
||||||
|
|||||||
Reference in new issue
Block a user