fix: cooler pathfinding & fixed tracker

This commit is contained in:
peaches94
2022-07-24 18:01:35 -05:00
parent 6202cf0b40
commit 06e169d3ca
3 changed files with 193 additions and 59 deletions

View File

@@ -6,4 +6,4 @@ group=host.bloom.petal
version=1.19-R0.1-SNAPSHOT version=1.19-R0.1-SNAPSHOT
mcVersion=1.19 mcVersion=1.19
packageVersion=1_19_R1 packageVersion=1_19_R1
purpurRef=8aa5c259c5cb5ebdc0b8e081834aef5aeee0dac6 purpurRef=2b3486efc2478cb2be0a0b3eaeedbee9154303cc

View File

@@ -6,10 +6,10 @@ Subject: [PATCH] feat: async path processing
diff --git a/src/main/java/host/bloom/pathfinding/AsyncPath.java b/src/main/java/host/bloom/pathfinding/AsyncPath.java diff --git a/src/main/java/host/bloom/pathfinding/AsyncPath.java b/src/main/java/host/bloom/pathfinding/AsyncPath.java
new file mode 100644 new file mode 100644
index 0000000000000000000000000000000000000000..c6d9d79707d7953f30e7373ca6e3eeced76de761 index 0000000000000000000000000000000000000000..db264161dfa5ef288f6d79a0031b56f95f17dd9d
--- /dev/null --- /dev/null
+++ b/src/main/java/host/bloom/pathfinding/AsyncPath.java +++ b/src/main/java/host/bloom/pathfinding/AsyncPath.java
@@ -0,0 +1,280 @@ @@ -0,0 +1,288 @@
+package host.bloom.pathfinding; +package host.bloom.pathfinding;
+ +
+import net.minecraft.core.BlockPos; +import net.minecraft.core.BlockPos;
@@ -20,9 +20,9 @@ index 0000000000000000000000000000000000000000..c6d9d79707d7953f30e7373ca6e3eece
+import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Nullable;
+ +
+import java.util.ArrayList;
+import java.util.List; +import java.util.List;
+import java.util.Set; +import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Supplier; +import java.util.function.Supplier;
+ +
+/** +/**
@@ -36,9 +36,9 @@ index 0000000000000000000000000000000000000000..c6d9d79707d7953f30e7373ca6e3eece
+ private volatile boolean processed = false; + private volatile boolean processed = false;
+ +
+ /** + /**
+ * a future representing the processing state of this path + * runnables waiting for this to be processed
+ */ + */
+ private final @NotNull CompletableFuture<Path> processingFuture; + private final List<Runnable> postProcessing = new ArrayList<>(0);
+ +
+ /** + /**
+ * a list of positions that this path could path towards + * a list of positions that this path could path towards
@@ -85,7 +85,7 @@ index 0000000000000000000000000000000000000000..c6d9d79707d7953f30e7373ca6e3eece
+ this.positions = positions; + this.positions = positions;
+ this.pathSupplier = pathSupplier; + this.pathSupplier = pathSupplier;
+ +
+ this.processingFuture = AsyncPathProcessor.queue(this).thenApply((unused) -> this); + AsyncPathProcessor.queue(this);
+ } + }
+ +
+ @Override + @Override
@@ -97,8 +97,12 @@ index 0000000000000000000000000000000000000000..c6d9d79707d7953f30e7373ca6e3eece
+ * returns the future representing the processing state of this path + * returns the future representing the processing state of this path
+ * @return a future + * @return a future
+ */ + */
+ public @NotNull CompletableFuture<Path> getProcessingFuture() { + public synchronized void postProcessing(@NotNull Runnable runnable) {
+ return this.processingFuture; + if (this.processed) {
+ runnable.run();
+ } else {
+ this.postProcessing.add(runnable);
+ }
+ } + }
+ +
+ /** + /**
@@ -131,6 +135,10 @@ index 0000000000000000000000000000000000000000..c6d9d79707d7953f30e7373ca6e3eece
+ this.canReach = bestPath.canReach(); + this.canReach = bestPath.canReach();
+ +
+ this.processed = true; + this.processed = true;
+
+ for (Runnable runnable : this.postProcessing) {
+ runnable.run();
+ }
+ } + }
+ +
+ /** + /**
@@ -292,7 +300,7 @@ index 0000000000000000000000000000000000000000..c6d9d79707d7953f30e7373ca6e3eece
+} +}
diff --git a/src/main/java/host/bloom/pathfinding/AsyncPathProcessor.java b/src/main/java/host/bloom/pathfinding/AsyncPathProcessor.java diff --git a/src/main/java/host/bloom/pathfinding/AsyncPathProcessor.java b/src/main/java/host/bloom/pathfinding/AsyncPathProcessor.java
new file mode 100644 new file mode 100644
index 0000000000000000000000000000000000000000..e900f8fb637c0d69f94c5a24fb17f794513a482b index 0000000000000000000000000000000000000000..421670f967568c10e1e9052d0fc25818538d3b51
--- /dev/null --- /dev/null
+++ b/src/main/java/host/bloom/pathfinding/AsyncPathProcessor.java +++ b/src/main/java/host/bloom/pathfinding/AsyncPathProcessor.java
@@ -0,0 +1,44 @@ @@ -0,0 +1,44 @@
@@ -334,7 +342,7 @@ index 0000000000000000000000000000000000000000..e900f8fb637c0d69f94c5a24fb17f794
+ */ + */
+ public static void awaitProcessing(@Nullable Path path, Consumer<@Nullable Path> afterProcessing) { + public static void awaitProcessing(@Nullable Path path, Consumer<@Nullable Path> afterProcessing) {
+ if (path != null && !path.isProcessed() && path instanceof AsyncPath asyncPath) { + if (path != null && !path.isProcessed() && path instanceof AsyncPath asyncPath) {
+ asyncPath.getProcessingFuture().thenAcceptAsync(afterProcessing, mainThreadExecutor); + asyncPath.postProcessing(() -> mainThreadExecutor.execute(() -> afterProcessing.accept(path)));
+ } else { + } else {
+ afterProcessing.accept(path); + afterProcessing.accept(path);
+ } + }
@@ -446,78 +454,170 @@ index bf3b8ccb3e031e0ad24cd51e28ea8cbd4f8a8030..e0453df8c0fcdb40ef0ed5ae8865d45d
} }
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java b/src/main/java/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java b/src/main/java/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java
index 18364ce4c60172529b10bc9e3a813dcedc4b766f..e6cd04bc4e19a54b1bd621ce1c880e932207bce3 100644 index 18364ce4c60172529b10bc9e3a813dcedc4b766f..b91abb2c5f06b3b81c242f16f738bed450d923b7 100644
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java --- a/src/main/java/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java +++ b/src/main/java/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java
@@ -58,6 +58,7 @@ public class MoveToTargetSink extends Behavior<Mob> { @@ -3,6 +3,7 @@ package net.minecraft.world.entity.ai.behavior;
import com.google.common.collect.ImmutableMap;
import java.util.Optional;
import javax.annotation.Nullable;
+
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Mob;
@@ -16,11 +17,13 @@ import net.minecraft.world.entity.ai.util.DefaultRandomPos;
import net.minecraft.world.level.pathfinder.Path;
import net.minecraft.world.phys.Vec3;
+// petal start
public class MoveToTargetSink extends Behavior<Mob> {
private static final int MAX_COOLDOWN_BEFORE_RETRYING = 40;
private int remainingCooldown;
@Nullable
private Path path;
+ private boolean finishedProcessing; // petal - track when path is processed
@Nullable
private BlockPos lastTargetPos;
private float speedModifier;
@@ -39,11 +42,11 @@ public class MoveToTargetSink extends Behavior<Mob> {
--this.remainingCooldown;
return false;
} else {
+ // petal start - async path processing means we cant know if the path is reachable here
Brain<?> brain = entity.getBrain();
WalkTarget walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET).get();
boolean bl = this.reachedTarget(entity, walkTarget);
- if (!bl && this.tryComputePath(entity, walkTarget, world.getGameTime())) {
- this.lastTargetPos = walkTarget.getTarget().currentBlockPosition();
+ if (!bl) {
return true;
} else {
brain.eraseMemory(MemoryModuleType.WALK_TARGET);
@@ -53,11 +56,14 @@ public class MoveToTargetSink extends Behavior<Mob> {
return false;
}
+ // petal end
}
}
@Override @Override
protected boolean canStillUse(ServerLevel serverLevel, Mob mob, long l) { protected boolean canStillUse(ServerLevel serverLevel, Mob mob, long l) {
+ if (this.path != null && !this.path.isProcessed()) return true; // petal - wait for path to process + if (!this.finishedProcessing) return true; // petal - wait for processing
+
if (this.path != null && this.lastTargetPos != null) { if (this.path != null && this.lastTargetPos != null) {
Optional<WalkTarget> optional = mob.getBrain().getMemory(MemoryModuleType.WALK_TARGET); Optional<WalkTarget> optional = mob.getBrain().getMemory(MemoryModuleType.WALK_TARGET);
PathNavigation pathNavigation = mob.getNavigation(); PathNavigation pathNavigation = mob.getNavigation();
@@ -87,6 +88,8 @@ public class MoveToTargetSink extends Behavior<Mob> { @@ -81,59 +87,79 @@ public class MoveToTargetSink extends Behavior<Mob> {
@Override
protected void start(ServerLevel serverLevel, Mob mob, long l) {
- mob.getBrain().setMemory(MemoryModuleType.PATH, this.path);
- mob.getNavigation().moveTo(this.path, (double)this.speedModifier);
+ Brain<?> brain = mob.getBrain();
+ WalkTarget walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET).get();
+
+ this.finishedProcessing = false;
+ this.lastTargetPos = walkTarget.getTarget().currentBlockPosition();
+ this.path = this.computePath(mob, walkTarget);
}
@Override @Override
protected void tick(ServerLevel world, Mob entity, long time) { protected void tick(ServerLevel world, Mob entity, long time) {
+ if (this.path != null && !this.path.isProcessed()) return; // petal - wait for processing + if (this.path != null && !this.path.isProcessed()) return; // petal - wait for processing
+ +
Path path = entity.getNavigation().getPath(); + if (!this.finishedProcessing) {
Brain<?> brain = entity.getBrain(); + this.finishedProcessing = true;
if (this.path != path) { +
@@ -94,6 +97,12 @@ public class MoveToTargetSink extends Behavior<Mob> { + Brain<?> brain = entity.getBrain();
brain.setMemory(MemoryModuleType.PATH, path); + boolean canReach = this.path != null && this.path.canReach();
} + if (canReach) {
+ brain.eraseMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE);
+ // petal start - periodically recall moveTo to ensure we're moving towards the correct path + } else if (!brain.hasMemoryValue(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE)) {
+ if (time % 8 == 0) { + brain.setMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE, time);
+ }
+
+ if (!canReach) {
+ Optional<WalkTarget> walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET);
+
+ if (walkTarget.isPresent()) {
+ BlockPos blockPos = walkTarget.get().getTarget().currentBlockPosition();
+ Vec3 vec3 = DefaultRandomPos.getPosTowards((PathfinderMob)entity, 10, 7, Vec3.atBottomCenterOf(blockPos), (double)((float)Math.PI / 2F));
+ if (vec3 != null) {
+ // try recalculating the path using a random position
+ this.path = entity.getNavigation().createPath(vec3.x, vec3.y, vec3.z, 0);
+ this.finishedProcessing = false;
+ return;
+ }
+ }
+
+ // we failed, erase and move on
+ brain.eraseMemory(MemoryModuleType.WALK_TARGET);
+ this.path = null;
+
+ return;
+ }
+
+ entity.getBrain().setMemory(MemoryModuleType.PATH, this.path);
+ entity.getNavigation().moveTo(this.path, (double)this.speedModifier); + entity.getNavigation().moveTo(this.path, (double)this.speedModifier);
+ } + }
+ // petal end
+ +
if (path != null && this.lastTargetPos != null) { Path path = entity.getNavigation().getPath();
Brain<?> brain = entity.getBrain();
- if (this.path != path) {
- this.path = path;
- brain.setMemory(MemoryModuleType.PATH, path);
- }
- if (path != null && this.lastTargetPos != null) {
+ if (path != null && this.lastTargetPos != null && brain.hasMemoryValue(MemoryModuleType.WALK_TARGET)) {
WalkTarget walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET).get(); WalkTarget walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET).get();
if (walkTarget.getTarget().currentBlockPosition().distSqr(this.lastTargetPos) > 4.0D && this.tryComputePath(entity, walkTarget, world.getGameTime())) { - if (walkTarget.getTarget().currentBlockPosition().distSqr(this.lastTargetPos) > 4.0D && this.tryComputePath(entity, walkTarget, world.getGameTime())) {
@@ -112,6 +121,20 @@ public class MoveToTargetSink extends Behavior<Mob> { - this.lastTargetPos = walkTarget.getTarget().currentBlockPosition();
+ if (walkTarget.getTarget().currentBlockPosition().distSqr(this.lastTargetPos) > 4.0D) {
this.start(world, entity, time);
}
}
}
- private boolean tryComputePath(Mob entity, WalkTarget walkTarget, long time) {
+ private Path computePath(Mob entity, WalkTarget walkTarget) {
BlockPos blockPos = walkTarget.getTarget().currentBlockPosition();
- this.path = entity.getNavigation().createPath(blockPos, 0);
this.speedModifier = walkTarget.getSpeedModifier();
Brain<?> brain = entity.getBrain();
if (this.reachedTarget(entity, walkTarget)) { if (this.reachedTarget(entity, walkTarget)) {
brain.eraseMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE); brain.eraseMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE);
} else { - } else {
+ // petal start - move this out to postProcessPath - boolean bl = this.path != null && this.path.canReach();
+ host.bloom.pathfinding.AsyncPathProcessor.awaitProcessing(this.path, (unusedPath) -> { - if (bl) {
+ this.postProcessPath(entity, walkTarget, time); - brain.eraseMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE);
+ }); - } else if (!brain.hasMemoryValue(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE)) {
+ return true; - brain.setMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE, time);
+ } - }
+ -
+ return false; - if (this.path != null) {
+ } - return true;
+ - }
+ private boolean postProcessPath(Mob entity, WalkTarget walkTarget, long time) { -
+ Brain<?> brain = entity.getBrain(); - Vec3 vec3 = DefaultRandomPos.getPosTowards((PathfinderMob)entity, 10, 7, Vec3.atBottomCenterOf(blockPos), (double)((float)Math.PI / 2F));
+ BlockPos blockPos = walkTarget.getTarget().currentBlockPosition(); - if (vec3 != null) {
+ - this.path = entity.getNavigation().createPath(vec3.x, vec3.y, vec3.z, 0);
boolean bl = this.path != null && this.path.canReach(); - return this.path != null;
if (bl) { - }
brain.eraseMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE);
@@ -128,10 +151,17 @@ public class MoveToTargetSink extends Behavior<Mob> {
this.path = entity.getNavigation().createPath(vec3.x, vec3.y, vec3.z, 0);
return this.path != null;
}
+
+ // We failed, so erase and move on
+ brain.eraseMemory(MemoryModuleType.WALK_TARGET);
+ if (bl) {
+ brain.eraseMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE);
} }
+ this.path = null;
return false; - return false;
+ return entity.getNavigation().createPath(blockPos, 0);
} }
+ // petal end
private boolean reachedTarget(Mob entity, WalkTarget walkTarget) { private boolean reachedTarget(Mob entity, WalkTarget walkTarget) {
return walkTarget.getTarget().currentBlockPosition().distManhattan(entity.blockPosition()) <= walkTarget.getCloseEnoughDist(); return walkTarget.getTarget().currentBlockPosition().distManhattan(entity.blockPosition()) <= walkTarget.getCloseEnoughDist();
}
}
+// petal end
\ No newline at end of file
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/SetClosestHomeAsWalkTarget.java b/src/main/java/net/minecraft/world/entity/ai/behavior/SetClosestHomeAsWalkTarget.java diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/SetClosestHomeAsWalkTarget.java b/src/main/java/net/minecraft/world/entity/ai/behavior/SetClosestHomeAsWalkTarget.java
index 9bd6d4f7b86daaaa9cfbad454dde06b797e3f667..dc9dca72a22df9acadb8cdae8383522c996cbe10 100644 index 9bd6d4f7b86daaaa9cfbad454dde06b797e3f667..dc9dca72a22df9acadb8cdae8383522c996cbe10 100644
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/SetClosestHomeAsWalkTarget.java --- a/src/main/java/net/minecraft/world/entity/ai/behavior/SetClosestHomeAsWalkTarget.java

View File

@@ -256,7 +256,7 @@ index ca42c2642a729b90d22b968af7258f3aee72e14b..40261b80d947a6be43465013fae55321
public boolean visible = true; public boolean visible = true;
diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java
index 3441339e1ba5efb0e25c16fa13cb65d2fbdafc42..9d0cb0468800cbd88f43424466e9a29a05d3945d 100644 index 3441339e1ba5efb0e25c16fa13cb65d2fbdafc42..555336cf6700566e8a99e0f0cd6f0cc41b6c5ba0 100644
--- a/src/main/java/net/minecraft/server/level/ServerEntity.java --- a/src/main/java/net/minecraft/server/level/ServerEntity.java
+++ b/src/main/java/net/minecraft/server/level/ServerEntity.java +++ b/src/main/java/net/minecraft/server/level/ServerEntity.java
@@ -249,14 +249,18 @@ public class ServerEntity { @@ -249,14 +249,18 @@ public class ServerEntity {
@@ -280,3 +280,37 @@ index 3441339e1ba5efb0e25c16fa13cb65d2fbdafc42..9d0cb0468800cbd88f43424466e9a29a
this.entity.startSeenByPlayer(player); this.entity.startSeenByPlayer(player);
} }
@@ -362,19 +366,30 @@ public class ServerEntity {
SynchedEntityData datawatcher = this.entity.getEntityData();
if (datawatcher.isDirty()) {
- this.broadcastAndSend(new ClientboundSetEntityDataPacket(this.entity.getId(), datawatcher, false));
+ // Petal start - sync
+ ((ServerLevel) this.entity.level).chunkSource.chunkMap.runOnTrackerMainThread(() ->
+ this.broadcastAndSend(new ClientboundSetEntityDataPacket(this.entity.getId(), datawatcher, false))
+ );
+ // Petal end
}
if (this.entity instanceof LivingEntity) {
Set<AttributeInstance> set = ((LivingEntity) this.entity).getAttributes().getDirtyAttributes();
if (!set.isEmpty()) {
+ // Petal start - sync
+ final var copy = Lists.newArrayList(set);
+ ((ServerLevel) this.entity.level).chunkSource.chunkMap.runOnTrackerMainThread(() -> {
+
// CraftBukkit start - Send scaled max health
if (this.entity instanceof ServerPlayer) {
- ((ServerPlayer) this.entity).getBukkitEntity().injectScaledMaxHealth(set, false);
+ ((ServerPlayer) this.entity).getBukkitEntity().injectScaledMaxHealth(copy, false);
}
// CraftBukkit end
- this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), set));
+ this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), copy));
+
+ });
+ // Petal end
}
set.clear();