mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-04 16:34:44 +01:00
85f704e537
Co-authored-by: Owen1212055 <23108066+Owen1212055@users.noreply.github.com>
666 lines
30 KiB
Diff
666 lines
30 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: MiniDigger <admin@benndorf.dev>
|
|
Date: Fri, 3 Jan 2020 16:26:19 +0100
|
|
Subject: [PATCH] Implement Mob Goal API
|
|
|
|
|
|
diff --git a/build.gradle.kts b/build.gradle.kts
|
|
index cb100e337521fd278893ec775606f128717105f7..d253682a020cc5cb41c9fdae48adf5c85258be62 100644
|
|
--- a/build.gradle.kts
|
|
+++ b/build.gradle.kts
|
|
@@ -54,6 +54,7 @@ dependencies {
|
|
runtimeOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.9.18")
|
|
runtimeOnly("org.apache.maven.resolver:maven-resolver-transport-http:1.9.18")
|
|
|
|
+ testImplementation("io.github.classgraph:classgraph:4.8.47") // Paper - mob goal test
|
|
testImplementation("org.junit.jupiter:junit-jupiter:5.10.2")
|
|
testImplementation("org.junit.platform:junit-platform-suite-engine:1.10.0")
|
|
testImplementation("org.hamcrest:hamcrest:2.2")
|
|
diff --git a/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java b/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..0a8dcfd9bdb56e988fee6404c4663aca7c5c7e98
|
|
--- /dev/null
|
|
+++ b/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java
|
|
@@ -0,0 +1,243 @@
|
|
+package com.destroystokyo.paper.entity.ai;
|
|
+
|
|
+import com.destroystokyo.paper.entity.RangedEntity;
|
|
+import com.google.common.base.CaseFormat;
|
|
+import java.lang.reflect.Constructor;
|
|
+import java.util.EnumSet;
|
|
+import java.util.HashMap;
|
|
+import java.util.Map;
|
|
+import io.papermc.paper.entity.SchoolableFish;
|
|
+import io.papermc.paper.util.ObfHelper;
|
|
+import net.minecraft.world.entity.ai.goal.Goal;
|
|
+import net.minecraft.world.entity.monster.RangedAttackMob;
|
|
+import org.apache.commons.lang3.math.NumberUtils;
|
|
+import org.bukkit.NamespacedKey;
|
|
+import org.bukkit.entity.*;
|
|
+
|
|
+public class MobGoalHelper {
|
|
+
|
|
+ private static final Map<Class<? extends Goal>, Class<? extends Mob>> entityClassCache = new HashMap<>();
|
|
+ private static final Map<Class<? extends net.minecraft.world.entity.Mob>, Class<? extends Mob>> bukkitMap = new HashMap<>();
|
|
+
|
|
+ static {
|
|
+ //<editor-fold defaultstate="collapsed" desc="bukkitMap Entities">
|
|
+ bukkitMap.put(net.minecraft.world.entity.Mob.class, Mob.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.AgeableMob.class, Ageable.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.ambient.AmbientCreature.class, Ambient.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.Animal.class, Animals.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.ambient.Bat.class, Bat.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.Bee.class, Bee.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.Blaze.class, Blaze.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.Cat.class, Cat.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.CaveSpider.class, CaveSpider.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.Chicken.class, Chicken.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.Cod.class, Cod.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.Cow.class, Cow.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.PathfinderMob.class, Creature.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.Creeper.class, Creeper.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.Dolphin.class, Dolphin.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.Drowned.class, Drowned.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.boss.enderdragon.EnderDragon.class, EnderDragon.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.EnderMan.class, Enderman.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.Endermite.class, Endermite.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.Evoker.class, Evoker.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.AbstractFish.class, Fish.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.AbstractSchoolingFish.class, SchoolableFish.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.FlyingMob.class, Flying.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.Fox.class, Fox.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.Ghast.class, Ghast.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.Giant.class, Giant.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.AbstractGolem.class, Golem.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.Guardian.class, Guardian.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.ElderGuardian.class, ElderGuardian.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.horse.Horse.class, Horse.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.horse.AbstractHorse.class, AbstractHorse.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.horse.AbstractChestedHorse.class, ChestedHorse.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.horse.Donkey.class, Donkey.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.horse.Mule.class, Mule.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.horse.SkeletonHorse.class, SkeletonHorse.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.horse.ZombieHorse.class, ZombieHorse.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.camel.Camel.class, org.bukkit.entity.Camel.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.AbstractIllager.class, Illager.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.Illusioner.class, Illusioner.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.SpellcasterIllager.class, Spellcaster.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.IronGolem.class, IronGolem.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.horse.Llama.class, Llama.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.horse.TraderLlama.class, TraderLlama.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.MagmaCube.class, MagmaCube.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.Monster.class, Monster.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.PatrollingMonster.class, Raider.class); // close enough
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.MushroomCow.class, MushroomCow.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.Ocelot.class, Ocelot.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.Panda.class, Panda.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.Parrot.class, Parrot.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.ShoulderRidingEntity.class, Parrot.class); // close enough
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.Phantom.class, Phantom.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.Pig.class, Pig.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.ZombifiedPiglin.class, PigZombie.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.Pillager.class, Pillager.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.PolarBear.class, PolarBear.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.Pufferfish.class, PufferFish.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.Rabbit.class, Rabbit.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.raid.Raider.class, Raider.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.Ravager.class, Ravager.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.Salmon.class, Salmon.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.Sheep.class, Sheep.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.Shulker.class, Shulker.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.Silverfish.class, Silverfish.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.Skeleton.class, Skeleton.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.AbstractSkeleton.class, AbstractSkeleton.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.Stray.class, Stray.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.WitherSkeleton.class, WitherSkeleton.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.Slime.class, Slime.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.SnowGolem.class, Snowman.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.Spider.class, Spider.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.Squid.class, Squid.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.TamableAnimal.class, Tameable.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.TropicalFish.class, TropicalFish.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.Turtle.class, Turtle.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.Vex.class, Vex.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.npc.Villager.class, Villager.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.npc.AbstractVillager.class, AbstractVillager.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.npc.WanderingTrader.class, WanderingTrader.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.Vindicator.class, Vindicator.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.WaterAnimal.class, WaterMob.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.Witch.class, Witch.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.boss.wither.WitherBoss.class, Wither.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.Wolf.class, Wolf.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.Zombie.class, Zombie.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.Husk.class, Husk.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.ZombieVillager.class, ZombieVillager.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.hoglin.Hoglin.class, Hoglin.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.piglin.Piglin.class, Piglin.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.piglin.AbstractPiglin.class, PiglinAbstract.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.piglin.PiglinBrute.class, PiglinBrute.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.Strider.class, Strider.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.Zoglin.class, Zoglin.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.GlowSquid.class, GlowSquid.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.axolotl.Axolotl.class, Axolotl.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.goat.Goat.class, Goat.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.frog.Frog.class, Frog.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.frog.Tadpole.class, Tadpole.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.warden.Warden.class, Warden.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.allay.Allay.class, Allay.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.sniffer.Sniffer.class, Sniffer.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.breeze.Breeze.class, Breeze.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.armadillo.Armadillo.class, Armadillo.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.Bogged.class, Bogged.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.creaking.Creaking.class, Creaking.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.monster.creaking.CreakingTransient.class, CreakingTransient.class);
|
|
+ bukkitMap.put(net.minecraft.world.entity.animal.AgeableWaterCreature.class, Squid.class); // close enough
|
|
+ //</editor-fold>
|
|
+ }
|
|
+
|
|
+ private static final Map<String, String> deobfuscationMap = new HashMap<>();
|
|
+
|
|
+ static {
|
|
+ // TODO these kinda should be checked on each release, in case obfuscation changes
|
|
+ deobfuscationMap.put("abstract_skeleton_1", "abstract_skeleton_melee");
|
|
+ }
|
|
+
|
|
+ private static String getPathName(String name) {
|
|
+ String pathName = name.substring(name.lastIndexOf('.') + 1);
|
|
+ boolean needDeobfMap = false;
|
|
+
|
|
+ // inner classes
|
|
+ int firstInnerDelimiter = pathName.indexOf('$');
|
|
+ if (firstInnerDelimiter != -1) {
|
|
+ String innerClassName = pathName.substring(firstInnerDelimiter + 1);
|
|
+ for (String nestedClass : innerClassName.split("\\$")) {
|
|
+ if (NumberUtils.isDigits(nestedClass)) {
|
|
+ needDeobfMap = true;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if (!needDeobfMap) {
|
|
+ pathName = innerClassName;
|
|
+ }
|
|
+ pathName = pathName.replace('$', '_');
|
|
+ // mapped, wooo!
|
|
+ }
|
|
+
|
|
+ pathName = pathName.replace("TargetGoal", "");
|
|
+ pathName = pathName.replace("Goal", "");
|
|
+ pathName = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, pathName);
|
|
+
|
|
+ if (needDeobfMap && !deobfuscationMap.containsKey(pathName)) {
|
|
+ System.err.println("need to map " + name + " (" + pathName + ")");
|
|
+ }
|
|
+
|
|
+ // did we rename this key?
|
|
+ return deobfuscationMap.getOrDefault(pathName, pathName);
|
|
+ }
|
|
+
|
|
+ public static EnumSet<GoalType> vanillaToPaper(Goal goal) {
|
|
+ EnumSet<GoalType> goals = EnumSet.noneOf(GoalType.class);
|
|
+ for (GoalType type : GoalType.values()) {
|
|
+ if (goal.getFlags().hasElement(paperToVanilla(type))) {
|
|
+ goals.add(type);
|
|
+ }
|
|
+ }
|
|
+ return goals;
|
|
+ }
|
|
+
|
|
+ public static GoalType vanillaToPaper(Goal.Flag type) {
|
|
+ return switch (type) {
|
|
+ case MOVE -> GoalType.MOVE;
|
|
+ case LOOK -> GoalType.LOOK;
|
|
+ case JUMP -> GoalType.JUMP;
|
|
+ case UNKNOWN_BEHAVIOR -> GoalType.UNKNOWN_BEHAVIOR;
|
|
+ case TARGET -> GoalType.TARGET;
|
|
+ default -> throw new IllegalArgumentException("Unknown vanilla mob goal type " + type.name());
|
|
+ };
|
|
+ }
|
|
+
|
|
+ public static EnumSet<Goal.Flag> paperToVanilla(EnumSet<GoalType> types) {
|
|
+ EnumSet<Goal.Flag> goals = EnumSet.noneOf(Goal.Flag.class);
|
|
+ for (GoalType type : types) {
|
|
+ goals.add(paperToVanilla(type));
|
|
+ }
|
|
+ return goals;
|
|
+ }
|
|
+
|
|
+ public static Goal.Flag paperToVanilla(GoalType type) {
|
|
+ return switch (type) {
|
|
+ case MOVE -> Goal.Flag.MOVE;
|
|
+ case LOOK -> Goal.Flag.LOOK;
|
|
+ case JUMP -> Goal.Flag.JUMP;
|
|
+ case UNKNOWN_BEHAVIOR -> Goal.Flag.UNKNOWN_BEHAVIOR;
|
|
+ case TARGET -> Goal.Flag.TARGET;
|
|
+ default -> throw new IllegalArgumentException("Unknown paper mob goal type " + type.name());
|
|
+ };
|
|
+ }
|
|
+
|
|
+ public static <T extends Mob> GoalKey<T> getKey(Class<? extends Goal> goalClass) {
|
|
+ String name = getPathName(io.papermc.paper.util.MappingEnvironment.reobf() ? ObfHelper.INSTANCE.deobfClassName(goalClass.getName()) : goalClass.getName());
|
|
+ return GoalKey.of(getEntity(goalClass), NamespacedKey.minecraft(name));
|
|
+ }
|
|
+
|
|
+ private static <T extends Mob> Class<T> getEntity(Class<? extends Goal> goalClass) {
|
|
+ //noinspection unchecked
|
|
+ return (Class<T>) entityClassCache.computeIfAbsent(goalClass, key -> {
|
|
+ for (Constructor<?> ctor : key.getDeclaredConstructors()) {
|
|
+ for (Class<?> param : ctor.getParameterTypes()) {
|
|
+ if (net.minecraft.world.entity.Mob.class.isAssignableFrom(param)) {
|
|
+ //noinspection unchecked
|
|
+ Class<? extends Mob> bukkitClass = toBukkitClass((Class<? extends net.minecraft.world.entity.Mob>) param);
|
|
+ if (bukkitClass == null) {
|
|
+ throw new RuntimeException("Can't figure out applicable bukkit entity for nms entity " + param); // maybe just return Mob?
|
|
+ }
|
|
+ return bukkitClass;
|
|
+ } else if (RangedAttackMob.class.isAssignableFrom(param)) {
|
|
+ return RangedEntity.class;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ throw new RuntimeException("Can't figure out applicable entity for mob goal " + goalClass); // maybe just return Mob?
|
|
+ });
|
|
+ }
|
|
+
|
|
+ public static Class<? extends Mob> toBukkitClass(Class<? extends net.minecraft.world.entity.Mob> nmsClass) {
|
|
+ return bukkitMap.get(nmsClass);
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/com/destroystokyo/paper/entity/ai/PaperCustomGoal.java b/src/main/java/com/destroystokyo/paper/entity/ai/PaperCustomGoal.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..26c745dd9ccdfdd5c5039f2acc5201b9b91fb274
|
|
--- /dev/null
|
|
+++ b/src/main/java/com/destroystokyo/paper/entity/ai/PaperCustomGoal.java
|
|
@@ -0,0 +1,53 @@
|
|
+package com.destroystokyo.paper.entity.ai;
|
|
+
|
|
+import org.bukkit.entity.Mob;
|
|
+
|
|
+/**
|
|
+ * Wraps api in vanilla
|
|
+ */
|
|
+public class PaperCustomGoal<T extends Mob> extends net.minecraft.world.entity.ai.goal.Goal {
|
|
+
|
|
+ private final Goal<T> handle;
|
|
+
|
|
+ public PaperCustomGoal(Goal<T> handle) {
|
|
+ this.handle = handle;
|
|
+
|
|
+ this.setFlags(MobGoalHelper.paperToVanilla(handle.getTypes()));
|
|
+ if (this.getFlags().size() == 0) {
|
|
+ this.getFlags().addUnchecked(Flag.UNKNOWN_BEHAVIOR);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canUse() {
|
|
+ return handle.shouldActivate();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canContinueToUse() {
|
|
+ return handle.shouldStayActive();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void start() {
|
|
+ handle.start();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void stop() {
|
|
+ handle.stop();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void tick() {
|
|
+ handle.tick();
|
|
+ }
|
|
+
|
|
+ public Goal<T> getHandle() {
|
|
+ return handle;
|
|
+ }
|
|
+
|
|
+ public GoalKey<T> getKey() {
|
|
+ return handle.getKey();
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/com/destroystokyo/paper/entity/ai/PaperMobGoals.java b/src/main/java/com/destroystokyo/paper/entity/ai/PaperMobGoals.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..e8a427ea777af040d0e2b9cc0ba2a80b9176d026
|
|
--- /dev/null
|
|
+++ b/src/main/java/com/destroystokyo/paper/entity/ai/PaperMobGoals.java
|
|
@@ -0,0 +1,226 @@
|
|
+package com.destroystokyo.paper.entity.ai;
|
|
+
|
|
+import java.util.Collection;
|
|
+import java.util.EnumSet;
|
|
+import java.util.HashSet;
|
|
+import java.util.LinkedList;
|
|
+import java.util.List;
|
|
+import java.util.Set;
|
|
+import net.minecraft.world.entity.ai.goal.GoalSelector;
|
|
+import net.minecraft.world.entity.ai.goal.WrappedGoal;
|
|
+import org.bukkit.craftbukkit.entity.CraftMob;
|
|
+import org.bukkit.entity.Mob;
|
|
+import org.jspecify.annotations.NullMarked;
|
|
+
|
|
+@NullMarked
|
|
+public class PaperMobGoals implements MobGoals {
|
|
+
|
|
+ @Override
|
|
+ public <T extends Mob> void addGoal(T mob, int priority, Goal<T> goal) {
|
|
+ CraftMob craftMob = (CraftMob) mob;
|
|
+ net.minecraft.world.entity.ai.goal.Goal mojangGoal;
|
|
+
|
|
+ if (goal instanceof PaperVanillaGoal vanillaGoal) {
|
|
+ mojangGoal = vanillaGoal.getHandle();
|
|
+ } else {
|
|
+ mojangGoal = new PaperCustomGoal<>(goal);
|
|
+ }
|
|
+
|
|
+ getHandle(craftMob, goal.getTypes()).addGoal(priority, mojangGoal);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public <T extends Mob> void removeGoal(T mob, Goal<T> goal) {
|
|
+ CraftMob craftMob = (CraftMob) mob;
|
|
+ if (goal instanceof PaperCustomGoal) {
|
|
+ getHandle(craftMob, goal.getTypes()).removeGoal((net.minecraft.world.entity.ai.goal.Goal) goal);
|
|
+ } else if (goal instanceof PaperVanillaGoal) {
|
|
+ getHandle(craftMob, goal.getTypes()).removeGoal(((PaperVanillaGoal<?>) goal).getHandle());
|
|
+ } else {
|
|
+ List<net.minecraft.world.entity.ai.goal.Goal> toRemove = new LinkedList<>();
|
|
+ for (WrappedGoal item : getHandle(craftMob, goal.getTypes()).getAvailableGoals()) {
|
|
+ if (item.getGoal() instanceof PaperCustomGoal) {
|
|
+ //noinspection unchecked
|
|
+ if (((PaperCustomGoal<T>) item.getGoal()).getHandle() == goal) {
|
|
+ toRemove.add(item.getGoal());
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ for (net.minecraft.world.entity.ai.goal.Goal g : toRemove) {
|
|
+ getHandle(craftMob, goal.getTypes()).removeGoal(g);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public <T extends Mob> void removeAllGoals(T mob) {
|
|
+ for (GoalType type : GoalType.values()) {
|
|
+ removeAllGoals(mob, type);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public <T extends Mob> void removeAllGoals(T mob, GoalType type) {
|
|
+ for (Goal<T> goal : getAllGoals(mob, type)) {
|
|
+ removeGoal(mob, goal);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public <T extends Mob> void removeGoal(T mob, GoalKey<T> key) {
|
|
+ for (Goal<T> goal : getGoals(mob, key)) {
|
|
+ removeGoal(mob, goal);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public <T extends Mob> boolean hasGoal(T mob, GoalKey<T> key) {
|
|
+ for (Goal<T> g : getAllGoals(mob)) {
|
|
+ if (g.getKey().equals(key)) {
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public <T extends Mob> Goal<T> getGoal(T mob, GoalKey<T> key) {
|
|
+ for (Goal<T> g : getAllGoals(mob)) {
|
|
+ if (g.getKey().equals(key)) {
|
|
+ return g;
|
|
+ }
|
|
+ }
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public <T extends Mob> Collection<Goal<T>> getGoals(T mob, GoalKey<T> key) {
|
|
+ Set<Goal<T>> goals = new HashSet<>();
|
|
+ for (Goal<T> g : getAllGoals(mob)) {
|
|
+ if (g.getKey().equals(key)) {
|
|
+ goals.add(g);
|
|
+ }
|
|
+ }
|
|
+ return goals;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public <T extends Mob> Collection<Goal<T>> getAllGoals(T mob) {
|
|
+ Set<Goal<T>> goals = new HashSet<>();
|
|
+ for (GoalType type : GoalType.values()) {
|
|
+ goals.addAll(getAllGoals(mob, type));
|
|
+ }
|
|
+ return goals;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public <T extends Mob> Collection<Goal<T>> getAllGoals(T mob, GoalType type) {
|
|
+ CraftMob craftMob = (CraftMob) mob;
|
|
+ Set<Goal<T>> goals = new HashSet<>();
|
|
+ for (WrappedGoal item : getHandle(craftMob, type).getAvailableGoals()) {
|
|
+ if (!item.getGoal().getFlags().hasElement(MobGoalHelper.paperToVanilla(type))) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (item.getGoal() instanceof PaperCustomGoal) {
|
|
+ //noinspection unchecked
|
|
+ goals.add(((PaperCustomGoal<T>) item.getGoal()).getHandle());
|
|
+ } else {
|
|
+ goals.add(item.getGoal().asPaperVanillaGoal());
|
|
+ }
|
|
+ }
|
|
+ return goals;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public <T extends Mob> Collection<Goal<T>> getAllGoalsWithout(T mob, GoalType type) {
|
|
+ CraftMob craftMob = (CraftMob) mob;
|
|
+ Set<Goal<T>> goals = new HashSet<>();
|
|
+ for (GoalType internalType : GoalType.values()) {
|
|
+ if (internalType == type) {
|
|
+ continue;
|
|
+ }
|
|
+ for (WrappedGoal item : getHandle(craftMob, internalType).getAvailableGoals()) {
|
|
+ if (item.getGoal().getFlags().hasElement(MobGoalHelper.paperToVanilla(type))) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (item.getGoal() instanceof PaperCustomGoal) {
|
|
+ //noinspection unchecked
|
|
+ goals.add(((PaperCustomGoal<T>) item.getGoal()).getHandle());
|
|
+ } else {
|
|
+ goals.add(item.getGoal().asPaperVanillaGoal());
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return goals;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public <T extends Mob> Collection<Goal<T>> getRunningGoals(T mob) {
|
|
+ Set<Goal<T>> goals = new HashSet<>();
|
|
+ for (GoalType type : GoalType.values()) {
|
|
+ goals.addAll(getRunningGoals(mob, type));
|
|
+ }
|
|
+ return goals;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public <T extends Mob> Collection<Goal<T>> getRunningGoals(T mob, GoalType type) {
|
|
+ CraftMob craftMob = (CraftMob) mob;
|
|
+ Set<Goal<T>> goals = new HashSet<>();
|
|
+ getHandle(craftMob, type).getAvailableGoals()
|
|
+ .stream().filter(WrappedGoal::isRunning)
|
|
+ .filter(item -> item.getGoal().getFlags().hasElement(MobGoalHelper.paperToVanilla(type)))
|
|
+ .forEach(item -> {
|
|
+ if (item.getGoal() instanceof PaperCustomGoal) {
|
|
+ //noinspection unchecked
|
|
+ goals.add(((PaperCustomGoal<T>) item.getGoal()).getHandle());
|
|
+ } else {
|
|
+ goals.add(item.getGoal().asPaperVanillaGoal());
|
|
+ }
|
|
+ });
|
|
+ return goals;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public <T extends Mob> Collection<Goal<T>> getRunningGoalsWithout(T mob, GoalType type) {
|
|
+ CraftMob craftMob = (CraftMob) mob;
|
|
+ Set<Goal<T>> goals = new HashSet<>();
|
|
+ for (GoalType internalType : GoalType.values()) {
|
|
+ if (internalType == type) {
|
|
+ continue;
|
|
+ }
|
|
+ getHandle(craftMob, internalType).getAvailableGoals()
|
|
+ .stream()
|
|
+ .filter(WrappedGoal::isRunning)
|
|
+ .filter(item -> !item.getGoal().getFlags().hasElement(MobGoalHelper.paperToVanilla(type)))
|
|
+ .forEach(item -> {
|
|
+ if (item.getGoal() instanceof PaperCustomGoal) {
|
|
+ //noinspection unchecked
|
|
+ goals.add(((PaperCustomGoal<T>) item.getGoal()).getHandle());
|
|
+ } else {
|
|
+ goals.add(item.getGoal().asPaperVanillaGoal());
|
|
+ }
|
|
+ });
|
|
+ }
|
|
+ return goals;
|
|
+ }
|
|
+
|
|
+ private GoalSelector getHandle(CraftMob mob, EnumSet<GoalType> types) {
|
|
+ if (types.contains(GoalType.TARGET)) {
|
|
+ return mob.getHandle().targetSelector;
|
|
+ } else {
|
|
+ return mob.getHandle().goalSelector;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private GoalSelector getHandle(CraftMob mob, GoalType type) {
|
|
+ if (type == GoalType.TARGET) {
|
|
+ return mob.getHandle().targetSelector;
|
|
+ } else {
|
|
+ return mob.getHandle().goalSelector;
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/com/destroystokyo/paper/entity/ai/PaperVanillaGoal.java b/src/main/java/com/destroystokyo/paper/entity/ai/PaperVanillaGoal.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..b5c594a5499556ad452d9939c75e150af8252e90
|
|
--- /dev/null
|
|
+++ b/src/main/java/com/destroystokyo/paper/entity/ai/PaperVanillaGoal.java
|
|
@@ -0,0 +1,61 @@
|
|
+package com.destroystokyo.paper.entity.ai;
|
|
+
|
|
+import java.util.EnumSet;
|
|
+import net.minecraft.world.entity.ai.goal.Goal;
|
|
+import org.bukkit.entity.Mob;
|
|
+
|
|
+/**
|
|
+ * Wraps vanilla in api
|
|
+ */
|
|
+public class PaperVanillaGoal<T extends Mob> implements VanillaGoal<T> {
|
|
+
|
|
+ private final Goal handle;
|
|
+ private final GoalKey<T> key;
|
|
+
|
|
+ private final EnumSet<GoalType> types;
|
|
+
|
|
+ public PaperVanillaGoal(Goal handle) {
|
|
+ this.handle = handle;
|
|
+ this.key = MobGoalHelper.getKey(handle.getClass());
|
|
+ this.types = MobGoalHelper.vanillaToPaper(handle);
|
|
+ }
|
|
+
|
|
+ public Goal getHandle() {
|
|
+ return handle;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean shouldActivate() {
|
|
+ return handle.canUse();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean shouldStayActive() {
|
|
+ return handle.canContinueToUse();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void start() {
|
|
+ handle.start();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void stop() {
|
|
+ handle.stop();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void tick() {
|
|
+ handle.tick();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public GoalKey<T> getKey() {
|
|
+ return key;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public EnumSet<GoalType> getTypes() {
|
|
+ return types;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java b/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java
|
|
index a8d6d7054110b5d95830296699f004418dae10db..acc25b08ed3b9f978229fa017d23f9fa0da519e3 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java
|
|
@@ -62,7 +62,19 @@ public abstract class Goal {
|
|
return (ServerLevel)world;
|
|
}
|
|
|
|
+ // Paper start - Mob goal api
|
|
+ private com.destroystokyo.paper.entity.ai.PaperVanillaGoal<?> vanillaGoal;
|
|
+ public <T extends org.bukkit.entity.Mob> com.destroystokyo.paper.entity.ai.Goal<T> asPaperVanillaGoal() {
|
|
+ if(this.vanillaGoal == null) {
|
|
+ this.vanillaGoal = new com.destroystokyo.paper.entity.ai.PaperVanillaGoal<>(this);
|
|
+ }
|
|
+ //noinspection unchecked
|
|
+ return (com.destroystokyo.paper.entity.ai.Goal<T>) this.vanillaGoal;
|
|
+ }
|
|
+ // Paper end - Mob goal api
|
|
+
|
|
public static enum Flag {
|
|
+ UNKNOWN_BEHAVIOR, // Paper - add UNKNOWN_BEHAVIOR
|
|
MOVE,
|
|
LOOK,
|
|
JUMP,
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
index e5a534c3cae53811899d33664b0a985c2442582c..4108eebafddc9c271be8c6eb5afbdca70e5fbb89 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
@@ -2966,5 +2966,11 @@ public final class CraftServer implements Server {
|
|
public boolean isStopping() {
|
|
return net.minecraft.server.MinecraftServer.getServer().hasStopped();
|
|
}
|
|
+
|
|
+ private com.destroystokyo.paper.entity.ai.MobGoals mobGoals = new com.destroystokyo.paper.entity.ai.PaperMobGoals();
|
|
+ @Override
|
|
+ public com.destroystokyo.paper.entity.ai.MobGoals getMobGoals() {
|
|
+ return mobGoals;
|
|
+ }
|
|
// Paper end
|
|
}
|