mirror of
https://github.com/Winds-Studio/Leaf.git
synced 2025-12-28 03:19:21 +00:00
Fix build
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?=E3=84=97=E3=84=A0=CB=8B=20=E3=84=91=E3=84=A7=CB=8A?=
|
||||
<tsao-chi@the-lingo.org>
|
||||
Date: Thu, 5 Jan 2023 09:08:17 +0800
|
||||
Subject: [PATCH] Akarin: Save Json list asynchronously
|
||||
|
||||
Original license: GPL v3
|
||||
Original project: https://github.com/Akarin-project/Akarin
|
||||
|
||||
diff --git a/net/minecraft/server/players/StoredUserList.java b/net/minecraft/server/players/StoredUserList.java
|
||||
index 39483f7b453d6faedeccc1ab1eda76669395ea5a..1fe9be5381b43d240593c8394977d7ffccd1132b 100644
|
||||
--- a/net/minecraft/server/players/StoredUserList.java
|
||||
+++ b/net/minecraft/server/players/StoredUserList.java
|
||||
@@ -97,13 +97,23 @@ public abstract class StoredUserList<K, V extends StoredUserEntry<K>> {
|
||||
}
|
||||
|
||||
public void save() throws IOException {
|
||||
+ Runnable saveTask = () -> {// Leaf - Akarin - Save Json list asynchronously
|
||||
this.removeExpired(); // Paper - remove expired values before saving
|
||||
JsonArray jsonArray = new JsonArray();
|
||||
this.map.values().stream().map(storedEntry -> Util.make(new JsonObject(), storedEntry::serialize)).forEach(jsonArray::add);
|
||||
|
||||
+ try { // Leaf - Akarin - Save Json list asynchronously
|
||||
try (BufferedWriter writer = Files.newWriter(this.file, StandardCharsets.UTF_8)) {
|
||||
GSON.toJson(jsonArray, GSON.newJsonWriter(writer));
|
||||
}
|
||||
+
|
||||
+ // Leaf start - Akarin - Save Json list asynchronously
|
||||
+ } catch (Exception e) {
|
||||
+ StoredUserList.LOGGER.warn("Failed to save {} asynchronously", this.file, e);
|
||||
+ }
|
||||
+ };
|
||||
+ io.papermc.paper.util.MCUtil.scheduleAsyncTask(saveTask);
|
||||
+ // Leaf end - Akarin - Save Json list asynchronously
|
||||
}
|
||||
|
||||
public void load() throws IOException {
|
||||
@@ -0,0 +1,49 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Cryptite <cryptite@gmail.com>
|
||||
Date: Sat, 13 Aug 2022 08:58:14 -0500
|
||||
Subject: [PATCH] Slice: Smooth Teleports
|
||||
|
||||
Original license: MIT
|
||||
Original project: https://github.com/Cryptite/Slice
|
||||
|
||||
Co-authored-by: HaHaWTH <102713261+HaHaWTH@users.noreply.github.com>
|
||||
|
||||
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
|
||||
index 1d7e48a8bea7356982b9c4bf1f4dd4877a8d9f44..50278223ec57f32a75bf413f57529f365267a4ee 100644
|
||||
--- a/net/minecraft/server/level/ServerPlayer.java
|
||||
+++ b/net/minecraft/server/level/ServerPlayer.java
|
||||
@@ -421,6 +421,7 @@ public class ServerPlayer extends Player {
|
||||
public @Nullable com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent playerNaturallySpawnedEvent; // Paper - PlayerNaturallySpawnCreaturesEvent
|
||||
public @Nullable String clientBrandName = null; // Paper - Brand support
|
||||
public @Nullable org.bukkit.event.player.PlayerQuitEvent.QuitReason quitReason = null; // Paper - Add API for quit reason; there are a lot of changes to do if we change all methods leading to the event
|
||||
+ public boolean smoothWorldTeleport; // Slice
|
||||
|
||||
public ServerPlayer(MinecraftServer server, ServerLevel level, GameProfile gameProfile, ClientInformation clientInformation) {
|
||||
super(level, level.getSharedSpawnPos(), level.getSharedSpawnAngle(), gameProfile);
|
||||
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
|
||||
index e62f24dcdc5f09ce9c316615f2c67778570bcc6e..2d932aa5a90cf00bbe4756f7108c7601561677d3 100644
|
||||
--- a/net/minecraft/server/players/PlayerList.java
|
||||
+++ b/net/minecraft/server/players/PlayerList.java
|
||||
@@ -794,11 +794,11 @@ public abstract class PlayerList {
|
||||
byte b = (byte)(keepInventory ? 1 : 0);
|
||||
ServerLevel serverLevel = serverPlayer.serverLevel();
|
||||
LevelData levelData = serverLevel.getLevelData();
|
||||
- serverPlayer.connection.send(new ClientboundRespawnPacket(serverPlayer.createCommonSpawnInfo(serverLevel), b));
|
||||
+ if (!serverPlayer.smoothWorldTeleport || !isSameLogicalHeight((ServerLevel) fromWorld, level)) serverPlayer.connection.send(new ClientboundRespawnPacket(serverPlayer.createCommonSpawnInfo(serverLevel), b)); // Leaf - Slice
|
||||
// serverPlayer.connection.teleport(serverPlayer.getX(), serverPlayer.getY(), serverPlayer.getZ(), serverPlayer.getYRot(), serverPlayer.getXRot());
|
||||
serverPlayer.connection.send(new ClientboundSetChunkCacheRadiusPacket(serverLevel.spigotConfig.viewDistance)); // Spigot
|
||||
serverPlayer.connection.send(new ClientboundSetSimulationDistancePacket(serverLevel.spigotConfig.simulationDistance)); // Spigot
|
||||
- serverPlayer.connection.teleport(org.bukkit.craftbukkit.util.CraftLocation.toBukkit(serverPlayer.position(), serverLevel.getWorld(), serverPlayer.getYRot(), serverPlayer.getXRot())); // CraftBukkit
|
||||
+ if (!serverPlayer.smoothWorldTeleport || !isSameLogicalHeight((ServerLevel) fromWorld, level)) serverPlayer.connection.teleport(org.bukkit.craftbukkit.util.CraftLocation.toBukkit(serverPlayer.position(), serverLevel.getWorld(), serverPlayer.getYRot(), serverPlayer.getXRot())); // CraftBukkit // Leaf - Slice
|
||||
serverPlayer.connection.send(new ClientboundSetDefaultSpawnPositionPacket(level.getSharedSpawnPos(), level.getSharedSpawnAngle()));
|
||||
serverPlayer.connection.send(new ClientboundChangeDifficultyPacket(levelData.getDifficulty(), levelData.isDifficultyLocked()));
|
||||
serverPlayer.connection
|
||||
@@ -875,6 +875,8 @@ public abstract class PlayerList {
|
||||
return serverPlayer;
|
||||
}
|
||||
|
||||
+ public static boolean isSameLogicalHeight(ServerLevel fromLevel, ServerLevel toLevel) { return fromLevel.getLogicalHeight() == toLevel.getLogicalHeight(); } // Leaf - Slice - Check world height before smooth teleport
|
||||
+
|
||||
public void sendActivePlayerEffects(ServerPlayer player) {
|
||||
this.sendActiveEffects(player, player.connection);
|
||||
}
|
||||
@@ -0,0 +1,839 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: peaches94 <peachescu94@gmail.com>
|
||||
Date: Sun, 26 Jun 2022 16:51:37 -0500
|
||||
Subject: [PATCH] Petal: Async Pathfinding
|
||||
|
||||
Fixed & Updated by KaiijuMC
|
||||
Original license: GPLv3
|
||||
Original project: https://github.com/KaiijuMC/Kaiiju
|
||||
|
||||
Original license: GPLv3
|
||||
Original project: https://github.com/Bloom-host/Petal
|
||||
|
||||
Co-authored-by: HaHaWTH <102713261+HaHaWTH@users.noreply.github.com>
|
||||
Co-authored-by: Taiyou06 <kaandindar21@gmail.com>
|
||||
Co-authored-by: Altiami <yoshimo.kristin@gmail.com>
|
||||
|
||||
This patch was ported downstream from the Petal fork.
|
||||
|
||||
Makes most pathfinding-related work happen asynchronously
|
||||
|
||||
diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java
|
||||
index 92ebc61aa7f6f70292a384b56bd8ef77a15e485c..a1ecb7c5ee0e1fe1164e277d8991d6d990035f76 100644
|
||||
--- a/net/minecraft/world/entity/Mob.java
|
||||
+++ b/net/minecraft/world/entity/Mob.java
|
||||
@@ -242,6 +242,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab
|
||||
@Nullable
|
||||
@Override
|
||||
public LivingEntity getTarget() {
|
||||
+ //if (Thread.currentThread().getName().contains("petal-async-pathfinding-thread")) return this.target; // Kaiiju - Don't reset target when async pathfinding! // Leaf - Don't need this
|
||||
return this.target;
|
||||
}
|
||||
|
||||
diff --git a/net/minecraft/world/entity/ai/behavior/AcquirePoi.java b/net/minecraft/world/entity/ai/behavior/AcquirePoi.java
|
||||
index 7470150b5c613ae31d94170d9f5eedac67add63d..5bcc53eef44faa1d3dccb72d1fd4be476c2f3793 100644
|
||||
--- a/net/minecraft/world/entity/ai/behavior/AcquirePoi.java
|
||||
+++ b/net/minecraft/world/entity/ai/behavior/AcquirePoi.java
|
||||
@@ -98,21 +98,20 @@ public class AcquirePoi {
|
||||
.limit(5L)
|
||||
.filter(pair1 -> predicate.test(level, pair1.getSecond()))
|
||||
.collect(Collectors.toSet());
|
||||
- Path path = findPathToPois(mob, set);
|
||||
- if (path != null && path.canReach()) {
|
||||
- BlockPos target = path.getTarget();
|
||||
- poiManager.getType(target).ifPresent(holder -> {
|
||||
- poiManager.take(acquirablePois, (holder1, blockPos) -> blockPos.equals(target), target, 1);
|
||||
- memoryAccessor.set(GlobalPos.of(level.dimension(), target));
|
||||
- entityEventId.ifPresent(id -> level.broadcastEntityEvent(mob, id));
|
||||
- map.clear();
|
||||
- DebugPackets.sendPoiTicketCountPacket(level, target);
|
||||
+ // Kaiiju start - petal - Async path processing
|
||||
+ if (org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled) {
|
||||
+ // await on path async
|
||||
+ Path possiblePath = findPathToPois(mob, set);
|
||||
+
|
||||
+ // wait on the path to be processed
|
||||
+ org.dreeam.leaf.async.path.AsyncPathProcessor.awaitProcessing(possiblePath, path -> {
|
||||
+ processPath(acquirablePois, entityEventId, (Long2ObjectMap<JitteredLinearRetry>) map, memoryAccessor, level, mob, time, poiManager, set, path);
|
||||
});
|
||||
} else {
|
||||
- for (Pair<Holder<PoiType>, BlockPos> pair : set) {
|
||||
- map.computeIfAbsent(pair.getSecond().asLong(), l -> new AcquirePoi.JitteredLinearRetry(level.random, time));
|
||||
- }
|
||||
+ Path path = findPathToPois(mob, set);
|
||||
+ processPath(acquirablePois, entityEventId, map, memoryAccessor, level, mob, time, poiManager, set, path);
|
||||
}
|
||||
+ // Kaiiju end - Async path processing
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -124,6 +123,34 @@ public class AcquirePoi {
|
||||
: BehaviorBuilder.create(instance -> instance.group(instance.absent(existingAbsentMemory)).apply(instance, memoryAccessor -> oneShot));
|
||||
}
|
||||
|
||||
+ // Leaf start - Kaiiju - Async path processing
|
||||
+ private static void processPath(Predicate<Holder<PoiType>> acquirablePois,
|
||||
+ Optional<Byte> entityEventId,
|
||||
+ Long2ObjectMap<JitteredLinearRetry> map,
|
||||
+ net.minecraft.world.entity.ai.behavior.declarative.MemoryAccessor<com.mojang.datafixers.kinds.Const.Mu<com.mojang.datafixers.util.Unit>, GlobalPos> memoryAccessor,
|
||||
+ ServerLevel level,
|
||||
+ PathfinderMob mob,
|
||||
+ long time,
|
||||
+ PoiManager poiManager,
|
||||
+ Set<Pair<Holder<PoiType>, BlockPos>> set,
|
||||
+ Path path) {
|
||||
+ if (path != null && path.canReach()) {
|
||||
+ BlockPos target = path.getTarget();
|
||||
+ poiManager.getType(target).ifPresent(holder -> {
|
||||
+ poiManager.take(acquirablePois, (holder1, blockPos) -> blockPos.equals(target), target, 1);
|
||||
+ memoryAccessor.set(GlobalPos.of(level.dimension(), target));
|
||||
+ entityEventId.ifPresent(id -> level.broadcastEntityEvent(mob, id));
|
||||
+ map.clear();
|
||||
+ DebugPackets.sendPoiTicketCountPacket(level, target);
|
||||
+ });
|
||||
+ } else {
|
||||
+ for (Pair<Holder<PoiType>, BlockPos> pair : set) {
|
||||
+ map.computeIfAbsent(pair.getSecond().asLong(), l -> new AcquirePoi.JitteredLinearRetry(level.random, time));
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ // Leaf end - Kaiiju - Async path processing
|
||||
+
|
||||
@Nullable
|
||||
public static Path findPathToPois(Mob mob, Set<Pair<Holder<PoiType>, BlockPos>> poiPositions) {
|
||||
if (poiPositions.isEmpty()) {
|
||||
diff --git a/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java b/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java
|
||||
index 621ba76784f2b92790eca62be4d0688834335ab6..e2e1532d2ffb709e347db42b1b5b6cae5e7e9700 100644
|
||||
--- a/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java
|
||||
+++ b/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java
|
||||
@@ -21,6 +21,7 @@ public class MoveToTargetSink extends Behavior<Mob> {
|
||||
private int remainingCooldown;
|
||||
@Nullable
|
||||
private Path path;
|
||||
+ private boolean finishedProcessing; // Kaiiju - petal - track when path is processed
|
||||
@Nullable
|
||||
private BlockPos lastTargetPos;
|
||||
private float speedModifier;
|
||||
@@ -53,9 +54,10 @@ public class MoveToTargetSink extends Behavior<Mob> {
|
||||
Brain<?> brain = owner.getBrain();
|
||||
WalkTarget walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET).get();
|
||||
boolean flag = this.reachedTarget(owner, walkTarget);
|
||||
- if (!flag && this.tryComputePath(owner, walkTarget, level.getGameTime())) {
|
||||
+ if (!org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled && !flag && this.tryComputePath(owner, walkTarget, level.getGameTime())) { // Kaiiju - petal - async path processing means we can't know if the path is reachable here
|
||||
this.lastTargetPos = walkTarget.getTarget().currentBlockPosition();
|
||||
return true;
|
||||
+ } else if (org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled && !flag) { return true; // Kaiiju - async pathfinding
|
||||
} else {
|
||||
brain.eraseMemory(MemoryModuleType.WALK_TARGET);
|
||||
if (flag) {
|
||||
@@ -69,6 +71,7 @@ public class MoveToTargetSink extends Behavior<Mob> {
|
||||
|
||||
@Override
|
||||
protected boolean canStillUse(ServerLevel level, Mob entity, long gameTime) {
|
||||
+ if (org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled && !this.finishedProcessing) return true; // Kaiiju - petal - wait for processing
|
||||
if (this.path != null && this.lastTargetPos != null) {
|
||||
Optional<WalkTarget> memory = entity.getBrain().getMemory(MemoryModuleType.WALK_TARGET);
|
||||
boolean flag = memory.map(MoveToTargetSink::isWalkTargetSpectator).orElse(false);
|
||||
@@ -95,12 +98,68 @@ public class MoveToTargetSink extends Behavior<Mob> {
|
||||
|
||||
@Override
|
||||
protected void start(ServerLevel level, Mob entity, long gameTime) {
|
||||
+ // Kaiiju start - petal - start processing
|
||||
+ if (org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled) {
|
||||
+ Brain<?> brain = entity.getBrain();
|
||||
+ WalkTarget walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET).get();
|
||||
+
|
||||
+ this.finishedProcessing = false;
|
||||
+ this.lastTargetPos = walkTarget.getTarget().currentBlockPosition();
|
||||
+ this.path = this.computePath(entity, walkTarget);
|
||||
+ return;
|
||||
+ }
|
||||
+ // Kaiiju end
|
||||
entity.getBrain().setMemory(MemoryModuleType.PATH, this.path);
|
||||
entity.getNavigation().moveTo(this.path, (double)this.speedModifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tick(ServerLevel level, Mob owner, long gameTime) {
|
||||
+ // Kaiiju start - petal - Async path processing
|
||||
+ if (org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled) {
|
||||
+ if (this.path != null && !this.path.isProcessed()) return; // wait for processing
|
||||
+
|
||||
+ if (!this.finishedProcessing) {
|
||||
+ this.finishedProcessing = true;
|
||||
+
|
||||
+ Brain<?> brain = owner.getBrain();
|
||||
+ boolean canReach = this.path != null && this.path.canReach();
|
||||
+ if (canReach) {
|
||||
+ brain.eraseMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE);
|
||||
+ } else if (!brain.hasMemoryValue(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE)) {
|
||||
+ brain.setMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE, gameTime);
|
||||
+ }
|
||||
+
|
||||
+ if (!canReach) {
|
||||
+ Optional<WalkTarget> walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET);
|
||||
+
|
||||
+ if (!walkTarget.isPresent()) return;
|
||||
+
|
||||
+ BlockPos blockPos = walkTarget.get().getTarget().currentBlockPosition();
|
||||
+ Vec3 vec3 = DefaultRandomPos.getPosTowards((PathfinderMob) owner, 10, 7, Vec3.atBottomCenterOf(blockPos), (float) Math.PI / 2F);
|
||||
+ if (vec3 != null) {
|
||||
+ // try recalculating the path using a random position
|
||||
+ this.path = owner.getNavigation().createPath(vec3.x, vec3.y, vec3.z, 0);
|
||||
+ this.finishedProcessing = false;
|
||||
+ return;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ owner.getBrain().setMemory(MemoryModuleType.PATH, this.path);
|
||||
+ owner.getNavigation().moveTo(this.path, this.speedModifier);
|
||||
+ }
|
||||
+
|
||||
+ Path path = owner.getNavigation().getPath();
|
||||
+ Brain<?> brain = owner.getBrain();
|
||||
+
|
||||
+ if (path != null && this.lastTargetPos != null && brain.hasMemoryValue(MemoryModuleType.WALK_TARGET)) {
|
||||
+ WalkTarget walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET).get(); // we know isPresent = true
|
||||
+ if (walkTarget.getTarget().currentBlockPosition().distSqr(this.lastTargetPos) > 4.0D) {
|
||||
+ this.start(level, owner, gameTime);
|
||||
+ }
|
||||
+ }
|
||||
+ } else {
|
||||
+ // Kaiiju end
|
||||
Path path = owner.getNavigation().getPath();
|
||||
Brain<?> brain = owner.getBrain();
|
||||
if (this.path != path) {
|
||||
@@ -115,7 +174,23 @@ public class MoveToTargetSink extends Behavior<Mob> {
|
||||
this.start(level, owner, gameTime);
|
||||
}
|
||||
}
|
||||
+ } // Kaiiju - async path processing
|
||||
+ }
|
||||
+
|
||||
+ // Kaiiju start - petal - Async path processing
|
||||
+ @Nullable
|
||||
+ private Path computePath(Mob entity, WalkTarget walkTarget) {
|
||||
+ BlockPos blockPos = walkTarget.getTarget().currentBlockPosition();
|
||||
+ // don't pathfind outside region
|
||||
+ //if (!io.papermc.paper.util.TickThread.isTickThreadFor((ServerLevel) entity.level(), blockPos)) return null; // Leaf - Don't need this
|
||||
+ this.speedModifier = walkTarget.getSpeedModifier();
|
||||
+ Brain<?> brain = entity.getBrain();
|
||||
+ if (this.reachedTarget(entity, walkTarget)) {
|
||||
+ brain.eraseMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE);
|
||||
+ }
|
||||
+ return entity.getNavigation().createPath(blockPos, 0);
|
||||
}
|
||||
+ // Kaiiju end
|
||||
|
||||
private boolean tryComputePath(Mob mob, WalkTarget target, long time) {
|
||||
BlockPos blockPos = target.getTarget().currentBlockPosition();
|
||||
diff --git a/net/minecraft/world/entity/ai/behavior/SetClosestHomeAsWalkTarget.java b/net/minecraft/world/entity/ai/behavior/SetClosestHomeAsWalkTarget.java
|
||||
index 4f9f3367b1ca3903df03a80fa2b01a3d24e6e77d..51413df5cd61b3ff59c6c6c3ec69d6732ab07d83 100644
|
||||
--- a/net/minecraft/world/entity/ai/behavior/SetClosestHomeAsWalkTarget.java
|
||||
+++ b/net/minecraft/world/entity/ai/behavior/SetClosestHomeAsWalkTarget.java
|
||||
@@ -60,17 +60,20 @@ public class SetClosestHomeAsWalkTarget {
|
||||
poi -> poi.is(PoiTypes.HOME), predicate, mob.blockPosition(), 48, PoiManager.Occupancy.ANY
|
||||
)
|
||||
.collect(Collectors.toSet());
|
||||
+ // Kaiiju start - petal - Async path processing
|
||||
+ if (org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled) {
|
||||
+ // await on path async
|
||||
+ Path possiblePath = AcquirePoi.findPathToPois(mob, set);
|
||||
+
|
||||
+ // wait on the path to be processed
|
||||
+ org.dreeam.leaf.async.path.AsyncPathProcessor.awaitProcessing(possiblePath, path -> {
|
||||
+ processPath(speedModifier, map, mutableLong, walkTarget, level, poiManager, mutableInt, path);
|
||||
+ });
|
||||
+ } else {
|
||||
+ // Kaiiju end
|
||||
Path path = AcquirePoi.findPathToPois(mob, set);
|
||||
- if (path != null && path.canReach()) {
|
||||
- BlockPos target = path.getTarget();
|
||||
- Optional<Holder<PoiType>> type = poiManager.getType(target);
|
||||
- if (type.isPresent()) {
|
||||
- walkTarget.set(new WalkTarget(target, speedModifier, 1));
|
||||
- DebugPackets.sendPoiTicketCountPacket(level, target);
|
||||
- }
|
||||
- } else if (mutableInt.getValue() < 5) {
|
||||
- map.long2LongEntrySet().removeIf(entry -> entry.getLongValue() < mutableLong.getValue());
|
||||
- }
|
||||
+ processPath(speedModifier, map, mutableLong, walkTarget, level, poiManager, mutableInt, path);
|
||||
+ } // Kaiiju - async path processing
|
||||
|
||||
return true;
|
||||
} else {
|
||||
@@ -81,4 +84,26 @@ public class SetClosestHomeAsWalkTarget {
|
||||
)
|
||||
);
|
||||
}
|
||||
+
|
||||
+ // Leaf start - Kaiiju - petal - Async path processing
|
||||
+ private static void processPath(float speedModifier,
|
||||
+ Long2LongMap map,
|
||||
+ MutableLong mutableLong,
|
||||
+ net.minecraft.world.entity.ai.behavior.declarative.MemoryAccessor<com.mojang.datafixers.kinds.Const.Mu<com.mojang.datafixers.util.Unit>, WalkTarget> walkTarget,
|
||||
+ net.minecraft.server.level.ServerLevel level,
|
||||
+ PoiManager poiManager,
|
||||
+ MutableInt mutableInt,
|
||||
+ @org.jetbrains.annotations.Nullable Path path) {
|
||||
+ if (path != null && path.canReach()) {
|
||||
+ BlockPos target = path.getTarget();
|
||||
+ Optional<Holder<PoiType>> type = poiManager.getType(target);
|
||||
+ if (type.isPresent()) {
|
||||
+ walkTarget.set(new WalkTarget(target, speedModifier, 1));
|
||||
+ DebugPackets.sendPoiTicketCountPacket(level, target);
|
||||
+ }
|
||||
+ } else if (mutableInt.getValue() < 5) {
|
||||
+ map.long2LongEntrySet().removeIf(entry -> entry.getLongValue() < mutableLong.getValue());
|
||||
+ }
|
||||
+ }
|
||||
+ // Leaf end - Kaiiju - petal - Async path processing
|
||||
}
|
||||
diff --git a/net/minecraft/world/entity/ai/goal/DoorInteractGoal.java b/net/minecraft/world/entity/ai/goal/DoorInteractGoal.java
|
||||
index d8f532c5e68ff4dff933556c4f981e9474c044e6..95733482a647935e1e7f81fa71a0e99ea52e9a9b 100644
|
||||
--- a/net/minecraft/world/entity/ai/goal/DoorInteractGoal.java
|
||||
+++ b/net/minecraft/world/entity/ai/goal/DoorInteractGoal.java
|
||||
@@ -56,7 +56,7 @@ public abstract class DoorInteractGoal extends Goal {
|
||||
} else {
|
||||
GroundPathNavigation groundPathNavigation = (GroundPathNavigation)this.mob.getNavigation();
|
||||
Path path = groundPathNavigation.getPath();
|
||||
- if (path != null && !path.isDone()) {
|
||||
+ if (path != null && path.isProcessed() && !path.isDone()) { // Kaiiju - async pathfinding - ensure path is processed
|
||||
for (int i = 0; i < Math.min(path.getNextNodeIndex() + 2, path.getNodeCount()); i++) {
|
||||
Node node = path.getNode(i);
|
||||
this.doorPos = new BlockPos(node.x, node.y + 1, node.z);
|
||||
diff --git a/net/minecraft/world/entity/ai/navigation/AmphibiousPathNavigation.java b/net/minecraft/world/entity/ai/navigation/AmphibiousPathNavigation.java
|
||||
index 66a02fe7594522ef391d67e09856bf3f70fe597d..55e4e6542ac05d89b8d062c546de85b29fb0099f 100644
|
||||
--- a/net/minecraft/world/entity/ai/navigation/AmphibiousPathNavigation.java
|
||||
+++ b/net/minecraft/world/entity/ai/navigation/AmphibiousPathNavigation.java
|
||||
@@ -12,9 +12,25 @@ public class AmphibiousPathNavigation extends PathNavigation {
|
||||
super(mob, level);
|
||||
}
|
||||
|
||||
+ // Kaiiju start - petal - async path processing
|
||||
+ private static final org.dreeam.leaf.async.path.NodeEvaluatorGenerator nodeEvaluatorGenerator = (org.dreeam.leaf.async.path.NodeEvaluatorFeatures nodeEvaluatorFeatures) -> {
|
||||
+ AmphibiousNodeEvaluator nodeEvaluator = new AmphibiousNodeEvaluator(false);
|
||||
+ nodeEvaluator.setCanPassDoors(nodeEvaluatorFeatures.canPassDoors());
|
||||
+ nodeEvaluator.setCanFloat(nodeEvaluatorFeatures.canFloat());
|
||||
+ nodeEvaluator.setCanWalkOverFences(nodeEvaluatorFeatures.canWalkOverFences());
|
||||
+ nodeEvaluator.setCanOpenDoors(nodeEvaluatorFeatures.canOpenDoors());
|
||||
+ return nodeEvaluator;
|
||||
+ };
|
||||
+ // Kaiiju end
|
||||
+
|
||||
@Override
|
||||
protected PathFinder createPathFinder(int maxVisitedNodes) {
|
||||
this.nodeEvaluator = new AmphibiousNodeEvaluator(false);
|
||||
+ // Kaiiju start - petal - async path processing
|
||||
+ if (org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled) {
|
||||
+ return new PathFinder(this.nodeEvaluator, maxVisitedNodes, nodeEvaluatorGenerator);
|
||||
+ }
|
||||
+ // Kaiiju end
|
||||
return new PathFinder(this.nodeEvaluator, maxVisitedNodes);
|
||||
}
|
||||
|
||||
diff --git a/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java b/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java
|
||||
index 71ea68b56b3069bdf8e47931156b6ef49ea8ce5d..5de4c1138c7092ff8240fcb30cd64ff1f4d12088 100644
|
||||
--- a/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java
|
||||
+++ b/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java
|
||||
@@ -16,9 +16,25 @@ public class FlyingPathNavigation extends PathNavigation {
|
||||
super(mob, level);
|
||||
}
|
||||
|
||||
+ // Kaiiju start - petal - async path processing
|
||||
+ private static final org.dreeam.leaf.async.path.NodeEvaluatorGenerator nodeEvaluatorGenerator = (org.dreeam.leaf.async.path.NodeEvaluatorFeatures nodeEvaluatorFeatures) -> {
|
||||
+ FlyNodeEvaluator nodeEvaluator = new FlyNodeEvaluator();
|
||||
+ nodeEvaluator.setCanPassDoors(nodeEvaluatorFeatures.canPassDoors());
|
||||
+ nodeEvaluator.setCanFloat(nodeEvaluatorFeatures.canFloat());
|
||||
+ nodeEvaluator.setCanWalkOverFences(nodeEvaluatorFeatures.canWalkOverFences());
|
||||
+ nodeEvaluator.setCanOpenDoors(nodeEvaluatorFeatures.canOpenDoors());
|
||||
+ return nodeEvaluator;
|
||||
+ };
|
||||
+ // Kaiiju end
|
||||
+
|
||||
@Override
|
||||
protected PathFinder createPathFinder(int maxVisitedNodes) {
|
||||
this.nodeEvaluator = new FlyNodeEvaluator();
|
||||
+ // Kaiiju start - petal - async path processing
|
||||
+ if (org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled) {
|
||||
+ return new PathFinder(this.nodeEvaluator, maxVisitedNodes, nodeEvaluatorGenerator);
|
||||
+ }
|
||||
+ // Kaiiju end
|
||||
return new PathFinder(this.nodeEvaluator, maxVisitedNodes);
|
||||
}
|
||||
|
||||
@@ -48,6 +64,7 @@ public class FlyingPathNavigation extends PathNavigation {
|
||||
if (this.hasDelayedRecomputation) {
|
||||
this.recomputePath();
|
||||
}
|
||||
+ if (this.path != null && !this.path.isProcessed()) return; // Kaiiju - petal - async path processing
|
||||
|
||||
if (!this.isDone()) {
|
||||
if (this.canUpdatePath()) {
|
||||
diff --git a/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java b/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java
|
||||
index 045cfafb3afe8271d60852ae3c7cdcb039b44d4f..3f55b00e2f8924a8450df8a3f9812a4ae2983df3 100644
|
||||
--- a/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java
|
||||
+++ b/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java
|
||||
@@ -24,9 +24,25 @@ public class GroundPathNavigation extends PathNavigation {
|
||||
super(mob, level);
|
||||
}
|
||||
|
||||
+ // Kaiiju start - petal - async path processing
|
||||
+ protected static final org.dreeam.leaf.async.path.NodeEvaluatorGenerator nodeEvaluatorGenerator = (org.dreeam.leaf.async.path.NodeEvaluatorFeatures nodeEvaluatorFeatures) -> {
|
||||
+ WalkNodeEvaluator nodeEvaluator = new WalkNodeEvaluator();
|
||||
+ nodeEvaluator.setCanPassDoors(nodeEvaluatorFeatures.canPassDoors());
|
||||
+ nodeEvaluator.setCanFloat(nodeEvaluatorFeatures.canFloat());
|
||||
+ nodeEvaluator.setCanWalkOverFences(nodeEvaluatorFeatures.canWalkOverFences());
|
||||
+ nodeEvaluator.setCanOpenDoors(nodeEvaluatorFeatures.canOpenDoors());
|
||||
+ return nodeEvaluator;
|
||||
+ };
|
||||
+ // Kaiiju end
|
||||
+
|
||||
@Override
|
||||
protected PathFinder createPathFinder(int maxVisitedNodes) {
|
||||
this.nodeEvaluator = new WalkNodeEvaluator();
|
||||
+ // Kaiiju start - petal - async path processing
|
||||
+ if (org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled) {
|
||||
+ return new PathFinder(this.nodeEvaluator, maxVisitedNodes, nodeEvaluatorGenerator);
|
||||
+ }
|
||||
+ // Kaiiju end
|
||||
return new PathFinder(this.nodeEvaluator, maxVisitedNodes);
|
||||
}
|
||||
|
||||
diff --git a/net/minecraft/world/entity/ai/navigation/PathNavigation.java b/net/minecraft/world/entity/ai/navigation/PathNavigation.java
|
||||
index 6bbdfc748f1ce66689c63424fadcf261b1e967b3..84e855a4a9890a4772aee2383797cbc3d9b46dc3 100644
|
||||
--- a/net/minecraft/world/entity/ai/navigation/PathNavigation.java
|
||||
+++ b/net/minecraft/world/entity/ai/navigation/PathNavigation.java
|
||||
@@ -167,6 +167,10 @@ public abstract class PathNavigation {
|
||||
return null;
|
||||
} else if (!this.canUpdatePath()) {
|
||||
return null;
|
||||
+ // Kaiiju start - petal - catch early if it's still processing these positions let it keep processing
|
||||
+ } else if (this.path instanceof org.dreeam.leaf.async.path.AsyncPath asyncPath && !asyncPath.isProcessed() && asyncPath.hasSameProcessingPositions(targets)) {
|
||||
+ return this.path;
|
||||
+ // Kaiiju end
|
||||
} else if (this.path != null && !this.path.isDone() && targets.contains(this.targetPos)) {
|
||||
return this.path;
|
||||
} else {
|
||||
@@ -191,11 +195,29 @@ public abstract class PathNavigation {
|
||||
int i = (int)(followRange + regionOffset);
|
||||
PathNavigationRegion pathNavigationRegion = new PathNavigationRegion(this.level, blockPos.offset(-i, -i, -i), blockPos.offset(i, i, i));
|
||||
Path path = this.pathFinder.findPath(pathNavigationRegion, this.mob, targets, followRange, accuracy, this.maxVisitedNodesMultiplier);
|
||||
+ // Kaiiju start - petal - async path processing
|
||||
+ if (org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled) {
|
||||
+ // assign early a target position. most calls will only have 1 position
|
||||
+ if (!targets.isEmpty()) this.targetPos = targets.iterator().next();
|
||||
+
|
||||
+ org.dreeam.leaf.async.path.AsyncPathProcessor.awaitProcessing(path, processedPath -> {
|
||||
+ // check that processing didn't take so long that we calculated a new path
|
||||
+ if (processedPath != this.path) return;
|
||||
+
|
||||
+ if (processedPath != null && processedPath.getTarget() != null) {
|
||||
+ this.targetPos = processedPath.getTarget();
|
||||
+ this.reachRange = accuracy;
|
||||
+ this.resetStuckTimeout();
|
||||
+ }
|
||||
+ });
|
||||
+ } else {
|
||||
+ // Kaiiju end
|
||||
if (path != null && path.getTarget() != null) {
|
||||
this.targetPos = path.getTarget();
|
||||
this.reachRange = accuracy;
|
||||
this.resetStuckTimeout();
|
||||
}
|
||||
+ } // Kaiiju - async path processing
|
||||
|
||||
return path;
|
||||
}
|
||||
@@ -246,8 +268,8 @@ public abstract class PathNavigation {
|
||||
if (this.isDone()) {
|
||||
return false;
|
||||
} else {
|
||||
- this.trimPath();
|
||||
- if (this.path.getNodeCount() <= 0) {
|
||||
+ if (path.isProcessed()) this.trimPath(); // Kaiiju - petal - only trim if processed
|
||||
+ if (path.isProcessed() && this.path.getNodeCount() <= 0) { // Kaiiju - petal - only check node count if processed
|
||||
return false;
|
||||
} else {
|
||||
this.speedModifier = speed;
|
||||
@@ -270,6 +292,7 @@ public abstract class PathNavigation {
|
||||
if (this.hasDelayedRecomputation) {
|
||||
this.recomputePath();
|
||||
}
|
||||
+ if (this.path != null && !this.path.isProcessed()) return; // Kaiiju - petal - skip pathfinding if we're still processing
|
||||
|
||||
if (!this.isDone()) {
|
||||
if (this.canUpdatePath()) {
|
||||
@@ -299,6 +322,7 @@ public abstract class PathNavigation {
|
||||
}
|
||||
|
||||
protected void followThePath() {
|
||||
+ if (!this.path.isProcessed()) return; // Kaiiju - petal - skip if not processed
|
||||
Vec3 tempMobPos = this.getTempMobPos();
|
||||
this.maxDistanceToWaypoint = this.mob.getBbWidth() > 0.75F ? this.mob.getBbWidth() / 2.0F : 0.75F - this.mob.getBbWidth() / 2.0F;
|
||||
Vec3i nextNodePos = this.path.getNextNodePos();
|
||||
@@ -455,7 +479,7 @@ public abstract class PathNavigation {
|
||||
public boolean shouldRecomputePath(BlockPos pos) {
|
||||
if (this.hasDelayedRecomputation) {
|
||||
return false;
|
||||
- } else if (this.path != null && !this.path.isDone() && this.path.getNodeCount() != 0) {
|
||||
+ } else if (this.path != null && this.path.isProcessed() && !this.path.isDone() && this.path.getNodeCount() != 0) { // Kaiiju - petal - Skip if not processed
|
||||
Node endNode = this.path.getEndNode();
|
||||
Vec3 vec3 = new Vec3((endNode.x + this.mob.getX()) / 2.0, (endNode.y + this.mob.getY()) / 2.0, (endNode.z + this.mob.getZ()) / 2.0);
|
||||
return pos.closerToCenterThan(vec3, this.path.getNodeCount() - this.path.getNextNodeIndex());
|
||||
diff --git a/net/minecraft/world/entity/ai/navigation/WaterBoundPathNavigation.java b/net/minecraft/world/entity/ai/navigation/WaterBoundPathNavigation.java
|
||||
index 2979846853898d78a2df19df2287da16dbe4ae71..1289a6e85f3fdb9187323343b6c20e17b6a7e296 100644
|
||||
--- a/net/minecraft/world/entity/ai/navigation/WaterBoundPathNavigation.java
|
||||
+++ b/net/minecraft/world/entity/ai/navigation/WaterBoundPathNavigation.java
|
||||
@@ -15,11 +15,27 @@ public class WaterBoundPathNavigation extends PathNavigation {
|
||||
super(mob, level);
|
||||
}
|
||||
|
||||
+ // Kaiiju start - petal - async path processing
|
||||
+ private static final org.dreeam.leaf.async.path.NodeEvaluatorGenerator nodeEvaluatorGenerator = (org.dreeam.leaf.async.path.NodeEvaluatorFeatures nodeEvaluatorFeatures) -> {
|
||||
+ SwimNodeEvaluator nodeEvaluator = new SwimNodeEvaluator(nodeEvaluatorFeatures.allowBreaching());
|
||||
+ nodeEvaluator.setCanPassDoors(nodeEvaluatorFeatures.canPassDoors());
|
||||
+ nodeEvaluator.setCanFloat(nodeEvaluatorFeatures.canFloat());
|
||||
+ nodeEvaluator.setCanWalkOverFences(nodeEvaluatorFeatures.canWalkOverFences());
|
||||
+ nodeEvaluator.setCanOpenDoors(nodeEvaluatorFeatures.canOpenDoors());
|
||||
+ return nodeEvaluator;
|
||||
+ };
|
||||
+ // Kaiiju end
|
||||
+
|
||||
@Override
|
||||
protected PathFinder createPathFinder(int maxVisitedNodes) {
|
||||
this.allowBreaching = this.mob.getType() == EntityType.DOLPHIN;
|
||||
this.nodeEvaluator = new SwimNodeEvaluator(this.allowBreaching);
|
||||
this.nodeEvaluator.setCanPassDoors(false);
|
||||
+ // Kaiiju start - async path processing
|
||||
+ if (org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled) {
|
||||
+ return new PathFinder(this.nodeEvaluator, maxVisitedNodes, nodeEvaluatorGenerator);
|
||||
+ }
|
||||
+ // Kaiiju end
|
||||
return new PathFinder(this.nodeEvaluator, maxVisitedNodes);
|
||||
}
|
||||
|
||||
diff --git a/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java b/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java
|
||||
index 6e9325f0800a35637fdec5edb8a514ea03741762..7ee85d8e9035093484f67437ec80755169436a88 100644
|
||||
--- a/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java
|
||||
+++ b/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java
|
||||
@@ -57,16 +57,31 @@ public class NearestBedSensor extends Sensor<Mob> {
|
||||
holder -> holder.is(PoiTypes.HOME), predicate, entity.blockPosition(), 48, PoiManager.Occupancy.ANY
|
||||
)
|
||||
.collect(Collectors.toSet());
|
||||
- Path path = AcquirePoi.findPathToPois(entity, set);
|
||||
- if (path != null && path.canReach()) {
|
||||
- BlockPos target = path.getTarget();
|
||||
- Optional<Holder<PoiType>> type = poiManager.getType(target);
|
||||
- if (type.isPresent()) {
|
||||
- entity.getBrain().setMemory(MemoryModuleType.NEAREST_BED, target);
|
||||
- }
|
||||
- } else if (this.triedCount < 5) {
|
||||
- this.batchCache.long2LongEntrySet().removeIf(entry -> entry.getLongValue() < this.lastUpdate);
|
||||
+ // Kaiiju start - await on async path processing
|
||||
+ if (org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled) {
|
||||
+ Path possiblePath = AcquirePoi.findPathToPois(entity, new java.util.HashSet<>(set));
|
||||
+ org.dreeam.leaf.async.path.AsyncPathProcessor.awaitProcessing(possiblePath, path -> {
|
||||
+ processPath(entity, poiManager, path);
|
||||
+ });
|
||||
+ } else {
|
||||
+ // Kaiiju end
|
||||
+ Path path = AcquirePoi.findPathToPois(entity, new java.util.HashSet<>(set));
|
||||
+ processPath(entity, poiManager, path);
|
||||
+ } // Kaiiju - async path processing
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // Leaf start - Kaiiju - await on async path processing
|
||||
+ private void processPath(Mob entity, PoiManager poiManager, @org.jetbrains.annotations.Nullable Path path) {
|
||||
+ if (path != null && path.canReach()) {
|
||||
+ BlockPos target = path.getTarget();
|
||||
+ Optional<Holder<PoiType>> type = poiManager.getType(target);
|
||||
+ if (type.isPresent()) {
|
||||
+ entity.getBrain().setMemory(MemoryModuleType.NEAREST_BED, target);
|
||||
}
|
||||
+ } else if (this.triedCount < 5) {
|
||||
+ this.batchCache.long2LongEntrySet().removeIf(entry -> entry.getLongValue() < this.lastUpdate);
|
||||
}
|
||||
}
|
||||
+ // Leaf end - Kaiiju - await on async path processing
|
||||
}
|
||||
diff --git a/net/minecraft/world/entity/animal/Bee.java b/net/minecraft/world/entity/animal/Bee.java
|
||||
index edca2fa21e600fa1e7ef91af673adaae7d4c86c4..6eb3a46ea67a30b7c88961a0c2168fc030cc14ad 100644
|
||||
--- a/net/minecraft/world/entity/animal/Bee.java
|
||||
+++ b/net/minecraft/world/entity/animal/Bee.java
|
||||
@@ -856,7 +856,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
|
||||
} else {
|
||||
Bee.this.pathfindRandomlyTowards(Bee.this.hivePos);
|
||||
}
|
||||
- } else {
|
||||
+ } else if (navigation.getPath() != null && navigation.getPath().isProcessed()) { // Kaiiju - petal - check processing
|
||||
boolean flag = this.pathfindDirectlyTowards(Bee.this.hivePos);
|
||||
if (!flag) {
|
||||
this.dropAndBlacklistHive();
|
||||
@@ -910,7 +910,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
|
||||
return true;
|
||||
} else {
|
||||
Path path = Bee.this.navigation.getPath();
|
||||
- return path != null && path.getTarget().equals(pos) && path.canReach() && path.isDone();
|
||||
+ return path != null && path.isProcessed() && path.getTarget().equals(pos) && path.canReach() && path.isDone(); // Kaiiju - petal - ensure path is processed
|
||||
}
|
||||
}
|
||||
}
|
||||
diff --git a/net/minecraft/world/entity/animal/frog/Frog.java b/net/minecraft/world/entity/animal/frog/Frog.java
|
||||
index bd80e58179fe577693fa419a77989b0db39abb04..ba215c4ebefe3b49b77cc94ee5855debe7f60c84 100644
|
||||
--- a/net/minecraft/world/entity/animal/frog/Frog.java
|
||||
+++ b/net/minecraft/world/entity/animal/frog/Frog.java
|
||||
@@ -431,9 +431,25 @@ public class Frog extends Animal {
|
||||
return pathType != PathType.WATER_BORDER && super.canCutCorner(pathType);
|
||||
}
|
||||
|
||||
+ // Kaiiju start - petal - async path processing
|
||||
+ private static final org.dreeam.leaf.async.path.NodeEvaluatorGenerator nodeEvaluatorGenerator = (org.dreeam.leaf.async.path.NodeEvaluatorFeatures nodeEvaluatorFeatures) -> {
|
||||
+ Frog.FrogNodeEvaluator nodeEvaluator = new Frog.FrogNodeEvaluator(true);
|
||||
+ nodeEvaluator.setCanPassDoors(nodeEvaluatorFeatures.canPassDoors());
|
||||
+ nodeEvaluator.setCanFloat(nodeEvaluatorFeatures.canFloat());
|
||||
+ nodeEvaluator.setCanWalkOverFences(nodeEvaluatorFeatures.canWalkOverFences());
|
||||
+ nodeEvaluator.setCanOpenDoors(nodeEvaluatorFeatures.canOpenDoors());
|
||||
+ return nodeEvaluator;
|
||||
+ };
|
||||
+ // Kaiiju end
|
||||
+
|
||||
@Override
|
||||
protected PathFinder createPathFinder(int maxVisitedNodes) {
|
||||
this.nodeEvaluator = new Frog.FrogNodeEvaluator(true);
|
||||
+ // Kaiiju start - petal - async path processing
|
||||
+ if (org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled) {
|
||||
+ return new PathFinder(this.nodeEvaluator, maxVisitedNodes, nodeEvaluatorGenerator);
|
||||
+ }
|
||||
+ // Kaiiju end
|
||||
return new PathFinder(this.nodeEvaluator, maxVisitedNodes);
|
||||
}
|
||||
}
|
||||
diff --git a/net/minecraft/world/entity/monster/Drowned.java b/net/minecraft/world/entity/monster/Drowned.java
|
||||
index 4978cd2a7a84130fc0de1cc481b39d61f388c812..8b39391015f76d6fc9c740385f4ac683b85eebe1 100644
|
||||
--- a/net/minecraft/world/entity/monster/Drowned.java
|
||||
+++ b/net/minecraft/world/entity/monster/Drowned.java
|
||||
@@ -239,7 +239,7 @@ public class Drowned extends Zombie implements RangedAttackMob {
|
||||
|
||||
protected boolean closeToNextPos() {
|
||||
Path path = this.getNavigation().getPath();
|
||||
- if (path != null) {
|
||||
+ if (path != null && path.isProcessed()) { // Kaiiju - petal - ensure path is processed
|
||||
BlockPos target = path.getTarget();
|
||||
if (target != null) {
|
||||
double d = this.distanceToSqr(target.getX(), target.getY(), target.getZ());
|
||||
diff --git a/net/minecraft/world/entity/monster/Strider.java b/net/minecraft/world/entity/monster/Strider.java
|
||||
index afa584e2aba6bebfb2dd343215b043c983281853..ed86080491c982c81306319e3730bfbd115d06aa 100644
|
||||
--- a/net/minecraft/world/entity/monster/Strider.java
|
||||
+++ b/net/minecraft/world/entity/monster/Strider.java
|
||||
@@ -507,9 +507,25 @@ public class Strider extends Animal implements ItemSteerable {
|
||||
super(strider, level);
|
||||
}
|
||||
|
||||
+ // Kaiiju start - petal - async path processing
|
||||
+ private static final org.dreeam.leaf.async.path.NodeEvaluatorGenerator nodeEvaluatorGenerator = (org.dreeam.leaf.async.path.NodeEvaluatorFeatures nodeEvaluatorFeatures) -> {
|
||||
+ WalkNodeEvaluator nodeEvaluator = new WalkNodeEvaluator();
|
||||
+ nodeEvaluator.setCanPassDoors(nodeEvaluatorFeatures.canPassDoors());
|
||||
+ nodeEvaluator.setCanFloat(nodeEvaluatorFeatures.canFloat());
|
||||
+ nodeEvaluator.setCanWalkOverFences(nodeEvaluatorFeatures.canWalkOverFences());
|
||||
+ nodeEvaluator.setCanOpenDoors(nodeEvaluatorFeatures.canOpenDoors());
|
||||
+ return nodeEvaluator;
|
||||
+ };
|
||||
+ // Kaiiju end
|
||||
+
|
||||
@Override
|
||||
protected PathFinder createPathFinder(int maxVisitedNodes) {
|
||||
this.nodeEvaluator = new WalkNodeEvaluator();
|
||||
+ // Kaiiju start - async path processing
|
||||
+ if (org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled) {
|
||||
+ return new PathFinder(this.nodeEvaluator, maxVisitedNodes, nodeEvaluatorGenerator);
|
||||
+ }
|
||||
+ // Kaiiju end
|
||||
return new PathFinder(this.nodeEvaluator, maxVisitedNodes);
|
||||
}
|
||||
|
||||
diff --git a/net/minecraft/world/entity/monster/warden/Warden.java b/net/minecraft/world/entity/monster/warden/Warden.java
|
||||
index 42ca4243d86ef4a14a9ce70da4b79f6c8eeb3a7d..ce66ded89ed851264affc075d65aa1c5abe84dc8 100644
|
||||
--- a/net/minecraft/world/entity/monster/warden/Warden.java
|
||||
+++ b/net/minecraft/world/entity/monster/warden/Warden.java
|
||||
@@ -555,6 +555,16 @@ public class Warden extends Monster implements VibrationSystem {
|
||||
@Override
|
||||
protected PathFinder createPathFinder(int maxVisitedNodes) {
|
||||
this.nodeEvaluator = new WalkNodeEvaluator();
|
||||
+ // Kaiiju start - petal - async path processing
|
||||
+ if (org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled) {
|
||||
+ return new PathFinder(this.nodeEvaluator, maxVisitedNodes, GroundPathNavigation.nodeEvaluatorGenerator) {
|
||||
+ @Override
|
||||
+ protected float distance(Node first, Node second) {
|
||||
+ return first.distanceToXZ(second);
|
||||
+ }
|
||||
+ };
|
||||
+ }
|
||||
+ // Kaiiju end
|
||||
return new PathFinder(this.nodeEvaluator, maxVisitedNodes) {
|
||||
@Override
|
||||
protected float distance(Node first, Node second) {
|
||||
diff --git a/net/minecraft/world/level/block/ShulkerBoxBlock.java b/net/minecraft/world/level/block/ShulkerBoxBlock.java
|
||||
index 49bac7af90b0a7c490141be6357563447783c6ca..8aedb5497ea52ae59e477162cb8089694a17f091 100644
|
||||
--- a/net/minecraft/world/level/block/ShulkerBoxBlock.java
|
||||
+++ b/net/minecraft/world/level/block/ShulkerBoxBlock.java
|
||||
@@ -166,9 +166,16 @@ public class ShulkerBoxBlock extends BaseEntityBlock {
|
||||
|
||||
@Override
|
||||
protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
|
||||
+ //if (Thread.currentThread().getName().contains("petal-async-pathfinding-thread")) return Shapes.block(); // Kaiiju - async pathfinding - we cannot get block entities // Leaf - Don't need this
|
||||
return level.getBlockEntity(pos) instanceof ShulkerBoxBlockEntity shulkerBoxBlockEntity
|
||||
? Shapes.create(shulkerBoxBlockEntity.getBoundingBox(state))
|
||||
: Shapes.block();
|
||||
+ // Kaiiju start - async pathfinding - workaround // Leaf - Don't need this
|
||||
+ /*
|
||||
+ } catch (NullPointerException e) {
|
||||
+ return Shapes.block();
|
||||
+ }
|
||||
+ */
|
||||
}
|
||||
|
||||
@Override
|
||||
diff --git a/net/minecraft/world/level/pathfinder/Path.java b/net/minecraft/world/level/pathfinder/Path.java
|
||||
index d6d3c8f5e5dd4a8cab0d3fcc131c3a59f06130c6..add5b8b98e4d09617cbd4e7dd2710dc50781613a 100644
|
||||
--- a/net/minecraft/world/level/pathfinder/Path.java
|
||||
+++ b/net/minecraft/world/level/pathfinder/Path.java
|
||||
@@ -26,6 +26,17 @@ public class Path {
|
||||
this.reached = reached;
|
||||
}
|
||||
|
||||
+ // Kaiiju start - petal - async path processing
|
||||
+ /**
|
||||
+ * checks if the path is completely processed in the case of it being computed async
|
||||
+ *
|
||||
+ * @return true if the path is processed
|
||||
+ */
|
||||
+ public boolean isProcessed() {
|
||||
+ return true;
|
||||
+ }
|
||||
+ // Kaiiju end
|
||||
+
|
||||
public void advance() {
|
||||
this.nextNodeIndex++;
|
||||
}
|
||||
@@ -99,6 +110,7 @@ public class Path {
|
||||
}
|
||||
|
||||
public boolean sameAs(@Nullable Path pathentity) {
|
||||
+ if (pathentity == this) return true; // Kaiiju - petal - short circuit
|
||||
if (pathentity == null) {
|
||||
return false;
|
||||
} else if (pathentity.nodes.size() != this.nodes.size()) {
|
||||
diff --git a/net/minecraft/world/level/pathfinder/PathFinder.java b/net/minecraft/world/level/pathfinder/PathFinder.java
|
||||
index c2baadcdceb1df6a881d6f73aa4eb4dd264bcdfe..61912c67611ded5a8f34e0c55d3d3017f144f970 100644
|
||||
--- a/net/minecraft/world/level/pathfinder/PathFinder.java
|
||||
+++ b/net/minecraft/world/level/pathfinder/PathFinder.java
|
||||
@@ -22,10 +22,18 @@ public class PathFinder {
|
||||
public final NodeEvaluator nodeEvaluator;
|
||||
private static final boolean DEBUG = false;
|
||||
private final BinaryHeap openSet = new BinaryHeap();
|
||||
+ private final @Nullable org.dreeam.leaf.async.path.NodeEvaluatorGenerator nodeEvaluatorGenerator; // Kaiiju - petal - we use this later to generate an evaluator
|
||||
|
||||
- public PathFinder(NodeEvaluator nodeEvaluator, int maxVisitedNodes) {
|
||||
+ public PathFinder(NodeEvaluator nodeEvaluator, int maxVisitedNodes, @Nullable org.dreeam.leaf.async.path.NodeEvaluatorGenerator nodeEvaluatorGenerator) { // Kaiiju - petal - add nodeEvaluatorGenerator
|
||||
this.nodeEvaluator = nodeEvaluator;
|
||||
this.maxVisitedNodes = maxVisitedNodes;
|
||||
+ // Kaiiju start - petal - support nodeEvaluatorgenerators
|
||||
+ this.nodeEvaluatorGenerator = nodeEvaluatorGenerator;
|
||||
+ }
|
||||
+
|
||||
+ public PathFinder(NodeEvaluator nodeEvaluator, int maxVisitedNodes) {
|
||||
+ this(nodeEvaluator, maxVisitedNodes, null);
|
||||
+ // Kaiiju end
|
||||
}
|
||||
|
||||
public void setMaxVisitedNodes(int maxVisitedNodes) {
|
||||
@@ -34,26 +42,63 @@ public class PathFinder {
|
||||
|
||||
@Nullable
|
||||
public Path findPath(PathNavigationRegion region, Mob mob, Set<BlockPos> targetPositions, float maxRange, int accuracy, float searchDepthMultiplier) {
|
||||
- this.openSet.clear();
|
||||
- this.nodeEvaluator.prepare(region, mob);
|
||||
- Node start = this.nodeEvaluator.getStart();
|
||||
+ if (!org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled)
|
||||
+ this.openSet.clear(); // Kaiiju - petal - it's always cleared in processPath
|
||||
+ // Kaiiju start - petal - use a generated evaluator if we have one otherwise run sync
|
||||
+ NodeEvaluator nodeEvaluator = this.nodeEvaluatorGenerator == null
|
||||
+ ? this.nodeEvaluator
|
||||
+ : org.dreeam.leaf.async.path.NodeEvaluatorCache.takeNodeEvaluator(this.nodeEvaluatorGenerator, this.nodeEvaluator);
|
||||
+ nodeEvaluator.prepare(region, mob);
|
||||
+ Node start = nodeEvaluator.getStart();
|
||||
+ // Kaiiju end
|
||||
if (start == null) {
|
||||
+ org.dreeam.leaf.async.path.NodeEvaluatorCache.removeNodeEvaluator(nodeEvaluator); // Kaiiju - petal - handle nodeEvaluatorGenerator
|
||||
return null;
|
||||
} else {
|
||||
// Paper start - Perf: remove streams and optimize collection
|
||||
List<Map.Entry<Target, BlockPos>> map = Lists.newArrayList();
|
||||
for (BlockPos pos : targetPositions) {
|
||||
- map.add(new java.util.AbstractMap.SimpleEntry<>(this.nodeEvaluator.getTarget(pos.getX(), pos.getY(), pos.getZ()), pos));
|
||||
+ map.add(new java.util.AbstractMap.SimpleEntry<>(nodeEvaluator.getTarget(pos.getX(), pos.getY(), pos.getZ()), pos)); // Kaiiju - petal - handle nodeEvaluatorGenerator
|
||||
}
|
||||
// Paper end - Perf: remove streams and optimize collection
|
||||
- Path path = this.findPath(start, map, maxRange, accuracy, searchDepthMultiplier);
|
||||
- this.nodeEvaluator.done();
|
||||
- return path;
|
||||
+ // Kaiiju start - petal - async path processing
|
||||
+ if (this.nodeEvaluatorGenerator == null) {
|
||||
+ // run sync :(
|
||||
+ org.dreeam.leaf.async.path.NodeEvaluatorCache.removeNodeEvaluator(nodeEvaluator);
|
||||
+ return this.findPath(start, map, maxRange, accuracy, searchDepthMultiplier);
|
||||
+ }
|
||||
+
|
||||
+ return new org.dreeam.leaf.async.path.AsyncPath(Lists.newArrayList(), targetPositions, () -> {
|
||||
+ try {
|
||||
+ return this.processPath(nodeEvaluator, start, map, maxRange, accuracy, searchDepthMultiplier);
|
||||
+ } catch (Exception e) {
|
||||
+ e.printStackTrace();
|
||||
+ return null;
|
||||
+ } finally {
|
||||
+ nodeEvaluator.done();
|
||||
+ org.dreeam.leaf.async.path.NodeEvaluatorCache.returnNodeEvaluator(nodeEvaluator);
|
||||
+ }
|
||||
+ });
|
||||
+ // Kaiiju end
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Path findPath(Node node, List<Map.Entry<Target, BlockPos>> positions, float maxRange, int accuracy, float searchDepthMultiplier) { // Paper - optimize collection
|
||||
+ // Kaiiju start - petal - split pathfinding into the original sync method for compat and processing for delaying
|
||||
+ try {
|
||||
+ return this.processPath(this.nodeEvaluator, node, positions, maxRange, accuracy, searchDepthMultiplier);
|
||||
+ } catch (Exception e) {
|
||||
+ e.printStackTrace();
|
||||
+ return null;
|
||||
+ } finally {
|
||||
+ this.nodeEvaluator.done();
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private synchronized @org.jetbrains.annotations.NotNull Path processPath(NodeEvaluator nodeEvaluator, Node node, List<Map.Entry<Target, BlockPos>> positions, float maxRange, int accuracy, float searchDepthMultiplier) { // sync to only use the caching functions in this class on a single thread
|
||||
+ org.apache.commons.lang3.Validate.isTrue(!positions.isEmpty()); // ensure that we have at least one position, which means we'll always return a path
|
||||
+ // Kaiiju end
|
||||
// Set<Target> set = targetPositions.keySet(); // Paper
|
||||
node.g = 0.0F;
|
||||
node.h = this.getBestH(node, positions); // Paper - optimize collection
|
||||
@@ -89,7 +134,7 @@ public class PathFinder {
|
||||
}
|
||||
|
||||
if (!(node1.distanceTo(node) >= maxRange)) {
|
||||
- int neighbors = this.nodeEvaluator.getNeighbors(this.neighbors, node1);
|
||||
+ int neighbors = nodeEvaluator.getNeighbors(this.neighbors, node1); // Kaiiju - petal - use provided nodeEvaluator
|
||||
|
||||
for (int i2 = 0; i2 < neighbors; i2++) {
|
||||
Node node2 = this.neighbors[i2];
|
||||
@@ -123,6 +168,7 @@ public class PathFinder {
|
||||
best = path;
|
||||
}
|
||||
}
|
||||
+ //noinspection ConstantConditions // Kaiiju - petal - ignore this warning, we know that the above loop always runs at least once since positions is not empty
|
||||
return best;
|
||||
// Paper end - Perf: remove streams and optimize collection
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: peaches94 <peachescu94@gmail.com>
|
||||
Date: Sun, 10 Jul 2022 13:29:20 -0500
|
||||
Subject: [PATCH] Petal: reduce work done by game event system
|
||||
|
||||
Original license: GPL v3
|
||||
Original project: https://github.com/Bloom-host/Petal
|
||||
|
||||
1. going into game event dispatching can be expensive so run the checks before dispatching
|
||||
|
||||
2. EuclideanGameEventListenerRegistry is not used concurrently so we ban that usage for improved performance with allays
|
||||
|
||||
diff --git a/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java b/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java
|
||||
index 2627583ab12d886b1fba0b1d1e599f942926b499..440d70811d96f97d3463c6aff131cbc5bd588254 100644
|
||||
--- a/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java
|
||||
+++ b/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java
|
||||
@@ -65,7 +65,7 @@ public class SculkCatalystBlockEntity extends BlockEntity implements GameEventLi
|
||||
return this.catalystListener;
|
||||
}
|
||||
|
||||
- public static class CatalystListener implements GameEventListener {
|
||||
+ public class CatalystListener implements GameEventListener { // Leaf - petal - reduce work done by game event system
|
||||
public static final int PULSE_TICKS = 8;
|
||||
final SculkSpreader sculkSpreader;
|
||||
private final BlockState blockState;
|
||||
@@ -127,6 +127,13 @@ public class SculkCatalystBlockEntity extends BlockEntity implements GameEventLi
|
||||
level.playSound(null, pos, SoundEvents.SCULK_CATALYST_BLOOM, SoundSource.BLOCKS, 2.0F, 0.6F + random.nextFloat() * 0.4F);
|
||||
}
|
||||
|
||||
+ // Leaf start - petal - reduce work done by game event system
|
||||
+ @Override
|
||||
+ public boolean listensToEvent(GameEvent gameEvent, GameEvent.Context context) {
|
||||
+ return !SculkCatalystBlockEntity.this.isRemoved() && gameEvent == GameEvent.ENTITY_DIE.value() && context.sourceEntity() instanceof LivingEntity;
|
||||
+ }
|
||||
+ // Leaf end - petal - reduce work done by game event system
|
||||
+
|
||||
private void tryAwardItSpreadsAdvancement(Level level, LivingEntity entity) {
|
||||
if (entity.getLastHurtByMob() instanceof ServerPlayer serverPlayer) {
|
||||
DamageSource damageSource = entity.getLastDamageSource() == null
|
||||
diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
index 626e87d9c1862fe0c896172ee240844e50d7902f..f5a627d900298218e8dbbf12d5950dc8a01bd39e 100644
|
||||
--- a/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
@@ -80,7 +80,19 @@ public class LevelChunk extends ChunkAccess {
|
||||
private Supplier<FullChunkStatus> fullStatus;
|
||||
@Nullable
|
||||
private LevelChunk.PostLoadProcessor postLoad;
|
||||
- private final Int2ObjectMap<GameEventListenerRegistry> gameEventListenerRegistrySections;
|
||||
+ // Leaf start - petal - reduce work done by game event system
|
||||
+ private final GameEventListenerRegistry[] gameEventListenerRegistrySections;
|
||||
+ private static final int GAME_EVENT_DISPATCHER_RADIUS = 2;
|
||||
+
|
||||
+ private static int getGameEventSectionIndex(int sectionIndex) {
|
||||
+ return sectionIndex + GAME_EVENT_DISPATCHER_RADIUS;
|
||||
+ }
|
||||
+
|
||||
+ private static int getGameEventSectionLength(int sectionCount) {
|
||||
+ return sectionCount + (GAME_EVENT_DISPATCHER_RADIUS * 2);
|
||||
+ }
|
||||
+
|
||||
+ // Leaf end - petal - reduce work done by game event system
|
||||
private final LevelChunkTicks<Block> blockTicks;
|
||||
private final LevelChunkTicks<Fluid> fluidTicks;
|
||||
private LevelChunk.UnsavedListener unsavedListener = chunkPos -> {};
|
||||
@@ -110,7 +122,7 @@ public class LevelChunk extends ChunkAccess {
|
||||
) {
|
||||
super(pos, data, level, net.minecraft.server.MinecraftServer.getServer().registryAccess().lookupOrThrow(Registries.BIOME), inhabitedTime, sections, blendingData); // Paper - Anti-Xray - The world isn't ready yet, use server singleton for registry
|
||||
this.level = (ServerLevel) level; // CraftBukkit - type
|
||||
- this.gameEventListenerRegistrySections = new Int2ObjectOpenHashMap<>();
|
||||
+ this.gameEventListenerRegistrySections = new GameEventListenerRegistry[getGameEventSectionLength(this.getSectionsCount())]; // Leaf - petal - reduce work done by game event system
|
||||
|
||||
for (Heightmap.Types types : Heightmap.Types.values()) {
|
||||
if (ChunkStatus.FULL.heightmapsAfter().contains(types)) {
|
||||
@@ -206,10 +218,27 @@ public class LevelChunk extends ChunkAccess {
|
||||
|
||||
@Override
|
||||
public GameEventListenerRegistry getListenerRegistry(int sectionY) {
|
||||
- return this.level instanceof ServerLevel serverLevel
|
||||
- ? this.gameEventListenerRegistrySections
|
||||
- .computeIfAbsent(sectionY, i -> new EuclideanGameEventListenerRegistry(serverLevel, sectionY, this::removeGameEventListenerRegistry))
|
||||
- : super.getListenerRegistry(sectionY);
|
||||
+ // Leaf start - petal - reduce work done by game event system
|
||||
+ if (this.level instanceof ServerLevel serverLevel) {
|
||||
+ int sectionIndex = getGameEventSectionIndex(this.getSectionIndexFromSectionY(sectionY));
|
||||
+
|
||||
+ // drop game events that are too far away (32 blocks) from loaded sections
|
||||
+ // this matches the highest radius of game events in the game
|
||||
+ if (sectionIndex < 0 || sectionIndex >= this.gameEventListenerRegistrySections.length) {
|
||||
+ return GameEventListenerRegistry.NOOP;
|
||||
+ }
|
||||
+
|
||||
+ var dispatcher = this.gameEventListenerRegistrySections[sectionIndex];
|
||||
+
|
||||
+ if (dispatcher == null) {
|
||||
+ dispatcher = this.gameEventListenerRegistrySections[sectionIndex] = new EuclideanGameEventListenerRegistry(serverLevel, sectionY, this::removeGameEventListenerRegistry);
|
||||
+ }
|
||||
+
|
||||
+ return dispatcher;
|
||||
+ }
|
||||
+
|
||||
+ return super.getListenerRegistry(sectionY);
|
||||
+ // Leaf end - petal - reduce work done by game event system
|
||||
}
|
||||
|
||||
// Paper start - Perf: Reduce instructions and provide final method
|
||||
@@ -556,7 +585,7 @@ public class LevelChunk extends ChunkAccess {
|
||||
}
|
||||
|
||||
private void removeGameEventListenerRegistry(int sectionY) {
|
||||
- this.gameEventListenerRegistrySections.remove(sectionY);
|
||||
+ this.gameEventListenerRegistrySections[getGameEventSectionIndex(this.getSectionIndexFromSectionY(sectionY))] = null; // Leaf - petal - reduce work done by game event system
|
||||
}
|
||||
|
||||
private void removeBlockEntityTicker(BlockPos pos) {
|
||||
diff --git a/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java b/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java
|
||||
index 5175fc90a1fc61c832c6697997a97ae199b195ac..fc56ef2d5ed813db51e35b635e373b6f8035593b 100644
|
||||
--- a/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java
|
||||
+++ b/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java
|
||||
@@ -14,8 +14,8 @@ import net.minecraft.world.phys.Vec3;
|
||||
|
||||
public class EuclideanGameEventListenerRegistry implements GameEventListenerRegistry {
|
||||
private final List<GameEventListener> listeners = Lists.newArrayList();
|
||||
- private final Set<GameEventListener> listenersToRemove = Sets.newHashSet();
|
||||
- private final List<GameEventListener> listenersToAdd = Lists.newArrayList();
|
||||
+ //private final Set<GameEventListener> listenersToRemove = Sets.newHashSet(); // Leaf - petal - reduce work done by game event system - Not necessary
|
||||
+ //private final List<GameEventListener> listenersToAdd = Lists.newArrayList(); // Leaf - petal - reduce work done by game event system
|
||||
private boolean processing;
|
||||
private final ServerLevel level;
|
||||
private final int sectionY;
|
||||
@@ -35,7 +35,7 @@ public class EuclideanGameEventListenerRegistry implements GameEventListenerRegi
|
||||
@Override
|
||||
public void register(GameEventListener listener) {
|
||||
if (this.processing) {
|
||||
- this.listenersToAdd.add(listener);
|
||||
+ throw new java.util.ConcurrentModificationException(); // Leaf - petal - reduce work done by game event system - Disallow concurrent modification
|
||||
} else {
|
||||
this.listeners.add(listener);
|
||||
}
|
||||
@@ -46,7 +46,7 @@ public class EuclideanGameEventListenerRegistry implements GameEventListenerRegi
|
||||
@Override
|
||||
public void unregister(GameEventListener listener) {
|
||||
if (this.processing) {
|
||||
- this.listenersToRemove.add(listener);
|
||||
+ throw new java.util.ConcurrentModificationException(); // Leaf - petal - reduce work done by game event system - Disallow concurrent modification
|
||||
} else {
|
||||
this.listeners.remove(listener);
|
||||
}
|
||||
@@ -66,7 +66,7 @@ public class EuclideanGameEventListenerRegistry implements GameEventListenerRegi
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
GameEventListener gameEventListener = iterator.next();
|
||||
- if (this.listenersToRemove.remove(gameEventListener)) {
|
||||
+ if (false) { // Leaf - petal - reduce work done by game event system - Disallow concurrent modification
|
||||
iterator.remove();
|
||||
} else {
|
||||
Optional<Vec3> postableListenerPosition = getPostableListenerPosition(this.level, pos, gameEventListener);
|
||||
@@ -80,6 +80,8 @@ public class EuclideanGameEventListenerRegistry implements GameEventListenerRegi
|
||||
this.processing = false;
|
||||
}
|
||||
|
||||
+ // Leaf start - petal - reduce work done by game event system
|
||||
+ /*
|
||||
if (!this.listenersToAdd.isEmpty()) {
|
||||
this.listeners.addAll(this.listenersToAdd);
|
||||
this.listenersToAdd.clear();
|
||||
@@ -89,6 +91,8 @@ public class EuclideanGameEventListenerRegistry implements GameEventListenerRegi
|
||||
this.listeners.removeAll(this.listenersToRemove);
|
||||
this.listenersToRemove.clear();
|
||||
}
|
||||
+ */
|
||||
+ // Leaf end - petal - reduce work done by game event system
|
||||
|
||||
return flag;
|
||||
}
|
||||
diff --git a/net/minecraft/world/level/gameevent/GameEventDispatcher.java b/net/minecraft/world/level/gameevent/GameEventDispatcher.java
|
||||
index b06ded98c01cd2a71d52b96070a6b23da1e40f9c..a781038d42166e1dd1bfee0fbd5c2db8ec1d08c0 100644
|
||||
--- a/net/minecraft/world/level/gameevent/GameEventDispatcher.java
|
||||
+++ b/net/minecraft/world/level/gameevent/GameEventDispatcher.java
|
||||
@@ -38,6 +38,7 @@ public class GameEventDispatcher {
|
||||
int sectionPosCoord5 = SectionPos.blockToSectionCoord(blockPos.getZ() + notificationRadius);
|
||||
List<GameEvent.ListenerInfo> list = new ArrayList<>();
|
||||
GameEventListenerRegistry.ListenerVisitor listenerVisitor = (listener, pos1) -> {
|
||||
+ if (!listener.listensToEvent(gameEvent.value(), context)) return; // Leaf - petal - reduce work done by game event system - If they don't listen, ignore
|
||||
if (listener.getDeliveryMode() == GameEventListener.DeliveryMode.BY_DISTANCE) {
|
||||
list.add(new GameEvent.ListenerInfo(gameEvent, pos, context, listener, pos1));
|
||||
} else {
|
||||
diff --git a/net/minecraft/world/level/gameevent/GameEventListener.java b/net/minecraft/world/level/gameevent/GameEventListener.java
|
||||
index 5a31b5f1e75dd7b412ab577ea6621b7e87fc0590..4d991ab3290646ec3fd6645154abfa5b4e42d00a 100644
|
||||
--- a/net/minecraft/world/level/gameevent/GameEventListener.java
|
||||
+++ b/net/minecraft/world/level/gameevent/GameEventListener.java
|
||||
@@ -23,4 +23,11 @@ public interface GameEventListener {
|
||||
public interface Provider<T extends GameEventListener> {
|
||||
T getListener();
|
||||
}
|
||||
+
|
||||
+ // Leaf start - petal - reduce work done by game event system
|
||||
+ // Add check for seeing if this listener cares about an event
|
||||
+ default boolean listensToEvent(GameEvent gameEvent, GameEvent.Context context) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ // Leaf end - petal - reduce work done by game event system
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com>
|
||||
Date: Mon, 11 Sep 2023 15:47:19 -0400
|
||||
Subject: [PATCH] Reduce canSee work
|
||||
|
||||
Co-authored by: Martijn Muijsers <martijnmuijsers@live.nl>
|
||||
Co-authored by: MachineBreaker <machinebreaker>
|
||||
|
||||
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
|
||||
index 270720ac00b0dc4ccbe08b789728dd09d0a9f1e3..c0c0c095154a8c7910df27ee4f0641ac60df637c 100644
|
||||
--- a/net/minecraft/world/level/Level.java
|
||||
+++ b/net/minecraft/world/level/Level.java
|
||||
@@ -308,17 +308,19 @@ public abstract class Level implements LevelAccessor, UUIDLookup<Entity>, AutoCl
|
||||
for (int i = 0, len = entities.size(); i < len; ++i) {
|
||||
Entity entity = entities.get(i);
|
||||
|
||||
- if (checkCanSee && source instanceof net.minecraft.server.level.ServerPlayer && entity instanceof net.minecraft.server.level.ServerPlayer
|
||||
- && !((net.minecraft.server.level.ServerPlayer) source).getBukkitEntity().canSee(((net.minecraft.server.level.ServerPlayer) entity).getBukkitEntity())) {
|
||||
- continue;
|
||||
- }
|
||||
-
|
||||
+ // Leaf start - Reduce canSee work - move up
|
||||
// !entity1.isRemoved() && entity1.blocksBuilding && (entity == null || !entity1.isPassengerOfSameVehicle(entity))
|
||||
// elide the last check since vanilla calls with entity = null
|
||||
// only we care about the source for the canSee check
|
||||
if (entity.isRemoved() || !entity.blocksBuilding) {
|
||||
continue;
|
||||
}
|
||||
+ // Leaf end - Reduce canSee work - move up
|
||||
+
|
||||
+ if (checkCanSee && source instanceof net.minecraft.server.level.ServerPlayer && entity instanceof net.minecraft.server.level.ServerPlayer
|
||||
+ && !((net.minecraft.server.level.ServerPlayer) source).getBukkitEntity().canSee(((net.minecraft.server.level.ServerPlayer) entity).getBukkitEntity())) {
|
||||
+ continue;
|
||||
+ }
|
||||
|
||||
if (net.minecraft.world.phys.shapes.Shapes.joinIsNotEmpty(collisionShape, net.minecraft.world.phys.shapes.Shapes.create(entity.getBoundingBox()), net.minecraft.world.phys.shapes.BooleanOp.AND)) {
|
||||
return false;
|
||||
@@ -0,0 +1,20 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: pixelNightly <pixelnightly@proton.me>
|
||||
Date: Mon, 4 Dec 2023 16:11:36 +0200
|
||||
Subject: [PATCH] Fix sprint glitch
|
||||
|
||||
|
||||
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
|
||||
index 08c2021a43f626ae142d38d0d0492bffeb1b346b..e65914c2623197031d50508af5c45a4db6b98836 100644
|
||||
--- a/net/minecraft/world/entity/LivingEntity.java
|
||||
+++ b/net/minecraft/world/entity/LivingEntity.java
|
||||
@@ -1358,7 +1358,8 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
||||
player.setRealHealth(health);
|
||||
}
|
||||
|
||||
- player.updateScaledHealth(false);
|
||||
+ //player.updateScaledHealth(false); // Leaf - Fix sprint glitch - commented out
|
||||
+ this.entityData.set(LivingEntity.DATA_HEALTH_ID, player.getScaledHealth()); // Leaf - Fix sprint glitch
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
48
leaf-server/paper-patches/features/0024-Remove-Timings.patch
Normal file
48
leaf-server/paper-patches/features/0024-Remove-Timings.patch
Normal file
@@ -0,0 +1,48 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com>
|
||||
Date: Sat, 23 Sep 2023 03:12:14 -0400
|
||||
Subject: [PATCH] Remove Timings
|
||||
|
||||
Completely remove the Timings, since it wastes too much performance. Use Spark instead.
|
||||
|
||||
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 7ce9ebba8ce304d1f3f21d4f15ee5f3560d7700b..4c003acccdd2dd17918b15316001e52e7670123e 100644
|
||||
--- a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java
|
||||
+++ b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java
|
||||
@@ -1,6 +1,5 @@
|
||||
package io.papermc.paper.plugin.manager;
|
||||
|
||||
-import co.aikar.timings.TimedEventExecutor;
|
||||
import com.destroystokyo.paper.event.server.ServerExceptionEvent;
|
||||
import com.destroystokyo.paper.exception.ServerEventException;
|
||||
import com.google.common.collect.Sets;
|
||||
@@ -95,7 +94,6 @@ class PaperEventManager {
|
||||
throw new IllegalPluginAccessException("Plugin attempted to register " + event + " while not enabled");
|
||||
}
|
||||
|
||||
- executor = new TimedEventExecutor(executor, plugin, null, event);
|
||||
this.getEventListeners(event).register(new RegisteredListener(listener, executor, priority, plugin, ignoreCancelled));
|
||||
}
|
||||
|
||||
@@ -182,7 +180,7 @@ class PaperEventManager {
|
||||
}
|
||||
}
|
||||
|
||||
- EventExecutor executor = new TimedEventExecutor(EventExecutor.create(method, eventClass), plugin, method, eventClass);
|
||||
+ EventExecutor executor = EventExecutor.create(method, eventClass); // Leaf - Remove Timings
|
||||
eventSet.add(new RegisteredListener(listener, executor, eh.priority(), plugin, eh.ignoreCancelled()));
|
||||
}
|
||||
return ret;
|
||||
diff --git a/src/main/java/io/papermc/paper/plugin/manager/PaperPluginManagerImpl.java b/src/main/java/io/papermc/paper/plugin/manager/PaperPluginManagerImpl.java
|
||||
index 097500a59336db1bbfffcd1aa4cff7a8586e46ec..f06076864582ed153c6154fd7f3e9101bf21c4ac 100644
|
||||
--- a/src/main/java/io/papermc/paper/plugin/manager/PaperPluginManagerImpl.java
|
||||
+++ b/src/main/java/io/papermc/paper/plugin/manager/PaperPluginManagerImpl.java
|
||||
@@ -232,7 +232,7 @@ public class PaperPluginManagerImpl implements PluginManager, DependencyContext
|
||||
|
||||
@Override
|
||||
public boolean useTimings() {
|
||||
- return co.aikar.timings.Timings.isTimingsEnabled();
|
||||
+ return false; // Leaf - Remove Timings
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -0,0 +1,44 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: nostalgic853 <yuu8583@proton.me>
|
||||
Date: Sun, 20 Nov 2022 00:20:00 +0800
|
||||
Subject: [PATCH] KeYi: Player Skull API
|
||||
|
||||
Original license: MIT
|
||||
Original project: https://github.com/KeYiMC/KeYi
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
||||
index e1f26ff10f83ea28b09dfba5b02caea182a8f445..d81979c51c81ba08cfb3a401b3f7fde84f492065 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
||||
@@ -3593,4 +3593,31 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
public void setSimplifyContainerDesyncCheck(final boolean simplifyContainerDesyncCheck) {
|
||||
this.simplifyContainerDesyncCheck = simplifyContainerDesyncCheck;
|
||||
}
|
||||
+
|
||||
+ // Leaf start - KeYi - Player Skull API
|
||||
+ @Override
|
||||
+ public ItemStack getSkull() {
|
||||
+ ItemStack skull = new ItemStack(Material.PLAYER_HEAD, 1);
|
||||
+ org.bukkit.inventory.meta.SkullMeta meta = (org.bukkit.inventory.meta.SkullMeta) skull.getItemMeta();
|
||||
+
|
||||
+ meta.setOwningPlayer(this);
|
||||
+ skull.setItemMeta(meta);
|
||||
+
|
||||
+ return skull;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ @Deprecated(forRemoval = true)
|
||||
+ public CompletableFuture<ItemStack> getSkullAsynchronously() {
|
||||
+ org.apache.logging.log4j.LogManager.getLogger("Leaf")
|
||||
+ .warn("You should not use this method: Player#getSkullAsynchronously(), cause low performance, " +
|
||||
+ "and will be removed in the future.");
|
||||
+ java.util.concurrent.ExecutorService executorService = java.util.concurrent.Executors.newCachedThreadPool();
|
||||
+
|
||||
+ CompletableFuture<ItemStack> future = (CompletableFuture<ItemStack>) executorService.submit(this::getSkull);
|
||||
+ executorService.shutdown();
|
||||
+
|
||||
+ return future;
|
||||
+ }
|
||||
+ // Leaf end - KeYi - Player Skull API
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Cryptite <cryptite@gmail.com>
|
||||
Date: Sat, 13 Aug 2022 08:58:14 -0500
|
||||
Subject: [PATCH] Slice: Smooth Teleports
|
||||
|
||||
Original license: MIT
|
||||
Original project: https://github.com/Cryptite/Slice
|
||||
|
||||
Co-authored-by: HaHaWTH <102713261+HaHaWTH@users.noreply.github.com>
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
||||
index d81979c51c81ba08cfb3a401b3f7fde84f492065..905cea7470f7bb8dee27bbfc4d267d46c28fe21b 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
||||
@@ -1361,6 +1361,25 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
// Paper end - Teleportation API
|
||||
}
|
||||
|
||||
+ // Slice start
|
||||
+ @Override
|
||||
+ public void teleportWithoutRespawn(Location location) {
|
||||
+ ServerPlayer serverPlayer = getHandle();
|
||||
+ serverPlayer.smoothWorldTeleport = true;
|
||||
+ teleport(location);
|
||||
+ serverPlayer.smoothWorldTeleport = false;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean teleportWithoutRespawnOptionally(Location location) {
|
||||
+ ServerPlayer serverPlayer = getHandle();
|
||||
+ serverPlayer.smoothWorldTeleport = true;
|
||||
+ boolean teleportResult = teleport(location);
|
||||
+ serverPlayer.smoothWorldTeleport = false;
|
||||
+ return teleportResult;
|
||||
+ }
|
||||
+ // Slice end
|
||||
+
|
||||
@Override
|
||||
public boolean teleport(Location location, PlayerTeleportEvent.TeleportCause cause) {
|
||||
// Paper start - Teleport API
|
||||
@@ -0,0 +1,30 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: lilingfengdev <145678359+lilingfengdev@users.noreply.github.com>
|
||||
Date: Wed, 17 Jan 2024 18:16:38 +0800
|
||||
Subject: [PATCH] Skip event if no listeners
|
||||
|
||||
|
||||
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 4c003acccdd2dd17918b15316001e52e7670123e..db031298c2090eb36032de4b52335c62186e4cfb 100644
|
||||
--- a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java
|
||||
+++ b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java
|
||||
@@ -35,15 +35,16 @@ class PaperEventManager {
|
||||
|
||||
// SimplePluginManager
|
||||
public void callEvent(@NotNull Event event) {
|
||||
+ // Leaf start - Skip event if no listeners
|
||||
+ RegisteredListener[] listeners = event.getHandlers().getRegisteredListeners();
|
||||
+ if (listeners.length == 0) return;
|
||||
+ // Leaf end - Skip event if no listeners
|
||||
if (event.isAsynchronous() && this.server.isPrimaryThread()) {
|
||||
throw new IllegalStateException(event.getEventName() + " may only be triggered asynchronously.");
|
||||
} else if (!event.isAsynchronous() && !this.server.isPrimaryThread() && !this.server.isStopping()) {
|
||||
throw new IllegalStateException(event.getEventName() + " may only be triggered synchronously.");
|
||||
}
|
||||
|
||||
- HandlerList handlers = event.getHandlers();
|
||||
- RegisteredListener[] listeners = handlers.getRegisteredListeners();
|
||||
-
|
||||
for (RegisteredListener registration : listeners) {
|
||||
if (!registration.getPlugin().isEnabled()) {
|
||||
continue;
|
||||
@@ -0,0 +1,288 @@
|
||||
package org.dreeam.leaf.async.path;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.pathfinder.Node;
|
||||
import net.minecraft.world.level.pathfinder.Path;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* i'll be using this to represent a path that not be processed yet!
|
||||
*/
|
||||
public class AsyncPath extends Path {
|
||||
|
||||
/**
|
||||
* marks whether this async path has been processed
|
||||
*/
|
||||
private volatile PathProcessState processState = PathProcessState.WAITING;
|
||||
|
||||
/**
|
||||
* runnables waiting for this to be processed
|
||||
*/
|
||||
private final List<Runnable> postProcessing = new ArrayList<>(0);
|
||||
|
||||
/**
|
||||
* a list of positions that this path could path towards
|
||||
*/
|
||||
private final Set<BlockPos> positions;
|
||||
|
||||
/**
|
||||
* the supplier of the real processed path
|
||||
*/
|
||||
private final Supplier<Path> pathSupplier;
|
||||
|
||||
/*
|
||||
* Processed values
|
||||
*/
|
||||
|
||||
/**
|
||||
* this is a reference to the nodes list in the parent `Path` object
|
||||
*/
|
||||
private final List<Node> nodes;
|
||||
/**
|
||||
* the block we're trying to path to
|
||||
* <p>
|
||||
* while processing, we have no idea where this is so consumers of `Path` should check that the path is processed before checking the target block
|
||||
*/
|
||||
private @Nullable BlockPos target;
|
||||
/**
|
||||
* how far we are to the target
|
||||
* <p>
|
||||
* while processing, the target could be anywhere but theoretically we're always "close" to a theoretical target so default is 0
|
||||
*/
|
||||
private float distToTarget = 0;
|
||||
/**
|
||||
* whether we can reach the target
|
||||
* <p>
|
||||
* while processing, we can always theoretically reach the target so default is true
|
||||
*/
|
||||
private boolean canReach = true;
|
||||
|
||||
public AsyncPath(@NotNull List<Node> emptyNodeList, @NotNull Set<BlockPos> positions, @NotNull Supplier<Path> pathSupplier) {
|
||||
//noinspection ConstantConditions
|
||||
super(emptyNodeList, null, false);
|
||||
|
||||
this.nodes = emptyNodeList;
|
||||
this.positions = positions;
|
||||
this.pathSupplier = pathSupplier;
|
||||
|
||||
AsyncPathProcessor.queue(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProcessed() {
|
||||
return this.processState == PathProcessState.COMPLETED;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the future representing the processing state of this path
|
||||
*/
|
||||
public synchronized void postProcessing(@NotNull Runnable runnable) {
|
||||
if (isProcessed()) {
|
||||
runnable.run();
|
||||
} else {
|
||||
this.postProcessing.add(runnable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* an easy way to check if this processing path is the same as an attempted new path
|
||||
*
|
||||
* @param positions - the positions to compare against
|
||||
* @return true if we are processing the same positions
|
||||
*/
|
||||
public boolean hasSameProcessingPositions(final Set<BlockPos> positions) {
|
||||
if (this.positions.size() != positions.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.positions.containsAll(positions);
|
||||
}
|
||||
|
||||
/**
|
||||
* starts processing this path
|
||||
*/
|
||||
public synchronized void process() {
|
||||
if (this.processState == PathProcessState.COMPLETED ||
|
||||
this.processState == PathProcessState.PROCESSING) {
|
||||
return;
|
||||
}
|
||||
|
||||
processState = PathProcessState.PROCESSING;
|
||||
|
||||
final Path bestPath = this.pathSupplier.get();
|
||||
|
||||
this.nodes.addAll(bestPath.nodes); // we mutate this list to reuse the logic in Path
|
||||
this.target = bestPath.getTarget();
|
||||
this.distToTarget = bestPath.getDistToTarget();
|
||||
this.canReach = bestPath.canReach();
|
||||
|
||||
processState = PathProcessState.COMPLETED;
|
||||
|
||||
for (Runnable runnable : this.postProcessing) {
|
||||
runnable.run();
|
||||
} // Run tasks after processing
|
||||
}
|
||||
|
||||
/**
|
||||
* if this path is accessed while it hasn't processed, just process it in-place
|
||||
*/
|
||||
private void checkProcessed() {
|
||||
if (this.processState == PathProcessState.WAITING ||
|
||||
this.processState == PathProcessState.PROCESSING) { // Block if we are on processing
|
||||
this.process();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* overrides we need for final fields that we cannot modify after processing
|
||||
*/
|
||||
|
||||
@Override
|
||||
public @NotNull BlockPos getTarget() {
|
||||
this.checkProcessed();
|
||||
|
||||
return this.target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDistToTarget() {
|
||||
this.checkProcessed();
|
||||
|
||||
return this.distToTarget;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canReach() {
|
||||
this.checkProcessed();
|
||||
|
||||
return this.canReach;
|
||||
}
|
||||
|
||||
/*
|
||||
* overrides to ensure we're processed first
|
||||
*/
|
||||
|
||||
@Override
|
||||
public boolean isDone() {
|
||||
return this.processState == PathProcessState.COMPLETED && super.isDone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void advance() {
|
||||
this.checkProcessed();
|
||||
|
||||
super.advance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean notStarted() {
|
||||
this.checkProcessed();
|
||||
|
||||
return super.notStarted();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Node getEndNode() {
|
||||
this.checkProcessed();
|
||||
|
||||
return super.getEndNode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getNode(int index) {
|
||||
this.checkProcessed();
|
||||
|
||||
return super.getNode(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void truncateNodes(int length) {
|
||||
this.checkProcessed();
|
||||
|
||||
super.truncateNodes(length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replaceNode(int index, Node node) {
|
||||
this.checkProcessed();
|
||||
|
||||
super.replaceNode(index, node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNodeCount() {
|
||||
this.checkProcessed();
|
||||
|
||||
return super.getNodeCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNextNodeIndex() {
|
||||
this.checkProcessed();
|
||||
|
||||
return super.getNextNodeIndex();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNextNodeIndex(int nodeIndex) {
|
||||
this.checkProcessed();
|
||||
|
||||
super.setNextNodeIndex(nodeIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec3 getEntityPosAtNode(Entity entity, int index) {
|
||||
this.checkProcessed();
|
||||
|
||||
return super.getEntityPosAtNode(entity, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockPos getNodePos(int index) {
|
||||
this.checkProcessed();
|
||||
|
||||
return super.getNodePos(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec3 getNextEntityPos(Entity entity) {
|
||||
this.checkProcessed();
|
||||
|
||||
return super.getNextEntityPos(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockPos getNextNodePos() {
|
||||
this.checkProcessed();
|
||||
|
||||
return super.getNextNodePos();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getNextNode() {
|
||||
this.checkProcessed();
|
||||
|
||||
return super.getNextNode();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Node getPreviousNode() {
|
||||
this.checkProcessed();
|
||||
|
||||
return super.getPreviousNode();
|
||||
}
|
||||
|
||||
public PathProcessState getProcessState() {
|
||||
return processState;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package org.dreeam.leaf.async.path;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.world.level.pathfinder.Path;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* used to handle the scheduling of async path processing
|
||||
*/
|
||||
public class AsyncPathProcessor {
|
||||
|
||||
private static final String THREAD_PREFIX = "Leaf Async Pathfinding";
|
||||
private static final Logger LOGGER = LogManager.getLogger(THREAD_PREFIX);
|
||||
private static long lastWarnMillis = System.currentTimeMillis();
|
||||
private static final ThreadPoolExecutor pathProcessingExecutor = new ThreadPoolExecutor(
|
||||
1,
|
||||
org.dreeam.leaf.config.modules.async.AsyncPathfinding.asyncPathfindingMaxThreads,
|
||||
org.dreeam.leaf.config.modules.async.AsyncPathfinding.asyncPathfindingKeepalive, TimeUnit.SECONDS,
|
||||
getQueueImpl(),
|
||||
new ThreadFactoryBuilder()
|
||||
.setNameFormat(THREAD_PREFIX + " Thread - %d")
|
||||
.setPriority(Thread.NORM_PRIORITY - 2)
|
||||
.build(),
|
||||
new RejectedTaskHandler()
|
||||
);
|
||||
|
||||
private static class RejectedTaskHandler implements RejectedExecutionHandler {
|
||||
@Override
|
||||
public void rejectedExecution(Runnable rejectedTask, ThreadPoolExecutor executor) {
|
||||
BlockingQueue<Runnable> workQueue = executor.getQueue();
|
||||
if (!executor.isShutdown()) {
|
||||
switch (org.dreeam.leaf.config.modules.async.AsyncPathfinding.asyncPathfindingRejectPolicy) {
|
||||
case FLUSH_ALL -> {
|
||||
if (!workQueue.isEmpty()) {
|
||||
List<Runnable> pendingTasks = new ArrayList<>(workQueue.size());
|
||||
|
||||
workQueue.drainTo(pendingTasks);
|
||||
|
||||
for (Runnable pendingTask : pendingTasks) {
|
||||
pendingTask.run();
|
||||
}
|
||||
}
|
||||
rejectedTask.run();
|
||||
}
|
||||
case CALLER_RUNS -> rejectedTask.run();
|
||||
}
|
||||
}
|
||||
|
||||
if (System.currentTimeMillis() - lastWarnMillis > 30000L) {
|
||||
LOGGER.warn("Async pathfinding processor is busy! Pathfinding tasks will be treated as policy defined in config. Increasing max-threads in Leaf config may help.");
|
||||
lastWarnMillis = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static CompletableFuture<Void> queue(@NotNull AsyncPath path) {
|
||||
return CompletableFuture.runAsync(path::process, pathProcessingExecutor)
|
||||
.orTimeout(60L, TimeUnit.SECONDS)
|
||||
.exceptionally(throwable -> {
|
||||
if (throwable instanceof TimeoutException e) {
|
||||
LOGGER.warn("Async Pathfinding process timed out", e);
|
||||
} else LOGGER.warn("Error occurred while processing async path", throwable);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* takes a possibly unprocessed path, and waits until it is completed
|
||||
* the consumer will be immediately invoked if the path is already processed
|
||||
* the consumer will always be called on the main thread
|
||||
*
|
||||
* @param path a path to wait on
|
||||
* @param afterProcessing a consumer to be called
|
||||
*/
|
||||
public static void awaitProcessing(@Nullable Path path, Consumer<@Nullable Path> afterProcessing) {
|
||||
if (path != null && !path.isProcessed() && path instanceof AsyncPath asyncPath) {
|
||||
asyncPath.postProcessing(() ->
|
||||
MinecraftServer.getServer().scheduleOnMain(() -> afterProcessing.accept(path))
|
||||
);
|
||||
} else {
|
||||
afterProcessing.accept(path);
|
||||
}
|
||||
}
|
||||
|
||||
private static BlockingQueue<Runnable> getQueueImpl() {
|
||||
final int queueCapacity = org.dreeam.leaf.config.modules.async.AsyncPathfinding.asyncPathfindingQueueSize;
|
||||
|
||||
return new LinkedBlockingQueue<>(queueCapacity);
|
||||
}}
|
||||
@@ -0,0 +1,46 @@
|
||||
package org.dreeam.leaf.async.path;
|
||||
|
||||
import ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue;
|
||||
import net.minecraft.world.level.pathfinder.NodeEvaluator;
|
||||
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class NodeEvaluatorCache {
|
||||
|
||||
private static final Map<NodeEvaluatorFeatures, MultiThreadedQueue<NodeEvaluator>> threadLocalNodeEvaluators = new ConcurrentHashMap<>();
|
||||
private static final Map<NodeEvaluator, NodeEvaluatorGenerator> nodeEvaluatorToGenerator = new ConcurrentHashMap<>();
|
||||
|
||||
private static @NotNull Queue<NodeEvaluator> getQueueForFeatures(@NotNull NodeEvaluatorFeatures nodeEvaluatorFeatures) {
|
||||
return threadLocalNodeEvaluators.computeIfAbsent(nodeEvaluatorFeatures, key -> new MultiThreadedQueue<>());
|
||||
}
|
||||
|
||||
public static @NotNull NodeEvaluator takeNodeEvaluator(@NotNull NodeEvaluatorGenerator generator, @NotNull NodeEvaluator localNodeEvaluator) {
|
||||
final NodeEvaluatorFeatures nodeEvaluatorFeatures = NodeEvaluatorFeatures.fromNodeEvaluator(localNodeEvaluator);
|
||||
NodeEvaluator nodeEvaluator = getQueueForFeatures(nodeEvaluatorFeatures).poll();
|
||||
|
||||
if (nodeEvaluator == null) {
|
||||
nodeEvaluator = generator.generate(nodeEvaluatorFeatures);
|
||||
}
|
||||
|
||||
nodeEvaluatorToGenerator.put(nodeEvaluator, generator);
|
||||
|
||||
return nodeEvaluator;
|
||||
}
|
||||
|
||||
public static void returnNodeEvaluator(@NotNull NodeEvaluator nodeEvaluator) {
|
||||
final NodeEvaluatorGenerator generator = nodeEvaluatorToGenerator.remove(nodeEvaluator);
|
||||
Validate.notNull(generator, "NodeEvaluator already returned");
|
||||
|
||||
final NodeEvaluatorFeatures nodeEvaluatorFeatures = NodeEvaluatorFeatures.fromNodeEvaluator(nodeEvaluator);
|
||||
getQueueForFeatures(nodeEvaluatorFeatures).offer(nodeEvaluator);
|
||||
}
|
||||
|
||||
public static void removeNodeEvaluator(@NotNull NodeEvaluator nodeEvaluator) {
|
||||
nodeEvaluatorToGenerator.remove(nodeEvaluator);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.dreeam.leaf.async.path;
|
||||
|
||||
import net.minecraft.world.level.pathfinder.NodeEvaluator;
|
||||
import net.minecraft.world.level.pathfinder.SwimNodeEvaluator;
|
||||
|
||||
public record NodeEvaluatorFeatures(
|
||||
NodeEvaluatorType type,
|
||||
boolean canPassDoors,
|
||||
boolean canFloat,
|
||||
boolean canWalkOverFences,
|
||||
boolean canOpenDoors,
|
||||
boolean allowBreaching
|
||||
) {
|
||||
public static NodeEvaluatorFeatures fromNodeEvaluator(NodeEvaluator nodeEvaluator) {
|
||||
NodeEvaluatorType type = NodeEvaluatorType.fromNodeEvaluator(nodeEvaluator);
|
||||
boolean canPassDoors = nodeEvaluator.canPassDoors();
|
||||
boolean canFloat = nodeEvaluator.canFloat();
|
||||
boolean canWalkOverFences = nodeEvaluator.canWalkOverFences();
|
||||
boolean canOpenDoors = nodeEvaluator.canOpenDoors();
|
||||
boolean allowBreaching = nodeEvaluator instanceof SwimNodeEvaluator swimNodeEvaluator && swimNodeEvaluator.allowBreaching;
|
||||
return new NodeEvaluatorFeatures(type, canPassDoors, canFloat, canWalkOverFences, canOpenDoors, allowBreaching);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package org.dreeam.leaf.async.path;
|
||||
|
||||
import net.minecraft.world.level.pathfinder.NodeEvaluator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface NodeEvaluatorGenerator {
|
||||
@NotNull NodeEvaluator generate(NodeEvaluatorFeatures nodeEvaluatorFeatures);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.dreeam.leaf.async.path;
|
||||
|
||||
import net.minecraft.world.level.pathfinder.*;
|
||||
|
||||
public enum NodeEvaluatorType {
|
||||
WALK,
|
||||
SWIM,
|
||||
AMPHIBIOUS,
|
||||
FLY;
|
||||
|
||||
public static NodeEvaluatorType fromNodeEvaluator(NodeEvaluator nodeEvaluator) {
|
||||
if (nodeEvaluator instanceof SwimNodeEvaluator) return SWIM;
|
||||
if (nodeEvaluator instanceof FlyNodeEvaluator) return FLY;
|
||||
if (nodeEvaluator instanceof AmphibiousNodeEvaluator) return AMPHIBIOUS;
|
||||
return WALK;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package org.dreeam.leaf.async.path;
|
||||
|
||||
public enum PathProcessState {
|
||||
WAITING,
|
||||
PROCESSING,
|
||||
COMPLETED,
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.dreeam.leaf.async.path;
|
||||
|
||||
import org.dreeam.leaf.config.LeafConfig;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public enum PathfindTaskRejectPolicy {
|
||||
FLUSH_ALL,
|
||||
CALLER_RUNS;
|
||||
|
||||
public static PathfindTaskRejectPolicy fromString(String policy) {
|
||||
try {
|
||||
return PathfindTaskRejectPolicy.valueOf(policy.toUpperCase(Locale.ROOT));
|
||||
} catch (IllegalArgumentException e) {
|
||||
LeafConfig.LOGGER.warn("Invalid pathfind task reject policy: {}, falling back to {}.", policy, FLUSH_ALL.toString());
|
||||
return FLUSH_ALL;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package org.dreeam.leaf.config.modules.async;
|
||||
|
||||
import org.dreeam.leaf.async.path.PathfindTaskRejectPolicy;
|
||||
import org.dreeam.leaf.config.ConfigModules;
|
||||
import org.dreeam.leaf.config.EnumConfigCategory;
|
||||
import org.dreeam.leaf.config.LeafConfig;
|
||||
|
||||
public class AsyncPathfinding extends ConfigModules {
|
||||
|
||||
public String getBasePath() {
|
||||
return EnumConfigCategory.ASYNC.getBaseKeyName() + ".async-pathfinding";
|
||||
}
|
||||
|
||||
public static boolean enabled = false;
|
||||
public static int asyncPathfindingMaxThreads = 0;
|
||||
public static int asyncPathfindingKeepalive = 60;
|
||||
public static int asyncPathfindingQueueSize = 0;
|
||||
public static PathfindTaskRejectPolicy asyncPathfindingRejectPolicy = PathfindTaskRejectPolicy.FLUSH_ALL;
|
||||
|
||||
@Override
|
||||
public void onLoaded() {
|
||||
final int availableProcessors = Runtime.getRuntime().availableProcessors();
|
||||
enabled = config.getBoolean(getBasePath() + ".enabled", enabled);
|
||||
asyncPathfindingMaxThreads = config.getInt(getBasePath() + ".max-threads", asyncPathfindingMaxThreads);
|
||||
asyncPathfindingKeepalive = config.getInt(getBasePath() + ".keepalive", asyncPathfindingKeepalive);
|
||||
asyncPathfindingQueueSize = config.getInt(getBasePath() + ".queue-size", asyncPathfindingQueueSize);
|
||||
|
||||
if (asyncPathfindingMaxThreads < 0)
|
||||
asyncPathfindingMaxThreads = Math.max(availableProcessors + asyncPathfindingMaxThreads, 1);
|
||||
else if (asyncPathfindingMaxThreads == 0)
|
||||
asyncPathfindingMaxThreads = Math.max(availableProcessors / 4, 1);
|
||||
if (!enabled)
|
||||
asyncPathfindingMaxThreads = 0;
|
||||
else
|
||||
LeafConfig.LOGGER.info("Using {} threads for Async Pathfinding", asyncPathfindingMaxThreads);
|
||||
|
||||
if (asyncPathfindingQueueSize <= 0)
|
||||
asyncPathfindingQueueSize = asyncPathfindingMaxThreads * 256;
|
||||
|
||||
asyncPathfindingRejectPolicy = PathfindTaskRejectPolicy.fromString(config.getString(getBasePath() + ".reject-policy", availableProcessors >= 12 && asyncPathfindingQueueSize < 512 ? PathfindTaskRejectPolicy.FLUSH_ALL.toString() : PathfindTaskRejectPolicy.CALLER_RUNS.toString(), config.pickStringRegionBased(
|
||||
"""
|
||||
The policy to use when the queue is full and a new task is submitted.
|
||||
FLUSH_ALL: All pending tasks will be run on server thread.
|
||||
CALLER_RUNS: Newly submitted task will be run on server thread.""",
|
||||
"""
|
||||
当队列满时, 新提交的任务将使用以下策略处理.
|
||||
FLUSH_ALL: 所有等待中的任务都将在主线程上运行.
|
||||
CALLER_RUNS: 新提交的任务将在主线程上运行."""
|
||||
)));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user