This commit is contained in:
Janet Blackquill 2024-04-29 13:16:43 +09:30 committed by GitHub
commit a3e4e2ff3e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 807 additions and 0 deletions

View File

@ -0,0 +1,217 @@
package io.papermc.paper.registry.keys;
import static net.kyori.adventure.key.Key.key;
import io.papermc.paper.entity.ai.Activity;
import io.papermc.paper.generated.GeneratedFrom;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.TypedKey;
import net.kyori.adventure.key.Key;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
/**
* Vanilla keys for {@link RegistryKey#ACTIVITY}.
*
* @apiNote The fields provided here are a direct representation of
* what is available from the vanilla game source. They may be
* changed (including removals) on any Minecraft version
* bump, so cross-version compatibility is not provided on the
* same level as it is on most of the other API.
*/
@SuppressWarnings({
"unused",
"SpellCheckingInspection"
})
@GeneratedFrom("1.20.4")
@ApiStatus.Experimental
public final class ActivityKeys {
/**
* {@code minecraft:core}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<Activity> CORE = create(key("core"));
/**
* {@code minecraft:idle}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<Activity> IDLE = create(key("idle"));
/**
* {@code minecraft:work}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<Activity> WORK = create(key("work"));
/**
* {@code minecraft:play}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<Activity> PLAY = create(key("play"));
/**
* {@code minecraft:rest}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<Activity> REST = create(key("rest"));
/**
* {@code minecraft:meet}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<Activity> MEET = create(key("meet"));
/**
* {@code minecraft:panic}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<Activity> PANIC = create(key("panic"));
/**
* {@code minecraft:raid}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<Activity> RAID = create(key("raid"));
/**
* {@code minecraft:pre_raid}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<Activity> PRE_RAID = create(key("pre_raid"));
/**
* {@code minecraft:hide}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<Activity> HIDE = create(key("hide"));
/**
* {@code minecraft:fight}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<Activity> FIGHT = create(key("fight"));
/**
* {@code minecraft:celebrate}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<Activity> CELEBRATE = create(key("celebrate"));
/**
* {@code minecraft:admire_item}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<Activity> ADMIRE_ITEM = create(key("admire_item"));
/**
* {@code minecraft:avoid}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<Activity> AVOID = create(key("avoid"));
/**
* {@code minecraft:ride}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<Activity> RIDE = create(key("ride"));
/**
* {@code minecraft:play_dead}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<Activity> PLAY_DEAD = create(key("play_dead"));
/**
* {@code minecraft:long_jump}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<Activity> LONG_JUMP = create(key("long_jump"));
/**
* {@code minecraft:ram}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<Activity> RAM = create(key("ram"));
/**
* {@code minecraft:tongue}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<Activity> TONGUE = create(key("tongue"));
/**
* {@code minecraft:swim}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<Activity> SWIM = create(key("swim"));
/**
* {@code minecraft:lay_spawn}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<Activity> LAY_SPAWN = create(key("lay_spawn"));
/**
* {@code minecraft:sniff}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<Activity> SNIFF = create(key("sniff"));
/**
* {@code minecraft:investigate}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<Activity> INVESTIGATE = create(key("investigate"));
/**
* {@code minecraft:roar}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<Activity> ROAR = create(key("roar"));
/**
* {@code minecraft:emerge}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<Activity> EMERGE = create(key("emerge"));
/**
* {@code minecraft:dig}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<Activity> DIG = create(key("dig"));
private ActivityKeys() {
}
private static @NotNull TypedKey<Activity> create(final @NotNull Key key) {
return TypedKey.create(RegistryKey.ACTIVITY, key);
}
}

View File

@ -13,6 +13,7 @@ import org.bukkit.generator.structure.Structure;
import org.bukkit.generator.structure.StructureType;
import org.bukkit.inventory.meta.trim.TrimMaterial;
import org.bukkit.inventory.meta.trim.TrimPattern;
import io.papermc.paper.entity.ai.Activity;
public interface Generators {
@ -23,6 +24,7 @@ public interface Generators {
simpleKey("TrimPatternKeys", TrimPattern.class, Registries.TRIM_PATTERN, RegistryKey.TRIM_PATTERN, true),
simpleKey("StructureKeys", Structure.class, Registries.STRUCTURE, RegistryKey.STRUCTURE, true),
simpleKey("StructureTypeKeys", StructureType.class, Registries.STRUCTURE_TYPE, RegistryKey.STRUCTURE_TYPE, false),
simpleKey("ActivityKeys", Activity.class, Registries.ACTIVITY, RegistryKey.ACTIVITY, false),
new MobGoalGenerator("VanillaGoal", "com.destroystokyo.paper.entity.ai")
};

View File

@ -0,0 +1,202 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Janet Blackquill <uhhadd@gmail.com>
Date: Fri, 26 Apr 2024 23:45:11 -0400
Subject: [PATCH] Add preliminary API for working with 'new' mob AI system
diff --git a/src/main/java/io/papermc/paper/entity/ai/Activity.java b/src/main/java/io/papermc/paper/entity/ai/Activity.java
new file mode 100644
index 0000000000000000000000000000000000000000..0dff7ba2e36c1185453832e28466c82ccb2cd621
--- /dev/null
+++ b/src/main/java/io/papermc/paper/entity/ai/Activity.java
@@ -0,0 +1,9 @@
+package io.papermc.paper.entity.ai;
+
+import org.bukkit.Keyed;
+
+/**
+ * An Activity is a key to a list of tasks in a Brain.
+ */
+public interface Activity extends Keyed {
+}
diff --git a/src/main/java/io/papermc/paper/entity/ai/Brain.java b/src/main/java/io/papermc/paper/entity/ai/Brain.java
new file mode 100644
index 0000000000000000000000000000000000000000..b359ece13fece6d89b1a1cbf55a78af8152a93de
--- /dev/null
+++ b/src/main/java/io/papermc/paper/entity/ai/Brain.java
@@ -0,0 +1,49 @@
+package io.papermc.paper.entity.ai;
+
+import java.util.List;
+import org.bukkit.entity.LivingEntity;
+import org.bukkit.entity.Entity;
+import org.bukkit.Location;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * A brain is the central AI for some modern Minecraft entities such as
+ * villagers and sniffers.
+ */
+public interface Brain<E extends LivingEntity> {
+ /**
+ * If the tasks in the given `activity` can be run, the brain will switch to
+ * the given `activity`.
+ */
+ public void useActivityIfPossible(@NotNull Activity activity);
+ /**
+ * Makes the brain switch to the default activity.
+ */
+ public void useDefaultActivity();
+ /**
+ * Sets the tasks for the given `activity` to the provided list of `tasks`.
+ */
+ ////// TODO: what is that int parameter doing
+ public void setTasksForActivity(@NotNull Activity activity, int begin, @NotNull List<Task<E>> tasks);
+ /**
+ * Clears all tasks for activities associated with this brain.
+ */
+ public void clearActivities();
+ /**
+ * Sets the default activity for this brain.
+ */
+ public void setDefaultActivity(@NotNull Activity activity);
+ /**
+ * Checks whether the given activity is active.
+ */
+ public boolean isActive(@NotNull Activity activity);
+ /**
+ * Sets the entity's current walk target to the given location.
+ */
+ public void setWalkTarget(@NotNull Location location, float speed, int completeWithinDistance);
+ /**
+ * Sets the entity's current walk target to the given entity.
+ */
+ public void setWalkTarget(@NotNull Entity entity, float speed, int completeWithinDistance);
+}
+
diff --git a/src/main/java/io/papermc/paper/entity/ai/Task.java b/src/main/java/io/papermc/paper/entity/ai/Task.java
new file mode 100644
index 0000000000000000000000000000000000000000..8ee58cbb3a520a473e28e48e378c8a9fcb7d24eb
--- /dev/null
+++ b/src/main/java/io/papermc/paper/entity/ai/Task.java
@@ -0,0 +1,10 @@
+package io.papermc.paper.entity.ai;
+
+import org.bukkit.entity.LivingEntity;
+
+/**
+ * A task can be associated with an Activity in a Brain in order
+ * to instruct the entity to do something.
+ */
+public interface Task<E extends LivingEntity> {
+}
\ No newline at end of file
diff --git a/src/main/java/io/papermc/paper/entity/ai/Tasks.java b/src/main/java/io/papermc/paper/entity/ai/Tasks.java
new file mode 100644
index 0000000000000000000000000000000000000000..955dec447a3b93016b62b161b5018fd75251f962
--- /dev/null
+++ b/src/main/java/io/papermc/paper/entity/ai/Tasks.java
@@ -0,0 +1,45 @@
+package io.papermc.paper.entity.ai;
+
+import org.bukkit.Location;
+import org.bukkit.entity.Mob;
+import org.bukkit.entity.memory.MemoryKey;
+import org.jetbrains.annotations.NotNull;
+import java.util.function.Predicate;
+import java.util.function.Function;
+import org.bukkit.entity.LivingEntity;
+import java.util.Map;
+
+public interface Tasks {
+ /**
+ * Instructs the entity to get within N blocks of the block location.
+ */
+ public <Entity extends Mob> @NotNull Task<Entity> walkToWalkTarget(int minRunTime, int maxRunTime);
+ /**
+ * Instructs the entity to swim if it is in water.
+ */
+ public <Entity extends Mob> @NotNull Task<Entity> swimIfInWater(float chance);
+ /**
+ * Instructs the entity to panic if it is hit, freezing, or on fire.
+ */
+ public <Entity extends Mob> @NotNull Task<Entity> panicOnDamage(float speed);
+ /**
+ * Instructs the entity to change its look target to the closest target
+ * for which the predicate returns true.
+ */
+ public <Entity extends LivingEntity> @NotNull Task<Entity> setLookTarget(@NotNull Predicate<LivingEntity> predicate, float maximumDistance);
+ /**
+ * Instructs the entity to set its walk target to the look target if it matches the given predicate.
+ *
+ * @param speed a function defining the speed at which the entity will walk to the given target
+ * @param completionRange the distance in blocks that the entity will attempt to get within
+ */
+ public <Entity extends LivingEntity> @NotNull Task<Entity> setWalkTargetToLookTarget(@NotNull Predicate<LivingEntity> predicate, @NotNull Function<LivingEntity, Float> speed, int completionRange);
+ /**
+ * Instructs the entity to look at the look target.
+ */
+ public <Entity extends Mob> @NotNull Task<Entity> lookAtLookTarget(int minRunTime, int maxRunTime);
+ /**
+ * Instructs the entity to run one of the given tasks with weighted probability.
+ */
+ public <Entity extends LivingEntity> @NotNull Task<Entity> runOneOf(@NotNull Map<Task<? super Entity>, Integer> tasks);
+}
diff --git a/src/main/java/io/papermc/paper/registry/RegistryKey.java b/src/main/java/io/papermc/paper/registry/RegistryKey.java
index c4b30b16ce4db754b958c493ad86d0863592c263..05f96057ffdd488877517625ce82dc83712150c5 100644
--- a/src/main/java/io/papermc/paper/registry/RegistryKey.java
+++ b/src/main/java/io/papermc/paper/registry/RegistryKey.java
@@ -8,6 +8,7 @@ import org.bukkit.generator.structure.StructureType;
import org.bukkit.inventory.meta.trim.TrimMaterial;
import org.bukkit.inventory.meta.trim.TrimPattern;
import org.jetbrains.annotations.ApiStatus;
+import io.papermc.paper.entity.ai.Activity;
import static io.papermc.paper.registry.RegistryKeyImpl.create;
@@ -35,6 +36,11 @@ public sealed interface RegistryKey<T> extends Keyed permits RegistryKeyImpl {
* @see io.papermc.paper.registry.keys.GameEventKeys
*/
RegistryKey<GameEvent> GAME_EVENT = create("game_event");
+ /**
+ * Built-in registry for activities
+ * @see io.papermc.paper.registry.keys.ActivityKeys
+ */
+ RegistryKey<Activity> ACTIVITY = create("activity");
/**
* Built-in registry for structure types.
* @see io.papermc.paper.registry.keys.StructureTypeKeys
diff --git a/src/main/java/org/bukkit/Registry.java b/src/main/java/org/bukkit/Registry.java
index e1fb4d8cca6a9c59047b1396f5c40bea957d777a..7e1f769db2a0df7e687a7af2c9329d93ecf00db1 100644
--- a/src/main/java/org/bukkit/Registry.java
+++ b/src/main/java/org/bukkit/Registry.java
@@ -270,6 +270,13 @@ public interface Registry<T extends Keyed> extends Iterable<T> {
Registry<GameEvent> GAME_EVENT = Objects.requireNonNull(Bukkit.getRegistry(GameEvent.class), "No registry present for GameEvent. This is a bug.");
// Paper start
+ /**
+ * Activities.
+ *
+ * @see io.papermc.paper.entity.ai.Activity
+ */
+ Registry<io.papermc.paper.entity.ai.Activity> ACTIVITY = Bukkit.getRegistry(io.papermc.paper.entity.ai.Activity.class);
+
/**
* Configured structures.
* @see io.papermc.paper.world.structure.ConfiguredStructure
diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
index 4ff1b38eb65f97344257204cf018f176f247ed36..4f57f4e0d65a99da3d5ce1ccc5b1534d608c3d36 100644
--- a/src/main/java/org/bukkit/Server.java
+++ b/src/main/java/org/bukkit/Server.java
@@ -2518,4 +2518,11 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
*/
boolean isOwnedByCurrentRegion(@NotNull Entity entity);
// Paper end - Folia region threading API
+ // Paper start - activities and tasks API
+ @NotNull
+ io.papermc.paper.entity.ai.Tasks getTasks();
+
+ @NotNull
+ <E extends org.bukkit.entity.LivingEntity> io.papermc.paper.entity.ai.Brain<E> getBrain(@NotNull E entity);
+ // Paper end
}

View File

@ -0,0 +1,323 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Janet Blackquill <uhhadd@gmail.com>
Date: Fri, 26 Apr 2024 23:46:35 -0400
Subject: [PATCH] Implement preliminary API for working with 'new' mob AI
system
diff --git a/src/main/java/io/papermc/paper/entity/ai/PaperActivity.java b/src/main/java/io/papermc/paper/entity/ai/PaperActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..8724ac343b62f29020aa06093fc01787619d64fb
--- /dev/null
+++ b/src/main/java/io/papermc/paper/entity/ai/PaperActivity.java
@@ -0,0 +1,36 @@
+package io.papermc.paper.entity.ai;
+
+import net.minecraft.world.entity.schedule.Activity;
+import net.minecraft.core.registries.Registries;
+import org.bukkit.NamespacedKey;
+import org.bukkit.Registry;
+import org.bukkit.craftbukkit.CraftRegistry;
+import org.bukkit.craftbukkit.util.Handleable;
+
+public class PaperActivity implements io.papermc.paper.entity.ai.Activity, Handleable<Activity> {
+ final private NamespacedKey key;
+ final private Activity handle;
+
+ public static io.papermc.paper.entity.ai.Activity minecraftToBukkit(Activity minecraft) {
+ return CraftRegistry.minecraftToBukkit(minecraft, Registries.ACTIVITY, Registry.ACTIVITY);
+ }
+ public static Activity bukkitToMinecraft(io.papermc.paper.entity.ai.Activity bukkit) {
+ return CraftRegistry.bukkitToMinecraft(bukkit);
+ }
+
+ public PaperActivity(NamespacedKey key, Activity handle) {
+ this.key = key;
+ this.handle = handle;
+ }
+
+ @Override
+ public Activity getHandle() {
+ return this.handle;
+ }
+
+ @Override
+ public NamespacedKey getKey() {
+ if (true) return java.util.Objects.requireNonNull(org.bukkit.Registry.ACTIVITY.getKey(this), () -> this + " doesn't have a key");
+ return this.key;
+ }
+}
diff --git a/src/main/java/io/papermc/paper/entity/ai/PaperBrain.java b/src/main/java/io/papermc/paper/entity/ai/PaperBrain.java
new file mode 100644
index 0000000000000000000000000000000000000000..dfc84db49790e65bae01623b232b322e319d3d38
--- /dev/null
+++ b/src/main/java/io/papermc/paper/entity/ai/PaperBrain.java
@@ -0,0 +1,71 @@
+package io.papermc.paper.entity.ai;
+
+import java.util.List;
+import org.bukkit.entity.LivingEntity;
+import net.minecraft.world.entity.ai.Brain;
+import net.minecraft.world.entity.ai.memory.MemoryModuleType;
+import net.minecraft.world.entity.ai.memory.MemoryStatus;
+import com.google.common.collect.ImmutableList;
+import net.minecraft.world.entity.ai.behavior.BehaviorControl;
+import org.jetbrains.annotations.NotNull;
+import org.bukkit.entity.Entity;
+import org.bukkit.craftbukkit.entity.CraftEntity;
+import org.bukkit.Location;
+import net.minecraft.world.entity.ai.memory.WalkTarget;
+import org.bukkit.craftbukkit.util.CraftLocation;
+
+public class PaperBrain<Paper extends LivingEntity, Minecraft extends net.minecraft.world.entity.LivingEntity> implements io.papermc.paper.entity.ai.Brain<Paper> {
+ public final Brain<Minecraft> handle;
+
+ public PaperBrain(Brain<Minecraft> handle) {
+ this.handle = handle;
+ }
+ public Brain<Minecraft> getHandle() {
+ return this.handle;
+ }
+
+ @Override
+ public void useDefaultActivity() {
+ this.getHandle().useDefaultActivity();
+ }
+
+ @Override
+ public void useActivityIfPossible(@NotNull Activity activity) {
+ this.getHandle().setActiveActivityIfPossible(((PaperActivity)activity).getHandle());
+ }
+
+ @Override
+ public void setTasksForActivity(@NotNull Activity activity, int begin, @NotNull List<Task<Paper>> tasks) {
+ this.getHandle().addActivity(
+ ((PaperActivity)activity).getHandle(),
+ begin,
+ ImmutableList.<BehaviorControl<Minecraft>>copyOf(tasks.stream().map(task -> ((PaperTask<Paper, Minecraft>)task).getHandle()).iterator())
+ );
+ }
+
+ @Override
+ public void clearActivities() {
+ this.getHandle().removeAllBehaviors();
+ }
+
+ @Override
+ public void setDefaultActivity(@NotNull Activity activity) {
+ this.getHandle().setDefaultActivity(((PaperActivity)activity).getHandle());
+ }
+
+ @Override
+ public boolean isActive(@NotNull Activity activity) {
+ return this.getHandle().isActive(((PaperActivity)activity).getHandle());
+ }
+
+ @Override
+ public void setWalkTarget(@NotNull Location location, float speed, int completeWithinDistance) {
+ this.getHandle().setMemory(MemoryModuleType.WALK_TARGET, new WalkTarget(CraftLocation.toBlockPosition(location), speed, completeWithinDistance));
+ }
+
+ @Override
+ public void setWalkTarget(@NotNull Entity entity, float speed, int completeWithinDistance) {
+ this.getHandle().setMemory(MemoryModuleType.WALK_TARGET, new WalkTarget(((CraftEntity)entity).getHandle(), speed, completeWithinDistance));
+ }
+}
+
diff --git a/src/main/java/io/papermc/paper/entity/ai/PaperTask.java b/src/main/java/io/papermc/paper/entity/ai/PaperTask.java
new file mode 100644
index 0000000000000000000000000000000000000000..870ff6a179b62302c1b6dbf603fd5621396dd964
--- /dev/null
+++ b/src/main/java/io/papermc/paper/entity/ai/PaperTask.java
@@ -0,0 +1,16 @@
+package io.papermc.paper.entity.ai;
+
+import org.bukkit.entity.LivingEntity;
+
+import net.minecraft.world.entity.ai.behavior.BehaviorControl;
+
+public class PaperTask<Paper extends LivingEntity, Minecraft extends net.minecraft.world.entity.LivingEntity> implements io.papermc.paper.entity.ai.Task {
+ final private BehaviorControl<Minecraft> handle;
+
+ public PaperTask(BehaviorControl<Minecraft> handle) {
+ this.handle = handle;
+ }
+ public BehaviorControl<Minecraft> getHandle() {
+ return this.handle;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/io/papermc/paper/entity/ai/PaperTasks.java b/src/main/java/io/papermc/paper/entity/ai/PaperTasks.java
new file mode 100644
index 0000000000000000000000000000000000000000..433fcfdbb0b0112f2e80e71fc9bea19ad62d32b5
--- /dev/null
+++ b/src/main/java/io/papermc/paper/entity/ai/PaperTasks.java
@@ -0,0 +1,54 @@
+package io.papermc.paper.entity.ai;
+
+import org.bukkit.Location;
+import org.bukkit.entity.Mob;
+import org.bukkit.entity.LivingEntity;
+import org.bukkit.entity.memory.MemoryKey;
+import net.minecraft.world.entity.ai.behavior.BehaviorControl;
+import net.minecraft.world.entity.ai.behavior.MoveToTargetSink;
+import net.minecraft.world.entity.ai.behavior.LookAtTargetSink;
+import net.minecraft.world.entity.ai.behavior.Swim;
+import net.minecraft.world.entity.ai.behavior.AnimalPanic;
+import net.minecraft.world.entity.ai.behavior.SetEntityLookTarget;
+import net.minecraft.world.entity.ai.behavior.SetWalkTargetFromLookTarget;
+import net.minecraft.world.entity.ai.behavior.RunOne;
+import org.bukkit.craftbukkit.entity.memory.CraftMemoryKey;
+import org.jetbrains.annotations.NotNull;
+import java.util.function.Predicate;
+import java.util.function.Function;
+import java.util.Map;
+import java.util.List;
+import java.util.ArrayList;
+import com.mojang.datafixers.util.Pair;
+
+public class PaperTasks implements io.papermc.paper.entity.ai.Tasks {
+ public <Entity extends Mob> @NotNull Task<Entity> walkToWalkTarget(int minRunTime, int maxRunTime) {
+ return new PaperTask(new MoveToTargetSink(minRunTime, maxRunTime));
+ }
+ public <Entity extends Mob> @NotNull Task<Entity> swimIfInWater(float chance) {
+ return new PaperTask(new Swim(chance));
+ }
+ public <Entity extends Mob> @NotNull Task<Entity> panicOnDamage(float speed) {
+ return new PaperTask(new AnimalPanic(speed));
+ }
+ public <Entity extends LivingEntity> @NotNull Task<Entity> setLookTarget(Predicate<LivingEntity> predicate, float maximumDistance) {
+ return new PaperTask(SetEntityLookTarget.create(nmsEntity -> predicate.test(nmsEntity.getBukkitLivingEntity()), maximumDistance));
+ }
+ public <Entity extends LivingEntity> @NotNull Task<Entity> setWalkTargetToLookTarget(Predicate<LivingEntity> predicate, Function<LivingEntity, Float> speed, int completionRange) {
+ return new PaperTask(SetWalkTargetFromLookTarget.create(
+ nmsEntity -> predicate.test(nmsEntity.getBukkitLivingEntity()),
+ nmsEntity -> speed.apply(nmsEntity.getBukkitLivingEntity()),
+ completionRange
+ ));
+ }
+ public <Entity extends Mob> @NotNull Task<Entity> lookAtLookTarget(int minRunTime, int maxRunTime) {
+ return new PaperTask(new LookAtTargetSink(minRunTime, maxRunTime));
+ }
+ public <Bukkit extends LivingEntity> @NotNull Task<Bukkit> runOneOf(Map<Task<? super Bukkit>, Integer> tasks) {
+ List<Pair<BehaviorControl<? super net.minecraft.world.entity.LivingEntity>, Integer>> mcTasks = new ArrayList<>(tasks.size());
+ for (Map.Entry<Task<? super Bukkit>, Integer> entry : tasks.entrySet()) {
+ mcTasks.add(new Pair(((PaperTask)entry.getKey()).getHandle(), entry.getValue()));
+ }
+ return new PaperTask(new RunOne(mcTasks));
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/net/minecraft/world/entity/ai/Brain.java b/src/main/java/net/minecraft/world/entity/ai/Brain.java
index afbb027021acfbe25d534a84f1750e420bbde6e0..8ca8152a98710ee256fc1a11be6cd932bc6695fb 100644
--- a/src/main/java/net/minecraft/world/entity/ai/Brain.java
+++ b/src/main/java/net/minecraft/world/entity/ai/Brain.java
@@ -79,7 +79,7 @@ public class Brain<E extends LivingEntity> {
}
public <T> DataResult<Brain<E>> decode(DynamicOps<T> dynamicOps, MapLike<T> mapLike) {
- MutableObject<DataResult<Builder<Brain.MemoryValue<?>>>> mutableObject = new MutableObject<>(
+ MutableObject<DataResult<Builder<Brain.MemoryValue<?>>>> dataResultMutableObject = new MutableObject<>( // Paper - fix decompilation name collision
DataResult.success(ImmutableList.builder())
);
mapLike.entries()
@@ -91,10 +91,10 @@ public class Brain<E extends LivingEntity> {
DataResult<? extends Brain.MemoryValue<?>> dataResult2 = dataResult.flatMap(
memoryType -> this.captureRead((MemoryModuleType<T>)memoryType, dynamicOps, (T)pair.getSecond())
);
- mutableObject.setValue(mutableObject.getValue().apply2(Builder::add, dataResult2));
+ dataResultMutableObject.setValue(dataResultMutableObject.getValue().apply2(Builder::add, dataResult2)); // Paper - fix decompilation name collision
}
);
- ImmutableList<Brain.MemoryValue<?>> immutableList = mutableObject.getValue()
+ ImmutableList<Brain.MemoryValue<?>> immutableList = dataResultMutableObject.getValue() // Paper - fix decompilation name collision
.resultOrPartial(Brain.LOGGER::error)
.map(Builder::build)
.orElseGet(ImmutableList::of);
@@ -180,28 +180,30 @@ public class Brain<E extends LivingEntity> {
}
<U> void setMemoryInternal(MemoryModuleType<U> type, Optional<? extends ExpirableValue<?>> memory) {
- if (this.memories.containsKey(type)) {
+ // Paper start - allow custom memories to be set on any brain
+ // if (this.memories.containsKey(type)) {
if (memory.isPresent() && this.isEmptyCollection(memory.get().getValue())) {
this.eraseMemory(type);
} else {
this.memories.put(type, memory);
}
- }
+ // }
+ // Paper end - allow custom memories to be set on any brain
}
public <U> Optional<U> getMemory(MemoryModuleType<U> type) {
- Optional<? extends ExpirableValue<?>> optional = this.memories.get(type);
+ Optional<? extends ExpirableValue<U>> optional = (Optional<? extends ExpirableValue<U>>)this.memories.get(type); // Paper - decompilation fix
if (optional == null) {
throw new IllegalStateException("Unregistered memory fetched: " + type);
} else {
- return optional.map(ExpirableValue::getValue);
+ return optional.map(x -> x.getValue()); // Paper - decompilation fix
}
}
@Nullable
public <U> Optional<U> getMemoryInternal(MemoryModuleType<U> type) {
- Optional<? extends ExpirableValue<?>> optional = this.memories.get(type);
- return optional == null ? null : optional.map(ExpirableValue::getValue);
+ Optional<? extends ExpirableValue<U>> optional = (Optional<? extends ExpirableValue<U>>)this.memories.get(type); // Paper - decompilation fix
+ return optional == null ? null : optional.map(x -> x.getValue()); // Paper - decompilation fix
}
public <U> long getTimeUntilExpiry(MemoryModuleType<U> type) {
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java b/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java
index 4fc02698a9312496e7f9bce1c64f317374d2a42f..05e3feb65dc9288c8288c1ecef7426bed018c57e 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java
@@ -125,6 +125,11 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
return new io.papermc.paper.world.structure.PaperConfiguredStructure.LegacyRegistry(registryHolder.registryOrThrow(Registries.STRUCTURE));
}
// Paper end
+ // Paper start - activity registry
+ if (bukkitClass == io.papermc.paper.entity.ai.Activity.class) {
+ return new CraftRegistry<>(io.papermc.paper.entity.ai.Activity.class, registryHolder.registryOrThrow(Registries.ACTIVITY), io.papermc.paper.entity.ai.PaperActivity::new);
+ }
+ // Paper end - activity registry
return null;
}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index c490a29bcf7410bc54959ee71375605964379ed5..b54752d076d187f23aafa90514b62c4301dca8ab 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -3259,4 +3259,17 @@ public final class CraftServer implements Server {
}
// Paper end
+ // Paper begin - activities and tasks AI API
+ private final io.papermc.paper.entity.ai.Tasks tasks = new io.papermc.paper.entity.ai.PaperTasks();
+
+ @Override
+ public io.papermc.paper.entity.ai.Tasks getTasks() {
+ return this.tasks;
+ }
+
+ @Override
+ public <E extends org.bukkit.entity.LivingEntity> io.papermc.paper.entity.ai.Brain<E> getBrain(E entity) {
+ return new io.papermc.paper.entity.ai.PaperBrain(((org.bukkit.craftbukkit.entity.CraftLivingEntity)entity).getHandle().getBrain());
+ }
+ // Paper end - activities and tasks AI API
}
diff --git a/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java b/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java
index 167cd003a52b722772708a528d724db14c89c35a..c4ee058f4ea6f320b945056f9848afdeb2ae1fc2 100644
--- a/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java
+++ b/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java
@@ -43,6 +43,7 @@ public class RegistriesArgumentProvider implements ArgumentsProvider {
DATA.add(Arguments.of(TrimMaterial.class, Registries.TRIM_MATERIAL, CraftTrimMaterial.class, net.minecraft.world.item.armortrim.TrimMaterial.class));
DATA.add(Arguments.of(TrimPattern.class, Registries.TRIM_PATTERN, CraftTrimPattern.class, net.minecraft.world.item.armortrim.TrimPattern.class));
DATA.add(Arguments.of(DamageType.class, Registries.DAMAGE_TYPE, CraftDamageType.class, net.minecraft.world.damagesource.DamageType.class));
+ DATA.add(Arguments.of(io.papermc.paper.entity.ai.Activity.class, Registries.ACTIVITY, io.papermc.paper.entity.ai.PaperActivity.class, net.minecraft.world.entity.schedule.Activity.class)); // Paper - add activity registry
}
@Override

View File

@ -2,11 +2,74 @@ package io.papermc.testplugin;
import org.bukkit.event.Listener;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Sniffer;
import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.entity.memory.MemoryKey;
import java.util.Collections;
import io.papermc.paper.registry.keys.ActivityKeys;
import org.bukkit.Registry;
import org.bukkit.NamespacedKey;
import java.util.Map;
public final class TestPlugin extends JavaPlugin implements Listener {
public class SnifferCommand extends org.bukkit.command.Command {
public SnifferCommand() {
super("sniffer");
}
public boolean execute(CommandSender sender, String label, String []params) {
var player = (Player)sender;
var sniffer = (Sniffer)player.getWorld()
.spawnEntity(player.getLocation(), EntityType.SNIFFER);
var target = player.getLocation();
target.add(10, 0, 0);
var server = Bukkit.getServer();
var core = Registry.ACTIVITY.get(new NamespacedKey(ActivityKeys.CORE.key().namespace(), ActivityKeys.CORE.key().value()));
var idle = Registry.ACTIVITY.get(new NamespacedKey(ActivityKeys.IDLE.key().namespace(), ActivityKeys.IDLE.key().value()));
var brain = Bukkit.getServer().getBrain(sniffer);
var tasks = server.getTasks();
brain.setWalkTarget(target, 1.25F, 0);
brain.clearActivities();
brain.setTasksForActivity(core, 0, List.of(
tasks.swimIfInWater(0.8f),
tasks.panicOnDamage(9.0f),
tasks.runOneOf(
Map.of(
tasks.setLookTarget(entity -> entity instanceof Sniffer, 30f),
6,
tasks.setLookTarget(entity -> entity instanceof Player, 30f),
12
)
),
tasks.runOneOf(
Map.of(
tasks.setWalkTargetToLookTarget(entity -> true, entity -> 9.0f, 0),
6,
tasks.setWalkTargetToLookTarget(entity -> true, entity -> 2.0f, 0),
12
)
)
));
brain.setTasksForActivity(idle, 0, List.of(
tasks.lookAtLookTarget(150, 250),
tasks.walkToWalkTarget(150, 250)
));
brain.useActivityIfPossible(idle);
sender.sendMessage(brain.isActive(idle) ? "Yes!" : "no no no");
return true;
}
}
@Override
public void onEnable() {
this.getServer().getCommandMap().register("sniffer", new SnifferCommand());
this.getServer().getPluginManager().registerEvents(this, this);
}
}