9
0
mirror of https://github.com/Winds-Studio/Leaf.git synced 2025-12-19 15:09:25 +00:00
Files
Leaf/patches/server/0138-Asynchronous-locator.patch
2024-11-29 17:55:29 -05:00

466 lines
23 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: HaHaWTH <102713261+HaHaWTH@users.noreply.github.com>
Date: Wed, 23 Oct 2024 23:54:00 +0800
Subject: [PATCH] Asynchronous locator
Original license: MIT
Original project: https://github.com/thebrightspark/AsyncLocator
diff --git a/src/main/java/ca/spottedleaf/moonrise/common/util/TickThread.java b/src/main/java/ca/spottedleaf/moonrise/common/util/TickThread.java
index 11b7f15755dde766140c29bedca456c80d53293f..749d00449ac3f3c79bfc73a5517ea3a07675e447 100644
--- a/src/main/java/ca/spottedleaf/moonrise/common/util/TickThread.java
+++ b/src/main/java/ca/spottedleaf/moonrise/common/util/TickThread.java
@@ -80,7 +80,7 @@ public class TickThread extends Thread {
this(run, name, ID_GENERATOR.incrementAndGet());
}
- private TickThread(final Runnable run, final String name, final int id) {
+ protected TickThread(final Runnable run, final String name, final int id) { // Leaf - private -> protected
super(run, name);
this.id = id;
}
diff --git a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java
index 6676be8304e9415099ed423d3315180cafebd928..30b56382e9574004e344c1c8289d7dcbb177386b 100644
--- a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java
+++ b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java
@@ -48,6 +48,12 @@ class PaperEventManager {
return;
}
// Leaf end - Multithreaded tracker
+ // Leaf start - Async locator
+ if (org.dreeam.leaf.config.modules.async.AsyncLocator.enabled) {
+ net.minecraft.server.MinecraftServer.getServer().scheduleOnMain(event::callEvent);
+ return;
+ }
+ // Leaf end - Async locator
throw new IllegalStateException(event.getEventName() + " may only be triggered synchronously.");
}
// Leaves start - skip photographer
diff --git a/src/main/java/net/minecraft/server/commands/LocateCommand.java b/src/main/java/net/minecraft/server/commands/LocateCommand.java
index 39f5deea47d8f573c3cfec5df431216ee806c32c..51994f272737f8754aac41dc0c55f43f45617519 100644
--- a/src/main/java/net/minecraft/server/commands/LocateCommand.java
+++ b/src/main/java/net/minecraft/server/commands/LocateCommand.java
@@ -106,6 +106,37 @@ public class LocateCommand {
BlockPos blockPos = BlockPos.containing(source.getPosition());
ServerLevel serverLevel = source.getLevel();
Stopwatch stopwatch = Stopwatch.createStarted(Util.TICKER);
+ // Leaf start - Async locator
+ if (org.dreeam.leaf.config.modules.async.AsyncLocator.enabled) {
+ net.minecraft.commands.CommandSource locatorSource = source.source;
+ if (locatorSource instanceof net.minecraft.server.level.ServerPlayer ||
+ locatorSource instanceof net.minecraft.server.MinecraftServer) {
+ BlockPos originPos = BlockPos.containing(source.getPosition());
+ org.dreeam.leaf.async.locate.AsyncLocator.locate(source.getLevel(), holderSet, originPos, 100, false)
+ .thenOnServerThread(pair -> {
+ stopwatch.stop();
+ if (pair != null) {
+ showLocateResult(
+ source,
+ predicate,
+ originPos,
+ pair,
+ "commands.locate.structure.success",
+ false,
+ stopwatch.elapsed()
+ );
+ } else {
+ source.sendFailure(
+ Component.literal(
+ ERROR_STRUCTURE_NOT_FOUND.create(predicate.asPrintable()).getMessage()
+ )
+ );
+ }
+ });
+ return 0;
+ }
+ }
+ // Leaf end - Async locator
Pair<BlockPos, Holder<Structure>> pair = serverLevel.getChunkSource()
.getGenerator()
.findNearestMapStructure(serverLevel, holderSet, blockPos, 100, false);
diff --git a/src/main/java/net/minecraft/world/entity/animal/Dolphin.java b/src/main/java/net/minecraft/world/entity/animal/Dolphin.java
index ef0124ceb7cafd58c01c7f0b4b542f38a383ab88..061d020c08b722b92187ba9042ab4084ecd72b06 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Dolphin.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Dolphin.java
@@ -466,6 +466,8 @@ public class Dolphin extends WaterAnimal {
private final Dolphin dolphin;
private boolean stuck;
+ @Nullable
+ private org.dreeam.leaf.async.locate.AsyncLocator.LocateTask<?> asyncLocator$locateTask;
DolphinSwimToTreasureGoal(Dolphin dolphin) {
this.dolphin = dolphin;
@@ -485,6 +487,11 @@ public class Dolphin extends WaterAnimal {
@Override
public boolean canContinueToUse() {
+ // Leaf start - Async locator
+ if (org.dreeam.leaf.config.modules.async.AsyncLocator.enabled && this.asyncLocator$locateTask != null) {
+ return true;
+ }
+ // Leaf end - Async locator
BlockPos blockposition = this.dolphin.getTreasurePos();
return !BlockPos.containing((double) blockposition.getX(), this.dolphin.getY(), (double) blockposition.getZ()).closerToCenterThan(this.dolphin.position(), 4.0D) && !this.stuck && this.dolphin.getAirSupply() >= 100;
@@ -498,6 +505,21 @@ public class Dolphin extends WaterAnimal {
this.stuck = false;
this.dolphin.getNavigation().stop();
BlockPos blockposition = this.dolphin.blockPosition();
+ // Leaf start - Async locator
+ if (org.dreeam.leaf.config.modules.async.AsyncLocator.enabled) {
+ asyncLocator$locateTask = org.dreeam.leaf.async.locate.AsyncLocator.locate(worldserver, StructureTags.DOLPHIN_LOCATED, blockposition, 50, false)
+ .thenOnServerThread(pos -> {
+ asyncLocator$locateTask = null;
+ if (pos != null) {
+ this.dolphin.setTreasurePos(pos);
+ worldserver.broadcastEntityEvent(this.dolphin, (byte) 38);
+ } else {
+ this.stuck = true;
+ }
+ });
+ return;
+ }
+ // Leaf end - Async locator
BlockPos blockposition1 = worldserver.findNearestMapStructure(StructureTags.DOLPHIN_LOCATED, blockposition, 50, false);
if (blockposition1 != null) {
@@ -511,6 +533,12 @@ public class Dolphin extends WaterAnimal {
@Override
public void stop() {
+ // Leaf start - Async locator
+ if (org.dreeam.leaf.config.modules.async.AsyncLocator.enabled && this.asyncLocator$locateTask != null) {
+ this.asyncLocator$locateTask.cancel();
+ this.asyncLocator$locateTask = null;
+ }
+ // Leaf end - Async locator
BlockPos blockposition = this.dolphin.getTreasurePos();
if (BlockPos.containing((double) blockposition.getX(), this.dolphin.getY(), (double) blockposition.getZ()).closerToCenterThan(this.dolphin.position(), 4.0D) || this.stuck) {
@@ -521,6 +549,11 @@ public class Dolphin extends WaterAnimal {
@Override
public void tick() {
+ // Leaf start - Async locator
+ if (org.dreeam.leaf.config.modules.async.AsyncLocator.enabled && this.asyncLocator$locateTask != null) {
+ return;
+ }
+ // Leaf end - Async locator
Level world = this.dolphin.level();
if (this.dolphin.closeToNextPos() || this.dolphin.getNavigation().isDone()) {
diff --git a/src/main/java/net/minecraft/world/entity/projectile/EyeOfEnder.java b/src/main/java/net/minecraft/world/entity/projectile/EyeOfEnder.java
index fca3786d0a3f99a3e61e7a4b2251361276eff9d7..cb4ff1e98418c651ef21f04f3c74cac7065031ae 100644
--- a/src/main/java/net/minecraft/world/entity/projectile/EyeOfEnder.java
+++ b/src/main/java/net/minecraft/world/entity/projectile/EyeOfEnder.java
@@ -27,6 +27,7 @@ public class EyeOfEnder extends Entity implements ItemSupplier {
public double tz;
public int life;
public boolean surviveAfterDeath;
+ public boolean asyncLocator$locateTaskOngoing = false; // Leaf - Async locator
public EyeOfEnder(EntityType<? extends EyeOfEnder> type, Level world) {
super(type, world);
@@ -114,6 +115,11 @@ public class EyeOfEnder extends Entity implements ItemSupplier {
@Override
public void tick() {
super.tick();
+ // Leaf start - Async locator
+ if (org.dreeam.leaf.config.modules.async.AsyncLocator.enabled && this.asyncLocator$locateTaskOngoing) {
+ return;
+ }
+ // Leaf end - Async locator
Vec3 vec3d = this.getDeltaMovement();
double d0 = this.getX() + vec3d.x;
double d1 = this.getY() + vec3d.y;
diff --git a/src/main/java/net/minecraft/world/item/EnderEyeItem.java b/src/main/java/net/minecraft/world/item/EnderEyeItem.java
index d8ce44a180f848f4c9c04967470c4359af979b2f..90abb83a6baa60bbcbedc7d818c3bc9f4317f04f 100644
--- a/src/main/java/net/minecraft/world/item/EnderEyeItem.java
+++ b/src/main/java/net/minecraft/world/item/EnderEyeItem.java
@@ -113,20 +113,54 @@ public class EnderEyeItem extends Item {
user.startUsingItem(hand);
if (world instanceof ServerLevel) {
ServerLevel worldserver = (ServerLevel) world;
- BlockPos blockposition = worldserver.findNearestMapStructure(StructureTags.EYE_OF_ENDER_LOCATED, user.blockPosition(), 100, false);
+ // Leaf start - Async locator
+ BlockPos blockposition;
+ if (org.dreeam.leaf.config.modules.async.AsyncLocator.enabled) {
+ blockposition = BlockPos.ZERO;
+ } else {
+ blockposition = worldserver.findNearestMapStructure(StructureTags.EYE_OF_ENDER_LOCATED, user.blockPosition(), 100, false);
+ }
+ // Leaf end - Async locator
if (blockposition != null) {
EyeOfEnder entityendersignal = new EyeOfEnder(world, user.getX(), user.getY(0.5D), user.getZ());
+ // Leaf start - Async locator
+ final boolean isAsyncLocatorEnabled = org.dreeam.leaf.config.modules.async.AsyncLocator.enabled;
+ if (isAsyncLocatorEnabled) {
+ entityendersignal.asyncLocator$locateTaskOngoing = true;
+ org.dreeam.leaf.async.locate.AsyncLocator.locate(
+ worldserver,
+ StructureTags.EYE_OF_ENDER_LOCATED,
+ user.blockPosition(),
+ 100,
+ false
+ ).thenOnServerThread(pos -> {
+ entityendersignal.asyncLocator$locateTaskOngoing = false;
+ if (pos != null) {
+ entityendersignal.signalTo(pos);
+ CriteriaTriggers.USED_ENDER_EYE.trigger((ServerPlayer) user, pos);
+ user.awardStat(Stats.ITEM_USED.get(this));
+ } else {
+ // Set the entity's life to long enough that it dies
+ entityendersignal.life = Integer.MAX_VALUE - 100;
+ }
+ });
+ }
+ // Leaf end - Async locator
entityendersignal.setItem(itemstack);
- entityendersignal.signalTo(blockposition);
+ // Leaf start - Async locator
+ if (!isAsyncLocatorEnabled) {
+ entityendersignal.signalTo(blockposition);
+ }
+ // Leaf end - Async locator
world.gameEvent((Holder) GameEvent.PROJECTILE_SHOOT, entityendersignal.position(), GameEvent.Context.of((Entity) user));
// CraftBukkit start
if (!world.addFreshEntity(entityendersignal)) {
return new InteractionResultHolder(InteractionResult.FAIL, itemstack);
}
// CraftBukkit end
- if (user instanceof ServerPlayer) {
+ if (!isAsyncLocatorEnabled && user instanceof ServerPlayer) { // Leaf - Async locator
ServerPlayer entityplayer = (ServerPlayer) user;
CriteriaTriggers.USED_ENDER_EYE.trigger(entityplayer, blockposition);
@@ -136,7 +170,11 @@ public class EnderEyeItem extends Item {
world.playSound((Player) null, user.getX(), user.getY(), user.getZ(), SoundEvents.ENDER_EYE_LAUNCH, SoundSource.NEUTRAL, 1.0F, f);
itemstack.consume(1, user);
- user.awardStat(Stats.ITEM_USED.get(this));
+ // Leaf start - Async locator
+ if (!isAsyncLocatorEnabled) {
+ user.awardStat(Stats.ITEM_USED.get(this));
+ }
+ // Leaf end - Async locator
user.swing(hand, true);
return InteractionResultHolder.success(itemstack);
}
diff --git a/src/main/java/org/dreeam/leaf/async/locate/AsyncLocator.java b/src/main/java/org/dreeam/leaf/async/locate/AsyncLocator.java
new file mode 100644
index 0000000000000000000000000000000000000000..fcede5af1f1352a8c8c089993040838d1e7c3042
--- /dev/null
+++ b/src/main/java/org/dreeam/leaf/async/locate/AsyncLocator.java
@@ -0,0 +1,164 @@
+package org.dreeam.leaf.async.locate;
+
+import ca.spottedleaf.moonrise.common.util.TickThread;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import com.mojang.datafixers.util.Pair;
+import net.minecraft.core.BlockPos;
+import net.minecraft.core.Holder;
+import net.minecraft.core.HolderSet;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.tags.TagKey;
+import net.minecraft.world.level.chunk.ChunkGenerator;
+import net.minecraft.world.level.levelgen.structure.Structure;
+
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+
+// Original project: https://github.com/thebrightspark/AsyncLocator
+public class AsyncLocator {
+ private static final ExecutorService LOCATING_EXECUTOR_SERVICE;
+
+ private AsyncLocator() {}
+
+ public static class AsyncLocatorThread extends TickThread {
+ private static final AtomicInteger THREAD_COUNTER = new AtomicInteger(0);
+ public AsyncLocatorThread(Runnable run, String name) {
+ super(run, name, THREAD_COUNTER.incrementAndGet());
+ }
+
+ @Override
+ public void run() {
+ super.run();
+ }
+ }
+
+ static {
+ int threads = org.dreeam.leaf.config.modules.async.AsyncLocator.asyncLocatorThreads;
+ LOCATING_EXECUTOR_SERVICE = new ThreadPoolExecutor(
+ 1,
+ threads,
+ org.dreeam.leaf.config.modules.async.AsyncLocator.asyncLocatorKeepalive,
+ TimeUnit.SECONDS,
+ new LinkedBlockingQueue<>(),
+ new ThreadFactoryBuilder()
+ .setThreadFactory(
+ r -> new AsyncLocatorThread(r, "Leaf Async Locator Thread") {
+ @Override
+ public void run() {
+ r.run();
+ }
+ }
+ )
+ .setNameFormat("Leaf Async Locator Thread - %d")
+ .setPriority(Thread.NORM_PRIORITY - 2)
+ .build()
+ );
+ }
+
+ public static void shutdownExecutorService() {
+ if (LOCATING_EXECUTOR_SERVICE != null) {
+ LOCATING_EXECUTOR_SERVICE.shutdown();
+ }
+ }
+
+ /**
+ * Queues a task to locate a feature using {@link ServerLevel#findNearestMapStructure(TagKey, BlockPos, int, boolean)}
+ * and returns a {@link LocateTask} with the futures for it.
+ */
+ public static LocateTask<BlockPos> locate(
+ ServerLevel level,
+ TagKey<Structure> structureTag,
+ BlockPos pos,
+ int searchRadius,
+ boolean skipKnownStructures
+ ) {
+ CompletableFuture<BlockPos> completableFuture = new CompletableFuture<>();
+ Future<?> future = LOCATING_EXECUTOR_SERVICE.submit(
+ () -> doLocateLevel(completableFuture, level, structureTag, pos, searchRadius, skipKnownStructures)
+ );
+ return new LocateTask<>(level.getServer(), completableFuture, future);
+ }
+
+ /**
+ * Queues a task to locate a feature using
+ * {@link ChunkGenerator#findNearestMapStructure(ServerLevel, HolderSet, BlockPos, int, boolean)} and returns a
+ * {@link LocateTask} with the futures for it.
+ */
+ public static LocateTask<Pair<BlockPos, Holder<Structure>>> locate(
+ ServerLevel level,
+ HolderSet<Structure> structureSet,
+ BlockPos pos,
+ int searchRadius,
+ boolean skipKnownStructures
+ ) {
+ CompletableFuture<Pair<BlockPos, Holder<Structure>>> completableFuture = new CompletableFuture<>();
+ Future<?> future = LOCATING_EXECUTOR_SERVICE.submit(
+ () -> doLocateChunkGenerator(completableFuture, level, structureSet, pos, searchRadius, skipKnownStructures)
+ );
+ return new LocateTask<>(level.getServer(), completableFuture, future);
+ }
+
+ private static void doLocateLevel(
+ CompletableFuture<BlockPos> completableFuture,
+ ServerLevel level,
+ TagKey<Structure> structureTag,
+ BlockPos pos,
+ int searchRadius,
+ boolean skipExistingChunks
+ ) {
+ BlockPos foundPos = level.findNearestMapStructure(structureTag, pos, searchRadius, skipExistingChunks);
+ completableFuture.complete(foundPos);
+ }
+
+ private static void doLocateChunkGenerator(
+ CompletableFuture<Pair<BlockPos, Holder<Structure>>> completableFuture,
+ ServerLevel level,
+ HolderSet<Structure> structureSet,
+ BlockPos pos,
+ int searchRadius,
+ boolean skipExistingChunks
+ ) {
+ Pair<BlockPos, Holder<Structure>> foundPair = level.getChunkSource().getGenerator()
+ .findNearestMapStructure(level, structureSet, pos, searchRadius, skipExistingChunks);
+ completableFuture.complete(foundPair);
+ }
+
+ /**
+ * Holder of the futures for an async locate task as well as providing some helper functions.
+ * The completableFuture will be completed once the call to
+ * {@link ServerLevel#findNearestMapStructure(TagKey, BlockPos, int, boolean)} has completed, and will hold the
+ * result of it.
+ * The taskFuture is the future for the {@link Runnable} itself in the executor service.
+ */
+ public record LocateTask<T>(MinecraftServer server, CompletableFuture<T> completableFuture, Future<?> taskFuture) {
+ /**
+ * Helper function that calls {@link CompletableFuture#thenAccept(Consumer)} with the given action.
+ * Bear in mind that the action will be executed from the task's thread. If you intend to change any game data,
+ * it's strongly advised you use {@link #thenOnServerThread(Consumer)} instead so that it's queued and executed
+ * on the main server thread instead.
+ */
+ public LocateTask<T> then(Consumer<T> action) {
+ completableFuture.thenAccept(action);
+ return this;
+ }
+
+ /**
+ * Helper function that calls {@link CompletableFuture#thenAccept(Consumer)} with the given action on the server
+ * thread.
+ */
+ public LocateTask<T> thenOnServerThread(Consumer<T> action) {
+ completableFuture.thenAccept(pos -> server.scheduleOnMain(() -> action.accept(pos)));
+ return this;
+ }
+
+ /**
+ * Helper function that cancels both completableFuture and taskFuture.
+ */
+ public void cancel() {
+ taskFuture.cancel(true);
+ completableFuture.cancel(false);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/dreeam/leaf/config/modules/async/AsyncLocator.java b/src/main/java/org/dreeam/leaf/config/modules/async/AsyncLocator.java
new file mode 100644
index 0000000000000000000000000000000000000000..0d741e4d6e7a39cf3da0f40816daa0405122a90a
--- /dev/null
+++ b/src/main/java/org/dreeam/leaf/config/modules/async/AsyncLocator.java
@@ -0,0 +1,37 @@
+package org.dreeam.leaf.config.modules.async;
+
+import org.dreeam.leaf.config.ConfigModules;
+import org.dreeam.leaf.config.EnumConfigCategory;
+import org.dreeam.leaf.config.LeafConfig;
+
+public class AsyncLocator extends ConfigModules {
+
+ public String getBasePath() {
+ return EnumConfigCategory.ASYNC.getBaseKeyName() + ".async-locator";
+ }
+
+ public static boolean enabled = false;
+ public static int asyncLocatorThreads = 0;
+ public static int asyncLocatorKeepalive = 60;
+
+ @Override
+ public void onLoaded() {
+ config.addCommentRegionBased(getBasePath(), """
+ Whether or not asynchronous locator should be enabled.
+ This offloads structure locating to other threads.
+ Only for locate command, dolphin treasure finding and eye of ender currently.""",
+ """
+ 是否启用异步结构搜索.
+ 目前可用于 /locate 指令, 海豚寻宝和末影之眼.""");
+ enabled = config.getBoolean(getBasePath() + ".enabled", enabled);
+ asyncLocatorThreads = config.getInt(getBasePath() + ".threads", asyncLocatorThreads);
+ asyncLocatorKeepalive = config.getInt(getBasePath() + ".keepalive", asyncLocatorKeepalive);
+
+ if (asyncLocatorThreads <= 0)
+ asyncLocatorThreads = 1;
+ if (!enabled)
+ asyncLocatorThreads = 0;
+ else
+ LeafConfig.LOGGER.info("Using {} threads for Async Locator", asyncLocatorThreads);
+ }
+}