Skip to content

Commit

Permalink
perf: do not depend on server thread to complete EMPTY state
Browse files Browse the repository at this point in the history
  • Loading branch information
ishland committed Apr 9, 2023
1 parent 287ca5d commit 036378a
Show file tree
Hide file tree
Showing 10 changed files with 102 additions and 25 deletions.
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ configure (allprojects - project(":tests")) {
implementation "com.electronwill.night-config:toml:${night_config_version}"
implementation "org.threadly:threadly:${threadly_version}"
implementation "net.objecthunter:exp4j:${exp4j_version}"
implementation "com.github.LlamaLad7:MixinExtras:${mixinextras_version}"

annotationProcessor "com.github.LlamaLad7:MixinExtras:${mixinextras_version}"
}
}

Expand Down Expand Up @@ -149,6 +152,7 @@ dependencies {
include implementation("com.electronwill.night-config:core:${night_config_version}")
include implementation("org.threadly:threadly:${threadly_version}")
include implementation("net.objecthunter:exp4j:${exp4j_version}")
include implementation("com.github.LlamaLad7:MixinExtras:${mixinextras_version}")

// PSA: Some older mods, compiled on Loom 0.2.1, might have outdated Maven POMs.
// You may need to force-disable transitiveness on them.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.ishland.c2me.base.common;

import com.llamalad7.mixinextras.MixinExtrasBootstrap;
import org.objectweb.asm.tree.ClassNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -19,6 +20,7 @@ public class ModuleMixinPlugin implements IMixinConfigPlugin {

@Override
public void onLoad(String mixinPackage) {
MixinExtrasBootstrap.init();
LOGGER.info("Initializing {}", mixinPackage);
final String[] split = mixinPackage.split("\\.");
final String targetClass = String.join(".", Arrays.copyOf(split, split.length - 1)) + ".ModuleEntryPoint";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,7 @@ public interface ProtoChunkExtension {

boolean getNeedBlending();

void setInitialMainThreadComputeFuture(CompletableFuture<Void> future);
CompletableFuture<Void> getInitialMainThreadComputeFuture();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.ishland.c2me.threading.chunkio.mixin;

import com.ishland.c2me.threading.chunkio.common.ProtoChunkExtension;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.block.BlockState;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.ChunkRegion;
import net.minecraft.world.StructureWorldAccess;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.ProtoChunk;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;

import java.util.concurrent.CompletableFuture;

@Mixin(ChunkRegion.class)
public abstract class MixinChunkRegion implements StructureWorldAccess {

@WrapOperation(method = "setBlockState", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/world/ServerWorld;onBlockChanged(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Lnet/minecraft/block/BlockState;)V"))
private void waitForFutureBeforeNotifyChanges(ServerWorld instance, BlockPos pos, BlockState oldBlock, BlockState newBlock, Operation<Void> operation) {
final Chunk chunk = this.getChunk(pos);
if (chunk instanceof ProtoChunk protoChunk) {
final CompletableFuture<Void> future = ((ProtoChunkExtension) protoChunk).getInitialMainThreadComputeFuture();
if (future != null && !future.isDone()) {
future.thenRun(() -> operation.call(instance, pos, oldBlock, newBlock));
return;
}
}
operation.call(instance, pos, oldBlock, newBlock);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ public class MixinProtoChunk implements ProtoChunkExtension {
@Unique
private CompletableFuture<Void> blendingComputeFuture = CompletableFuture.completedFuture(null);

@Unique
private CompletableFuture<Void> initialMainThreadComputeFuture = CompletableFuture.completedFuture(null);

@Unique
private boolean needBlending = false;

Expand Down Expand Up @@ -66,4 +69,15 @@ public boolean getNeedBlending() {
}
return needBlending;
}

@Override
public void setInitialMainThreadComputeFuture(CompletableFuture<Void> future) {
this.initialMainThreadComputeFuture = future;
}

@Override
public CompletableFuture<Void> getInitialMainThreadComputeFuture() {
return this.initialMainThreadComputeFuture;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import com.ishland.c2me.threading.chunkio.common.ISerializingRegionBasedStorage;
import com.ishland.c2me.threading.chunkio.common.ProtoChunkExtension;
import com.ishland.c2me.threading.chunkio.common.TaskCancellationException;
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
import com.mojang.datafixers.DataFixer;
import com.mojang.datafixers.util.Either;
import it.unimi.dsi.fastutil.longs.Long2ByteMap;
Expand Down Expand Up @@ -183,7 +184,7 @@ private CompletableFuture<Either<Chunk, ChunkHolder.Unloaded>> loadChunk(ChunkPo
return null; // unreachable
}
})
.thenCombine(poiData, (protoChunk, tag) -> protoChunk)
// .thenCombine(poiData, (protoChunk, tag) -> protoChunk)
// .thenCombine(blendingInfos, (protoChunk, bitSet) -> {
// if (protoChunk != null) ((ProtoChunkExtension) protoChunk).setBlendingInfo(pos, bitSet);
// return protoChunk;
Expand All @@ -199,24 +200,22 @@ private CompletableFuture<Either<Chunk, ChunkHolder.Unloaded>> loadChunk(ChunkPo
);
}

try {
((ISerializingRegionBasedStorage) this.pointOfInterestStorage).update(pos, poiData.join().orElse(null));
} catch (Throwable t) {
if (Config.recoverFromErrors) {
LOGGER.error("Couldn't load poi data for chunk {}, poi data will be lost!", pos, t);
} else {
SneakyThrow.sneaky(t);
((ProtoChunkExtension) protoChunk).setInitialMainThreadComputeFuture(poiData.thenAcceptAsync(poiDataNbt -> {
try {
((ISerializingRegionBasedStorage) this.pointOfInterestStorage).update(pos, poiDataNbt.orElse(null));
} catch (Throwable t) {
if (Config.recoverFromErrors) {
LOGGER.error("Couldn't load poi data for chunk {}, poi data will be lost!", pos, t);
} else {
SneakyThrow.sneaky(t);
}
}
}
ChunkIoMainThreadTaskUtils.drainQueue(mainThreadQueue);
if (protoChunk != null) {
this.mark(pos, protoChunk.getStatus().getChunkType());
return Either.left(protoChunk);
} else {
LOGGER.error("Why is protoChunk null? Trying to recover from this case...");
return Either.left(this.getProtoChunk(pos));
}
}, this.mainThreadExecutor);
ChunkIoMainThreadTaskUtils.drainQueue(mainThreadQueue);
}, this.mainThreadExecutor));

this.mark(pos, protoChunk.getStatus().getChunkType());
return Either.left(protoChunk);
}, GlobalExecutors.invokingExecutor);
future.exceptionally(throwable -> {
LOGGER.error("Couldn't load chunk {}", pos, throwable);
return null;
Expand Down Expand Up @@ -287,6 +286,26 @@ private CompletableFuture<Optional<NbtCompound>> getUpdatedChunkNbt(ChunkPos chu
});
}

@ModifyReturnValue(method = "getChunk", at = @At("RETURN"))
private CompletableFuture<Either<Chunk, ChunkHolder.Unloaded>> postGetChunk(CompletableFuture<Either<Chunk, ChunkHolder.Unloaded>> originalReturn, ChunkHolder holder, ChunkStatus requiredStatus) {
if (requiredStatus == ChunkStatus.FULL.getPrevious()) {
// wait for initial main thread tasks before proceeding to finish full chunk
return originalReturn.thenCompose(either -> {
if (either.left().isPresent()) {
final Chunk chunk = either.left().get();
if (chunk instanceof ProtoChunk protoChunk) {
final CompletableFuture<Void> future = ((ProtoChunkExtension) protoChunk).getInitialMainThreadComputeFuture();
if (future != null) {
return future.thenApply(v -> either);
}
}
}
return CompletableFuture.completedFuture(either);
});
}
return originalReturn;
}

private ConcurrentLinkedQueue<CompletableFuture<Void>> saveFutures = new ConcurrentLinkedQueue<>();

@Dynamic
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"plugin": "com.ishland.c2me.base.common.ModuleMixinPlugin",
"mixins": [
"MixinBlender",
"MixinChunkRegion",
"MixinChunkSerializer",
"MixinProtoChunk",
"MixinSerializingRegionBasedStorage",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.ishland.c2me.threading.worldgen.mixin.cancellation;

import com.llamalad7.mixinextras.injector.ModifyReturnValue;
import com.mojang.datafixers.util.Either;
import net.minecraft.server.world.ChunkHolder;
import net.minecraft.server.world.ThreadedAnvilChunkStorage;
Expand All @@ -11,9 +12,7 @@
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.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
Expand All @@ -37,9 +36,9 @@ private int redirectRemoveLightTicketDistance(ChunkStatus status) {
return status == ChunkStatus.LIGHT ? ChunkStatus.getDistanceFromFull(ChunkStatus.STRUCTURE_STARTS) - 2 : ChunkStatus.getDistanceFromFull(status);
}

@Inject(method = "getChunk", at = @At("RETURN"), cancellable = true)
private void injectCancellationHook(ChunkHolder holder, ChunkStatus requiredStatus, CallbackInfoReturnable<CompletableFuture<Either<Chunk, ChunkHolder.Unloaded>>> cir) {
cir.setReturnValue(cir.getReturnValue().thenCompose(either -> {
@ModifyReturnValue(method = "getChunk", at = @At("RETURN"))
private CompletableFuture<Either<Chunk, ChunkHolder.Unloaded>> injectCancellationHook(CompletableFuture<Either<Chunk, ChunkHolder.Unloaded>> originalReturn, ChunkHolder holder, ChunkStatus requiredStatus) {
return originalReturn.thenCompose(either -> {
if (either.right().isPresent()) {
return CompletableFuture.supplyAsync(() -> {
if (ChunkHolder.getTargetStatusForLevel(holder.getLevel()).isAtLeast(requiredStatus)) {
Expand All @@ -52,7 +51,7 @@ private void injectCancellationHook(ChunkHolder holder, ChunkStatus requiredStat
} else {
return CompletableFuture.completedFuture(either);
}
}));
});
}

}
1 change: 1 addition & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ async_util_version=0.1.0
night_config_version=3.6.5
threadly_version=7.0
exp4j_version=0.4.8
mixinextras_version=0.1.1
2 changes: 1 addition & 1 deletion src/main/resources/fabric.mod.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
]
},
"depends": {
"fabricloader": ">=0.14.0",
"fabricloader": ">=0.14.11",
"java": ">=17",
"minecraft": ">=1.19.4"
},
Expand Down

0 comments on commit 036378a

Please sign in to comment.