Files
fabric-carpet/src/main/java/carpet/mixins/ThreadedAnvilChunkStorage_scarpetChunkCreationMixin.java
T
2020-02-29 20:29:45 -04:00

226 lines
9.6 KiB
Java

package carpet.mixins;
import carpet.CarpetSettings;
import carpet.fakes.ChunkHolderInterface;
import carpet.fakes.ThreadedAnvilChunkStorageInterface;
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.MessageListener;
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 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.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
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 LongSet loadedChunks;
@Shadow @Final private LongSet unloadedChunks;
@Shadow @Final private Long2ObjectLinkedOpenHashMap<ChunkHolder> currentChunkHolders;
@Shadow @Final private Long2ObjectLinkedOpenHashMap<ChunkHolder> field_18807;
@Shadow private boolean chunkHolderListDirty;
@Shadow protected abstract void method_20458(long l, ChunkHolder chunkHolder);
@Shadow @Final private Queue<Runnable> field_19343;
@Shadow @Final private ServerLightingProvider serverLightingProvider;
@Shadow @Final private ChunkTaskPrioritySystem chunkTaskPrioritySystem;
@Shadow protected abstract CompletableFuture<Either<Chunk, ChunkHolder.Unloaded>> convertToFullChunk(ChunkHolder chunkHolder);
@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
//method_19534(Lnet/minecraft/server/world/ChunkHolder;Lnet/minecraft/world/chunk/Chunk;)Ljava/util/concurrent/CompletableFuture;
@Inject(method = "method_19534", at = @At(
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<CompletableFuture> cir)
{
if (CHUNK_GENERATED.isNeeded())
world.getServer().execute( () -> CHUNK_GENERATED.onChunkGenerated(world, chunk) );
}
@Override
public void regenerateChunkRegion(List<ChunkPos> requestedChunks)
{
//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();
}
Set<ChunkPos> loadedChunks = new HashSet<>();
for (ChunkPos pos: requestedChunks)
{
Chunk chunk = world.getChunk(pos.x, pos.z, ChunkStatus.FULL, false);
if (chunk != null && chunk.getStatus() != ChunkStatus.EMPTY)
loadedChunks.add(pos);
}
CarpetSettings.LOG.error("Area has "+requestedChunks.size()+" chunks");
CarpetSettings.LOG.error("Area has "+loadedChunks.size()+" loaded chunks");
Map<ChunkPos,ChunkStatus> targetStatus = new HashMap<>();
for (ChunkPos pos: requestedChunks)
{
Chunk chunk = world.getChunk(pos.x, pos.z, ChunkStatus.EMPTY, true);
if (chunk == null || chunk.getStatus() == ChunkStatus.EMPTY)
continue;
//unloaded areas can have unknown skirt chunk area
targetStatus.put(pos, loadedChunks.contains(pos)?chunk.getStatus():ChunkStatus.STRUCTURE_STARTS);
}
CarpetSettings.LOG.error("Area has "+targetStatus.size()+" chunks for removal");
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)
{
//CarpetSettings.LOG.error("current_level: "+chunkHolder.getLevel());
currentLevel = chunkHolder.getLevel();
//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);
}
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);
}
//this.serverLightingProvider.updateChunkStatus(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.chunkHolderListDirty = true;
// not needed right now
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();
}
for (ChunkStatus status : ChunkStatus.createOrderedList())
{
CarpetSettings.LOG.error("Creating layer: " + status.getId() + " for "+targetStatus.size()+" chunks");
if (status == ChunkStatus.LIGHT)
{
//break;
serverLightingProvider.setTaskBatchSize(targetStatus.size() + 20);
}
for (ChunkPos chpos : new ArrayList<>(targetStatus.keySet()))
{
if (targetStatus.get(chpos) == status)
targetStatus.remove(chpos);
//if (status==ChunkStatus.LIGHT)
//{
// serverLightingProvider.tick();
//}
//CarpetSettings.LOG.error("Creating layer: " + status.getId());
world.getChunk(chpos.x, chpos.z, status);
if (status == ChunkStatus.FULL)
convertToFullChunk(currentChunkHolders.get(chpos.toLong()));
//CarpetSettings.LOG.error(" .. done");
//if (status==ChunkStatus.LIGHT)
//{
// serverLightingProvider.tick();
//}
}
if (status == ChunkStatus.LIGHT)
{
serverLightingProvider.setTaskBatchSize(5);
}
}
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();
}
}
}