Skip to content
This repository has been archived by the owner on Nov 14, 2024. It is now read-only.

Commit

Permalink
Merge remote-tracking branch 'upstream/ver/1.20.1' into ver/1.20.1
Browse files Browse the repository at this point in the history
  • Loading branch information
coco875 committed Aug 12, 2024
2 parents 8d4e580 + cd07902 commit 57735cd
Show file tree
Hide file tree
Showing 9 changed files with 65 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import it.unimi.dsi.fastutil.longs.LongSet;
import net.minecraft.entity.Entity;
import net.minecraft.server.world.ChunkHolder;
import net.minecraft.server.world.ChunkTicketManager;
import net.minecraft.server.world.ServerChunkManager;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.world.chunk.WorldChunk;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
Expand All @@ -20,14 +22,13 @@ public class MixinServerChunkManager {

@Shadow @Final private ChunkTicketManager ticketManager;

// @Dynamic
// @Redirect(method = "tickChunks", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/world/ChunkHolder;getTickingFuture()Ljava/util/concurrent/CompletableFuture;")) // TODO lambda expression in tickChunks after "broadcast"
// private static CompletableFuture<Either<WorldChunk, ChunkHolder.Unloaded>> redirectTickingFuture(ChunkHolder chunkHolder) {
// return chunkHolder.getAccessibleFuture();
// }
@Redirect(method = "tickChunks", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/world/ChunkHolder;getWorldChunk()Lnet/minecraft/world/chunk/WorldChunk;"))
private WorldChunk includeAccessibleChunks(ChunkHolder instance) {
return instance.getAccessibleFuture().getNow(ChunkHolder.UNLOADED_WORLD_CHUNK).left().orElse(null);
}

@Redirect(method = "tickChunks()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/world/ServerWorld;iterateEntities()Ljava/lang/Iterable;"))
private Iterable<Entity> redirectIterateEntities(ServerWorld serverWorld) {
@WrapOperation(method = "tickChunks", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/world/ServerWorld;iterateEntities()Ljava/lang/Iterable;"))
private Iterable<Entity> redirectIterateEntities(ServerWorld serverWorld, Operation<Iterable<Entity>> op) {
final LongSet noTickOnlyChunks = ((IChunkTicketManager) this.ticketManager).getNoTickOnlyChunks();
if (noTickOnlyChunks == null) return serverWorld.iterateEntities();
return new FilteringIterable<>(serverWorld.iterateEntities(), entity -> !noTickOnlyChunks.contains(entity.getChunkPos().toLong()));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.ishland.c2me.opts.scheduling.common;

import net.minecraft.server.world.ServerWorld;

public interface ServerMidTickTask {

void executeTasksMidTick();
void executeTasksMidTick(ServerWorld world);

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,10 @@ public abstract class MixinMinecraftServer implements ServerMidTickTask {
@Unique
private long midTickChunkTasksLastRun = System.nanoTime();

public void executeTasksMidTick() {
public void executeTasksMidTick(ServerWorld world) {
if (this.serverThread != Thread.currentThread()) return;
if (System.nanoTime() - midTickChunkTasksLastRun < Config.midTickChunkTasksInterval) return;
for (ServerWorld world : this.getWorlds()) {
((ThreadExecutor<Runnable>) ((IServerChunkManager) world.getChunkManager()).getMainThreadExecutor()).runTask();
}
((ThreadExecutor<Runnable>) ((IServerChunkManager) world.getChunkManager()).getMainThreadExecutor()).runTask();
midTickChunkTasksLastRun = System.nanoTime();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class MixinServerChunkManager {
@Dynamic
@Inject(method = "tickChunks", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/world/ServerWorld;tickChunk(Lnet/minecraft/world/chunk/WorldChunk;I)V"))
private void onPostTickChunk(CallbackInfo ci) {
((ServerMidTickTask) this.world.getServer()).executeTasksMidTick();
((ServerMidTickTask) this.world.getServer()).executeTasksMidTick(this.world);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class MixinServerWorld {

@Inject(method = {"tickBlock", "tickFluid"}, at = @At("RETURN"), require = 2)
private void onPostTickBlockAndFluid(CallbackInfo info) {
((ServerMidTickTask) this.server).executeTasksMidTick();
((ServerMidTickTask) this.server).executeTasksMidTick((ServerWorld) (Object) this);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.ishland.c2me.opts.scheduling.common.ServerMidTickTask;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Final;
Expand All @@ -22,7 +23,7 @@ public abstract class MixinWorld {
private void onPostTickEntity(CallbackInfo ci) {
final MinecraftServer server = this.getServer();
if (!this.isClient && server != null) {
((ServerMidTickTask) server).executeTasksMidTick();
((ServerMidTickTask) server).executeTasksMidTick((ServerWorld) (Object) this);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,13 @@ public class C2MEStorageThread extends Thread {
if (Thread.currentThread() == this) {
command.run();
} else {
final boolean empty = pendingTasks.isEmpty();
pendingTasks.add(command);
LockSupport.unpark(this);
if (empty) this.wakeUp();
}
};
private final ObjectArraySet<CompletableFuture<Void>> writeFutures = new ObjectArraySet<>();
private final Object sync = new Object();

public C2MEStorageThread(Path directory, boolean dsync, String name) {
this.storage = new RegionBasedStorage(directory, dsync);
Expand All @@ -71,12 +73,10 @@ public C2MEStorageThread(Path directory, boolean dsync, String name) {

@Override
public void run() {
main_loop:
while (true) {
boolean hasWork = false;
hasWork = handleTasks() || hasWork;
hasWork = handlePendingWrites() || hasWork;
hasWork = handlePendingReads() || hasWork;
hasWork = writeBacklog() || hasWork;
hasWork |= pollTasks();

runWriteFutureGC();

Expand All @@ -91,13 +91,46 @@ public void run() {
this.closeFuture.complete(null);
break;
} else {
LockSupport.parkNanos("Waiting for tasks", 10_000_000);
// attempt to spin-wait before sleeping
if (!pollTasks()) {
Thread.interrupted(); // clear interrupt flag
for (int i = 0; i < 5000; i ++) {
if (pollTasks()) continue main_loop;
LockSupport.parkNanos("Spin-waiting for tasks", 10_000); // 100us
}
}
synchronized (sync) {
if (this.hasPendingTasks() || this.closing.get()) continue main_loop;
try {
sync.wait();
} catch (InterruptedException ignored) {
}
}
}
}
}
LOGGER.info("Storage thread {} stopped", this);
}

private boolean pollTasks() {
boolean hasWork = false;
hasWork = handleTasks() || hasWork;
hasWork = handlePendingWrites() || hasWork;
hasWork = handlePendingReads() || hasWork;
hasWork = writeBacklog() || hasWork;
return hasWork;
}

private boolean hasPendingTasks() {
return !this.pendingTasks.isEmpty() || !this.pendingReadRequests.isEmpty() || !this.pendingWriteRequests.isEmpty() || !this.writeBacklog.isEmpty();
}

private void wakeUp() {
synchronized (sync) {
sync.notifyAll();
}
}

/**
* Read chunk data from storage
* @param pos target pos
Expand All @@ -110,8 +143,9 @@ public CompletableFuture<NbtCompound> getChunkData(long pos, NbtScanner scanner)
future.completeExceptionally(new CancellationException());
return future.thenApply(Function.identity());
}
final boolean empty = this.pendingReadRequests.isEmpty();
this.pendingReadRequests.add(new ReadRequest(pos, future, scanner));
LockSupport.unpark(this);
if (empty) this.wakeUp();
future.thenApply(Function.identity()).orTimeout(60, TimeUnit.SECONDS).exceptionally(throwable -> {
if (throwable instanceof TimeoutException) {
LOGGER.warn("Chunk read at pos {} took too long (> 1min)", new ChunkPos(pos).toLong());
Expand All @@ -123,13 +157,15 @@ public CompletableFuture<NbtCompound> getChunkData(long pos, NbtScanner scanner)
}

public void setChunkData(long pos, @Nullable NbtCompound nbt) {
final boolean empty = this.pendingWriteRequests.isEmpty();
this.pendingWriteRequests.add(new WriteRequest(pos, nbt != null ? Either.left(nbt) : null));
LockSupport.unpark(this);
if (empty) this.wakeUp();
}

public void setChunkData(long pos, @Nullable byte[] data) {
final boolean empty = this.pendingWriteRequests.isEmpty();
this.pendingWriteRequests.add(new WriteRequest(pos, data != null ? Either.right(data) : null));
LockSupport.unpark(this);
if (empty) this.wakeUp();
}

public CompletableFuture<Void> flush(boolean sync) {
Expand All @@ -156,7 +192,7 @@ private void flush0(boolean sync) {

public CompletableFuture<Void> close() {
this.closing.set(true);
LockSupport.unpark(this);
this.wakeUp();
return this.closeFuture.thenApply(Function.identity());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public CompletableFuture<Void> scanChunk(ChunkPos pos, NbtScanner scanner) {

@Override
public void close() {
this.backend.close().join();
this.backend.close();
}

@Override
Expand Down
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

0 comments on commit 57735cd

Please sign in to comment.