From c441baf8796ea40a353a1c433a2b928f9e90605d Mon Sep 17 00:00:00 2001 From: Jazzkuh Date: Fri, 2 Jan 2026 01:16:05 +0100 Subject: [PATCH 1/2] Support 1.21.11 Update outdated dependencies for libdisguises and nocheatplus --- CompatibilityLib/main/pom.xml | 7 + CompatibilityLib/pom.xml | 1 + CompatibilityLib/v1_21_11/pom.xml | 76 ++ .../platform/v1_21_11/CompatibilityUtils.java | 901 ++++++++++++++++++ .../utility/platform/v1_21_11/ItemUtils.java | 201 ++++ .../utility/platform/v1_21_11/MobUtils.java | 865 +++++++++++++++++ .../utility/platform/v1_21_11/NBTUtils.java | 565 +++++++++++ .../utility/platform/v1_21_11/Platform.java | 31 + .../platform/v1_21_11/goal/IdleGoal.java | 10 + .../v1_21_11/goal/MagicCheckOwnerGoal.java | 24 + .../v1_21_11/goal/MagicFindOwnerGoal.java | 47 + .../v1_21_11/goal/MagicFollowMobGoal.java | 82 ++ .../v1_21_11/goal/MagicFollowOwnerGoal.java | 126 +++ .../platform/v1_21_11/goal/MagicGoal.java | 142 +++ .../v1_21_11/goal/MagicOwnerGoal.java | 26 + .../goal/MagicOwnerHurtByTargetGoal.java | 47 + .../goal/MagicOwnerHurtTargetGoal.java | 48 + .../v1_21_11/goal/MagicOwnerTargetGoal.java | 53 ++ .../v1_21_11/goal/MagicPanicGoal.java | 56 ++ .../platform/v1_21_11/goal/MagicTamed.java | 66 ++ .../v1_21_11/goal/MagicTemptGoal.java | 128 +++ .../v1_21_11/goal/RequirementsGoal.java | 49 + .../platform/v1_21_11/goal/SpinGoal.java | 25 + .../platform/v1_21_11/goal/TriggerGoal.java | 39 + Magic/pom.xml | 8 +- 25 files changed, 3621 insertions(+), 2 deletions(-) create mode 100644 CompatibilityLib/v1_21_11/pom.xml create mode 100644 CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/CompatibilityUtils.java create mode 100644 CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/ItemUtils.java create mode 100644 CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/MobUtils.java create mode 100644 CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/NBTUtils.java create mode 100644 CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/Platform.java create mode 100644 CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/IdleGoal.java create mode 100644 CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicCheckOwnerGoal.java create mode 100644 CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicFindOwnerGoal.java create mode 100644 CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicFollowMobGoal.java create mode 100644 CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicFollowOwnerGoal.java create mode 100644 CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicGoal.java create mode 100644 CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicOwnerGoal.java create mode 100644 CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicOwnerHurtByTargetGoal.java create mode 100644 CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicOwnerHurtTargetGoal.java create mode 100644 CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicOwnerTargetGoal.java create mode 100644 CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicPanicGoal.java create mode 100644 CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicTamed.java create mode 100644 CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicTemptGoal.java create mode 100644 CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/RequirementsGoal.java create mode 100644 CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/SpinGoal.java create mode 100644 CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/TriggerGoal.java diff --git a/CompatibilityLib/main/pom.xml b/CompatibilityLib/main/pom.xml index b7ae4518a1..7fb241a729 100644 --- a/CompatibilityLib/main/pom.xml +++ b/CompatibilityLib/main/pom.xml @@ -103,5 +103,12 @@ jar compile + + ${project.groupId} + CompatibilityLib-v1_21_11 + ${project.version} + jar + compile + diff --git a/CompatibilityLib/pom.xml b/CompatibilityLib/pom.xml index bddb41c8f6..b3ba76a6b5 100644 --- a/CompatibilityLib/pom.xml +++ b/CompatibilityLib/pom.xml @@ -57,6 +57,7 @@ v1_21_8 v1_21_9 v1_21_10 + v1_21_11 main diff --git a/CompatibilityLib/v1_21_11/pom.xml b/CompatibilityLib/v1_21_11/pom.xml new file mode 100644 index 0000000000..3d60ca9c33 --- /dev/null +++ b/CompatibilityLib/v1_21_11/pom.xml @@ -0,0 +1,76 @@ + + 4.0.0 + + + com.elmakers.mine.bukkit.compatibility + CompatibilityLib-parent + 10.10.6-SNAPSHOT + + CompatibilityLib-v1_21_11 + + + 1.21.11-R0.1-SNAPSHOT + + + + + org.spigotmc + spigot-api + ${bukkit.version} + provided + + + org.spigotmc + spigot + ${bukkit.version} + remapped-mojang + provided + + + ${project.groupId} + CompatibilityLib-base_v1_21_4 + ${project.version} + compile + + + + + + + net.md-5 + specialsource-maven-plugin + 2.0.3 + + + package + + remap + + remap-obf + + org.spigotmc:minecraft-server:${bukkit.version}:txt:maps-mojang + true + org.spigotmc:spigot:${bukkit.version}:jar:remapped-mojang + true + remapped-obf + + + + package + + remap + + remap-spigot + + ${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar + org.spigotmc:minecraft-server:${bukkit.version}:csrg:maps-spigot + org.spigotmc:spigot:${bukkit.version}:jar:remapped-obf + + + + + + + + diff --git a/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/CompatibilityUtils.java b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/CompatibilityUtils.java new file mode 100644 index 0000000000..88e423799e --- /dev/null +++ b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/CompatibilityUtils.java @@ -0,0 +1,901 @@ +package com.elmakers.mine.bukkit.utility.platform.v1_21_11; + +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Random; +import java.util.Set; +import java.util.UUID; +import java.util.logging.Level; + +import net.minecraft.world.entity.projectile.FishingHook; +import net.minecraft.world.entity.projectile.LlamaSpit; +import net.minecraft.world.entity.projectile.ShulkerBullet; +import net.minecraft.world.entity.projectile.arrow.Arrow; +import net.minecraft.world.entity.projectile.arrow.SpectralArrow; +import net.minecraft.world.entity.projectile.arrow.ThrownTrident; +import net.minecraft.world.entity.projectile.hurtingprojectile.*; +import net.minecraft.world.entity.projectile.throwableitemprojectile.*; +import org.bukkit.Art; +import org.bukkit.Bukkit; +import org.bukkit.FireworkEffect; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Rotation; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.boss.BossBar; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.craftbukkit.v1_21_R7.CraftArt; +import org.bukkit.craftbukkit.v1_21_R7.CraftWorld; +import org.bukkit.craftbukkit.v1_21_R7.block.CraftBlock; +import org.bukkit.craftbukkit.v1_21_R7.entity.*; +import org.bukkit.craftbukkit.v1_21_R7.scheduler.CraftTask; +import org.bukkit.craftbukkit.v1_21_R7.util.CraftChatMessage; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.Damageable; +import org.bukkit.entity.Enderman; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.FallingBlock; +import org.bukkit.entity.Hanging; +import org.bukkit.entity.Item; +import org.bukkit.entity.ItemFrame; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Painting; +import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; +import org.bukkit.entity.ThrownPotion; +import org.bukkit.entity.Witch; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.event.entity.ProjectileHitEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.FireworkMeta; +import org.bukkit.projectiles.ProjectileSource; +import org.bukkit.scheduler.BukkitTask; +import org.bukkit.util.BlockVector; +import org.bukkit.util.Vector; + +import com.elmakers.mine.bukkit.utility.ChatUtils; +import com.elmakers.mine.bukkit.utility.CompatibilityConstants; +import com.elmakers.mine.bukkit.utility.EnteredStateTracker; +import com.elmakers.mine.bukkit.utility.ReflectionUtils; +import com.elmakers.mine.bukkit.utility.platform.ItemUtils; +import com.elmakers.mine.bukkit.utility.platform.Platform; +import com.elmakers.mine.bukkit.utility.platform.SpigotUtils; +import com.elmakers.mine.bukkit.utility.platform.base_v1_21_4.CompatibilityUtilsBase_v1_21_4; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Holder; +import net.minecraft.core.component.DataComponents; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.network.chat.CommonComponents; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.ClientboundAddEntityPacket; +import net.minecraft.network.protocol.game.ClientboundBlockDestructionPacket; +import net.minecraft.network.protocol.game.ClientboundEntityEventPacket; +import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket; +import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket; +import net.minecraft.network.protocol.game.ClientboundSystemChatPacket; +import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.server.level.ServerBossEvent; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.util.ProblemReporter; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.damagesource.DamageSources; +import net.minecraft.world.entity.decoration.HangingEntity; +import net.minecraft.world.entity.item.FallingBlockEntity; +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.entity.projectile.FireworkRocketEntity; +import net.minecraft.world.item.BoneMealItem; +import net.minecraft.world.item.component.ItemLore; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.storage.TagValueOutput; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.Vec3; + +public class CompatibilityUtils extends CompatibilityUtilsBase_v1_21_4 { + private final Map> projectileEntityTypes = new HashMap<>(); + private final Map> projectileClasses = new HashMap<>(); + + public CompatibilityUtils(Platform platform) { + super(platform); + populateProjectileClasses(); + } + + private void addProjectileClass(String key, Class projectileClass, net.minecraft.world.entity.EntityType entityType) { + projectileClasses.put(key, projectileClass); + projectileEntityTypes.put(projectileClass.getSimpleName(), entityType); + } + + private void populateProjectileClasses() { + // Can't use reflection, so gonna do this the hard (coded) way. + addProjectileClass("arrow", Arrow.class, net.minecraft.world.entity.EntityType.ARROW); + addProjectileClass("tippedarrow", Arrow.class, net.minecraft.world.entity.EntityType.ARROW); + addProjectileClass("tipped_arrow", Arrow.class, net.minecraft.world.entity.EntityType.ARROW); + addProjectileClass("dragonfireball", DragonFireball.class, net.minecraft.world.entity.EntityType.DRAGON_FIREBALL); + addProjectileClass("dragon_fireball", DragonFireball.class, net.minecraft.world.entity.EntityType.DRAGON_FIREBALL); + addProjectileClass("fireball", LargeFireball.class, net.minecraft.world.entity.EntityType.FIREBALL); + addProjectileClass("largefireball", LargeFireball.class, net.minecraft.world.entity.EntityType.FIREBALL); + addProjectileClass("large_fireball", LargeFireball.class, net.minecraft.world.entity.EntityType.FIREBALL); + addProjectileClass("smallfireball", SmallFireball.class, net.minecraft.world.entity.EntityType.SMALL_FIREBALL); + addProjectileClass("small_fireball", SmallFireball.class, net.minecraft.world.entity.EntityType.SMALL_FIREBALL); + addProjectileClass("fireworks", FireworkRocketEntity.class, net.minecraft.world.entity.EntityType.FIREWORK_ROCKET); + addProjectileClass("firework", FireworkRocketEntity.class, net.minecraft.world.entity.EntityType.FIREWORK_ROCKET); + addProjectileClass("fireworkrocket", FireworkRocketEntity.class, net.minecraft.world.entity.EntityType.FIREWORK_ROCKET); + addProjectileClass("firework_rocket", FireworkRocketEntity.class, net.minecraft.world.entity.EntityType.FIREWORK_ROCKET); + addProjectileClass("fireworkrocketentity", FireworkRocketEntity.class, net.minecraft.world.entity.EntityType.FIREWORK_ROCKET); + addProjectileClass("fishinghook", FishingHook.class, net.minecraft.world.entity.EntityType.FISHING_BOBBER); + addProjectileClass("fishing_hook", FishingHook.class, net.minecraft.world.entity.EntityType.FISHING_BOBBER); + addProjectileClass("fishing_bobber", FishingHook.class, net.minecraft.world.entity.EntityType.FISHING_BOBBER); + addProjectileClass("llamaspit", LlamaSpit.class, net.minecraft.world.entity.EntityType.LLAMA_SPIT); + addProjectileClass("llama_spit", LlamaSpit.class, net.minecraft.world.entity.EntityType.LLAMA_SPIT); + addProjectileClass("shulkerbullet", ShulkerBullet.class, net.minecraft.world.entity.EntityType.SHULKER_BULLET); + addProjectileClass("shulker_bullet", ShulkerBullet.class, net.minecraft.world.entity.EntityType.SHULKER_BULLET); + addProjectileClass("snowball", Snowball.class, net.minecraft.world.entity.EntityType.SNOWBALL); + addProjectileClass("spectralarrow", SpectralArrow.class, net.minecraft.world.entity.EntityType.SPECTRAL_ARROW); + addProjectileClass("spectral_arrow", SpectralArrow.class, net.minecraft.world.entity.EntityType.SPECTRAL_ARROW); + addProjectileClass("egg", ThrownEgg.class, net.minecraft.world.entity.EntityType.EGG); + addProjectileClass("thrownegg", ThrownEgg.class, net.minecraft.world.entity.EntityType.EGG); + addProjectileClass("enderpearl", ThrownEnderpearl.class, net.minecraft.world.entity.EntityType.ENDER_PEARL); + addProjectileClass("ender_pearl", ThrownEnderpearl.class, net.minecraft.world.entity.EntityType.ENDER_PEARL); + addProjectileClass("thrownenderpearl", ThrownEnderpearl.class, net.minecraft.world.entity.EntityType.ENDER_PEARL); + addProjectileClass("thrownexperiencebottle", ThrownExperienceBottle.class, net.minecraft.world.entity.EntityType.EXPERIENCE_BOTTLE); + addProjectileClass("experiencebottle", ThrownExperienceBottle.class, net.minecraft.world.entity.EntityType.EXPERIENCE_BOTTLE); + addProjectileClass("thrownpotion", ThrownSplashPotion.class, net.minecraft.world.entity.EntityType.SPLASH_POTION); + addProjectileClass("potion", ThrownSplashPotion.class, net.minecraft.world.entity.EntityType.SPLASH_POTION); + addProjectileClass("throwntrident", ThrownTrident.class, net.minecraft.world.entity.EntityType.TRIDENT); + addProjectileClass("trident", ThrownTrident.class, net.minecraft.world.entity.EntityType.TRIDENT); + addProjectileClass("witherskull", WitherSkull.class, net.minecraft.world.entity.EntityType.WITHER_SKULL); + addProjectileClass("wither_skull", WitherSkull.class, net.minecraft.world.entity.EntityType.WITHER_SKULL); + } + + @Override + public Painting createPainting(Location location, BlockFace facing, Art art) { + Painting newPainting = null; + ServerLevel level = ((CraftWorld)location.getWorld()).getHandle(); + Direction directionEnum = null; + try { + directionEnum = Direction.valueOf(facing.name()); + } catch (Exception ex) { + ex.printStackTrace(); + return null; + } + BlockPos blockLocation = new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()); + net.minecraft.world.entity.decoration.painting.Painting newEntity = new net.minecraft.world.entity.decoration.painting.Painting(level, blockLocation, directionEnum, Holder.direct(CraftArt.bukkitToMinecraft(art))); + Entity bukkitEntity = newEntity.getBukkitEntity(); + if (bukkitEntity != null && bukkitEntity instanceof Painting) { + newPainting = (Painting)bukkitEntity; + } + return newPainting; + } + + @Override + public ItemFrame createItemFrame(Location location, BlockFace facing, Rotation rotation, ItemStack item) { + ItemFrame newItemFrame = null; + ServerLevel level = ((CraftWorld)location.getWorld()).getHandle(); + Direction directionEnum = null; + try { + directionEnum = Direction.valueOf(facing.name()); + } catch (Exception ex) { + ex.printStackTrace(); + return null; + } + BlockPos blockLocation = new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()); + net.minecraft.world.entity.decoration.ItemFrame newEntity = new net.minecraft.world.entity.decoration.ItemFrame(level, blockLocation, directionEnum); + Entity bukkitEntity = newEntity.getBukkitEntity(); + if (bukkitEntity != null && bukkitEntity instanceof ItemFrame) { + newItemFrame = (ItemFrame)bukkitEntity; + newItemFrame.setItem(platform.getItemUtils().getCopy(item)); + newItemFrame.setRotation(rotation); + } + return newItemFrame; + } + + @Override + public boolean addToWorld(World world, Entity entity, CreatureSpawnEvent.SpawnReason reason) { + ServerLevel level = ((CraftWorld)world).getHandle(); + net.minecraft.world.entity.Entity entityHandle = ((CraftEntity)entity).getHandle(); + level.addFreshEntity(entityHandle, reason); + return true; + } + + @Override + public void ageItem(Item item, int ticksToAge) { + ItemEntity nmsItem = ((CraftItem)item).getHandle(); + nmsItem.age = ticksToAge; + } + + @Override + public void magicDamage(Damageable target, double amount, Entity source) { + try { + if (target == null || target.isDead()) return; + + // Special-case for witches .. witches are immune to magic damage :\ + // And endermen are immune to indirect damage .. or something. + // Also armor stands suck. + // Might need to config-drive this, or just go back to defaulting to normal damage + if (!USE_MAGIC_DAMAGE || target instanceof Witch || target instanceof Enderman || target instanceof ArmorStand || !(target instanceof LivingEntity)) { + damage(target, amount, source); + return; + } + + net.minecraft.world.entity.Entity targetHandle = ((CraftEntity)target).getHandle(); + if (targetHandle == null) return; + + net.minecraft.world.entity.Entity sourceHandle = source == null ? null : ((CraftEntity)source).getHandle(); + + // Bukkit won't allow magic damage from anything but a potion.. + if (sourceHandle != null && source instanceof LivingEntity) { + Location location = target.getLocation(); + + ThrownPotion potion = getOrCreatePotionEntity(location); + net.minecraft.world.entity.Entity potionHandle = ((CraftEntity)potion).getHandle(); + potion.setShooter((LivingEntity) source); + + DamageSource magicSource = sourceHandle.damageSources().indirectMagic(potionHandle, sourceHandle); + + /* + // We can't modify the damage source anymore, it is taken from a common registry, rather than + // created on demand. We'll need to check and make sure that the ender dragon is damageable from spells. + // This is a bit of hack that lets us damage the ender dragon, who is a weird and annoying collection + // of various non-living entity pieces. + ((EntityDamageSource)magicSource).setThorns(); + */ + + try (EnteredStateTracker.Touchable damaging = isDamaging.enter()) { + damaging.touch(); + targetHandle.hurt(magicSource, (float)amount); + } + } else { + try (EnteredStateTracker.Touchable damaging = isDamaging.enter()) { + damaging.touch(); + targetHandle.hurt(targetHandle.damageSources().magic(), (float)amount); + } + } + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + protected DamageSource getDamageSource(String damageType, net.minecraft.world.entity.Entity source, DamageSources damageSources) { + switch (damageType.toUpperCase()) { + case "IN_FIRE" : return damageSources.inFire(); + case "LIGHTNING_BOLT" : return damageSources.lightningBolt(); + case "ON_FIRE" : return damageSources.onFire(); + case "LAVA" : return damageSources.lava(); + case "HOT_FLOOR" : return damageSources.hotFloor(); + case "IN_WALL" : return damageSources.inWall(); + case "CRAMMING" : return damageSources.cramming(); + case "DROWN" : return damageSources.drown(); + case "STARVE" : return damageSources.starve(); + case "CACTUS" : return damageSources.cactus(); + case "FALL" : return damageSources.fall(); + case "FLY_INTO_WALL" : return damageSources.flyIntoWall(); + case "OUT_OF_WORLD" : return damageSources.fellOutOfWorld(); + case "GENERIC" : return damageSources.generic(); + case "MAGIC" : return damageSources.magic(); + case "WITHER" : return damageSources.wither(); + case "ANVIL" : return damageSources.anvil(source); + case "FALLING_BLOCK" : return damageSources.fallingBlock(source); + case "DRAGON_BREATH" : return damageSources.dragonBreath(); + case "DRY_OUT" : return damageSources.dryOut(); + case "SWEET_BERRY_BUSH" : return damageSources.sweetBerryBush(); + case "FREEZE" : return damageSources.freeze(); + case "FALLING_STALACTITE" : return damageSources.fallingStalactite(source); + case "STALAGMITE" : return damageSources.stalagmite(); + default: return null; + } + } + + @Override + public void damage(Damageable target, double amount, Entity source, String damageType) { + if (target == null || target.isDead()) return; + if (damageType.equalsIgnoreCase("direct")) { + double health = target.getHealth() - amount; + target.setHealth(Math.max(health, 0)); + return; + } + if (damageType.equalsIgnoreCase("magic")) { + magicDamage(target, amount, source); + return; + } + net.minecraft.world.entity.Entity targetHandle = ((CraftEntity)target).getHandle(); + if (targetHandle == null) return; + net.minecraft.world.entity.Entity sourceHandle = ((CraftEntity)source).getHandle(); + DamageSource damageSource = getDamageSource(damageType, sourceHandle, targetHandle.damageSources()); + if (damageSource == null) { + magicDamage(target, amount, source); + return; + } + + try (EnteredStateTracker.Touchable damaging = isDamaging.enter()) { + damaging.touch(); + targetHandle.hurt(damageSource, (float)amount); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + @Override + public Vector getPosition(Object entityData, String tag) { + if (entityData == null || !(entityData instanceof CompoundTag)) return null; + CompoundTag data = (CompoundTag)entityData; + Optional optionalList = data.getList(tag); + if (!optionalList.isPresent()) return null; + ListTag list = optionalList.get(); + Optional optionalX = list.getDouble(0); + Optional optionalY = list.getDouble(1); + Optional optionalZ = list.getDouble(2); + if (!optionalX.isPresent() || !optionalY.isPresent() || !optionalZ.isPresent()) return null; + double x = optionalX.get(); + double y = optionalY.get(); + double z = optionalZ.get(); + return new Vector(x, y, z); + } + + @Override + public BlockVector getBlockVector(Object entityData, String tag) { + if (entityData == null || !(entityData instanceof CompoundTag)) return null; + CompoundTag data = (CompoundTag)entityData; + Optional optionalArray = data.getIntArray(tag); + if (!optionalArray.isPresent()) return null; + int[] coords = optionalArray.get(); + if (coords.length < 3) return null; + return new BlockVector(coords[0], coords[1], coords[2]); + } + + @Override + public void setEntityMotion(Entity entity, Vector motion) { + net.minecraft.world.entity.Entity nms = ((CraftEntity)entity).getHandle(); + nms.setDeltaMovement(new Vec3(motion.getX(), motion.getY(), motion.getZ())); + } + + @Override + public void setFallingBlockDamage(FallingBlock entity, float fallHurtAmount, int fallHurtMax) { + entity.setHurtEntities(true); + FallingBlockEntity nms = (FallingBlockEntity)((CraftEntity)entity).getHandle(); + nms.fallDamageMax = fallHurtMax; + nms.fallDamagePerDistance = fallHurtAmount; + } + + @Override + public void setDisabledSlots(ArmorStand armorStand, int disabledSlots) { + net.minecraft.world.entity.decoration.ArmorStand nms = ((CraftArmorStand)armorStand).getHandle(); + nms.disabledSlots = disabledSlots; + } + + @Override + public int getDisabledSlots(ArmorStand armorStand) { + net.minecraft.world.entity.decoration.ArmorStand nms = ((CraftArmorStand)armorStand).getHandle(); + return nms.disabledSlots; + } + + @Override + public void setInvisible(Entity entity, boolean invisible) { + net.minecraft.world.entity.Entity nms = ((CraftEntity)entity).getHandle(); + nms.setInvisible(invisible); + } + + @Override + public Boolean isInvisible(Entity entity) { + net.minecraft.world.entity.Entity nms = ((CraftEntity)entity).getHandle(); + return nms.isInvisible(); + } + + @Override + public boolean isPersistentInvisible(Entity entity) { + net.minecraft.world.entity.Entity nms = ((CraftEntity)entity).getHandle(); + return nms.persistentInvisibility; + } + + @Override + public void setPersistentInvisible(Entity entity, boolean invisible) { + net.minecraft.world.entity.Entity nms = ((CraftEntity)entity).getHandle(); + nms.persistentInvisibility = invisible; + } + + @Override + public void setYawPitch(Entity entity, float yaw, float pitch) { + net.minecraft.world.entity.Entity nms = ((CraftEntity)entity).getHandle(); + // Entity setYawPitch is protected, but I think we can do without the extra + // CraftBukkit checks ... er, hopefully. + nms.setYRot(yaw % 360.0F); + nms.setXRot(pitch % 360.0F); + } + + @Override + public void setLocation(Entity entity, double x, double y, double z, float yaw, float pitch) { + net.minecraft.world.entity.Entity nms = ((CraftEntity)entity).getHandle(); + nms.absSnapTo(x, y, z, yaw, pitch); + } + + @Override + public void addFlightExemption(Player player, int ticks) { + ServerPlayer nms = ((CraftPlayer)player).getHandle(); + // net.minecraft.server.network.PlayerConnection + ReflectionUtils.setPrivateNeedsFixing(platform.getLogger(), nms.connection, ServerGamePacketListenerImpl.class, "aboveGroundTickCount", "J", -ticks); + } + + @Override + public boolean isValidProjectileClass(Class projectileType) { + return projectileType != null && Projectile.class.isAssignableFrom(projectileType); + } + + @Override + public Projectile spawnProjectile(Class projectileType, Location location, Vector direction, ProjectileSource source, float speed, float spread, float spreadLocations, Random random) { + Constructor constructor = null; + ServerLevel nmsWorld = ((CraftWorld)location.getWorld()).getHandle(); + Projectile projectile = null; + try { + Object entityType = null; + constructor = projectileType.getConstructor(net.minecraft.world.entity.EntityType.class, net.minecraft.world.level.Level.class); + entityType = projectileEntityTypes.get(projectileType.getSimpleName()); + if (entityType == null) { + throw new Exception("Failed to find entity type for projectile class " + projectileType.getName()); + } + + Object nmsProjectile = null; + try { + nmsProjectile = constructor.newInstance(entityType, nmsWorld); + } catch (Exception ex) { + nmsProjectile = null; + platform.getLogger().log(Level.WARNING, "Error spawning projectile of class " + projectileType.getName(), ex); + } + + if (nmsProjectile == null || !(nmsProjectile instanceof net.minecraft.world.entity.Entity)) { + throw new Exception("Failed to spawn projectile of class " + projectileType.getName()); + } + + // Set position and rotation, and potentially velocity (direction) + // Velocity must be set manually- EntityFireball.setDirection applies a crazy-wide gaussian distribution! + if (nmsProjectile instanceof AbstractHurtingProjectile) { + AbstractHurtingProjectile fireballIsh = (AbstractHurtingProjectile)nmsProjectile; + // Taken from EntityArrow + double spreadWeight = Math.min(0.4f, spread * 0.007499999832361937D); + + double dx = direction.getX() + (random.nextGaussian() * spreadWeight); + double dy = direction.getY() + (random.nextGaussian() * spreadWeight); + double dz = direction.getZ() + (random.nextGaussian() * spreadWeight); + + fireballIsh.assignDirectionalMovement(new Vec3(dx, dy, dz), speed * 0.1D); + } + + net.minecraft.world.entity.Entity nmsEntity = ((net.minecraft.world.entity.Entity)nmsProjectile); + Vector modifiedLocation = location.toVector().clone(); + if (Fireball.class.isAssignableFrom(projectileType) && spreadLocations > 0) { + modifiedLocation.setX(modifiedLocation.getX() + direction.getX() + (random.nextGaussian() * spread / 5)); + modifiedLocation.setY(modifiedLocation.getY() + direction.getY() + (random.nextGaussian() * spread / 5)); + modifiedLocation.setZ(modifiedLocation.getZ() + direction.getZ() + (random.nextGaussian() * spread / 5)); + } + nmsEntity.snapTo(modifiedLocation.getX(), modifiedLocation.getY(), modifiedLocation.getZ(), location.getYaw(), location.getPitch()); + + if (nmsEntity instanceof net.minecraft.world.entity.projectile.Projectile) { + net.minecraft.world.entity.projectile.Projectile nms = (net.minecraft.world.entity.projectile.Projectile)nmsEntity; + nms.shoot(direction.getX(), direction.getY(), direction.getZ(), speed, spread); + } + + Entity entity = nmsEntity.getBukkitEntity(); + if (entity == null || !(entity instanceof Projectile)) { + throw new Exception("Got invalid bukkit entity from projectile of class " + projectileType.getName()); + } + + projectile = (Projectile)entity; + if (source != null) { + projectile.setShooter(source); + nmsEntity.projectileSource = source; + } + + nmsWorld.addFreshEntity(nmsEntity, CreatureSpawnEvent.SpawnReason.DEFAULT); + } catch (Throwable ex) { + ex.printStackTrace(); + return null; + } + + return projectile; + } + + @Override + public void setDamage(Projectile projectile, double damage) { + net.minecraft.world.entity.Entity nms = ((CraftEntity)projectile).getHandle(); + if (!(nms instanceof Arrow)) { + return; + } + Arrow arrow = (Arrow)nms; + arrow.setBaseDamage(damage); + } + + @Override + public void decreaseLifespan(Projectile projectile, int ticks) { + net.minecraft.world.entity.Entity nms = ((CraftEntity)projectile).getHandle(); + if (!(nms instanceof Arrow)) { + return; + } + Arrow arrow = (Arrow)nms; + arrow.life = ticks; + } + + @Override + public Entity spawnEntity(Location target, EntityType entityType, CreatureSpawnEvent.SpawnReason spawnReason) { + CraftWorld craftWorld = (CraftWorld)target.getWorld(); + return craftWorld.spawn(target, entityType.getEntityClass(), null, spawnReason); + } + + @Override + public CompoundTag getEntityData(Entity entity) { + if (entity == null) return null; + TagValueOutput valueOutput = TagValueOutput.createWithoutContext(ProblemReporter.DISCARDING); + ((CraftEntity)entity).getHandle().save(valueOutput); + return valueOutput.buildResult(); + } + + @Override + public String getEntityType(Entity entity) { + if (entity == null) return null; + return ((CraftEntity)entity).getHandle().getEncodeId(); + } + + protected void sendPacket(Server server, Location source, Collection players, Packet packet) throws Exception { + players = ((players != null && players.size() > 0) ? players : server.getOnlinePlayers()); + + int viewDistance = Bukkit.getServer().getViewDistance() * 16; + int viewDistanceSquared = viewDistance * viewDistance; + World sourceWorld = source.getWorld(); + for (Player player : players) { + Location location = player.getLocation(); + if (!location.getWorld().equals(sourceWorld)) continue; + if (location.distanceSquared(source) <= viewDistanceSquared) { + sendPacket(player, packet); + } + } + } + + protected void sendPacket(Player player, Packet packet) throws Exception { + ServerPlayer nmsPlayer = ((CraftPlayer)player).getHandle(); + nmsPlayer.connection.send(packet); + } + + @Override + public void sendBreaking(Player player, long id, Location location, int breakAmount) { + try { + BlockPos blockPosition = new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()); + Packet packet = new ClientboundBlockDestructionPacket((int)id, blockPosition, breakAmount); + sendPacket(player, packet); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + @Override + public Block getHitBlock(ProjectileHitEvent event) { + return event.getHitBlock(); + } + + @Override + public Entity getEntity(World world, UUID uuid) { + net.minecraft.world.entity.Entity nmsEntity = ((CraftWorld)world).getHandle().getEntity(uuid); + return nmsEntity == null ? null : nmsEntity.getBukkitEntity(); + } + + @Override + public boolean applyBonemeal(Location location) { + if (dummyItem == null) { + dummyItem = new ItemStack(Material.DIRT, 64); + dummyItem = platform.getItemUtils().makeReal(dummyItem); + } + dummyItem.setAmount(64); + ServerLevel nmsWorld = ((CraftWorld)location.getWorld()).getHandle(); + net.minecraft.world.item.ItemStack itemStack = (net.minecraft.world.item.ItemStack)platform.getItemUtils().getHandle(dummyItem); + BlockPos blockPosition = new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()); + return BoneMealItem.growCrop(itemStack, nmsWorld, blockPosition); + } + + @Override + public boolean applyPhysics(Block block) { + ServerLevel nmsWorld = ((CraftWorld)block.getWorld()).getHandle(); + BlockPos blockLocation = new BlockPos(block.getX(), block.getY(), block.getZ()); + net.minecraft.world.level.block.state.BlockState blockState = nmsWorld.getBlockState(blockLocation); + clearItems(block.getLocation()); + platform.getDeprecatedUtils().setTypeAndData(block, Material.AIR, (byte)0, false); + return nmsWorld.setBlock(blockLocation, blockState, 3); + } + + @Override + public Location getHangingLocation(Entity entity) { + Location location = entity.getLocation(); + if (!(entity instanceof Hanging)) return location; + HangingEntity nms = ((CraftHanging)entity).getHandle(); + BlockPos position = nms.blockPosition(); + location.setX(position.getX()); + location.setY(position.getY()); + location.setZ(position.getZ()); + return location; + } + + @Override + public boolean setAutoBlockState(Block block, Location target, BlockFace facing, boolean physics, Player originator) { + if (block == null || facing == null || target == null) return false; + net.minecraft.world.level.block.state.BlockState blockState = ((CraftBlock)block).getNMS(); + if (blockState == null) return false; + net.minecraft.world.level.block.Block nmsBlock = blockState.getBlock(); + ItemStack blockItem = new ItemStack(block.getType()); + ServerPlayer originatorHandle = ((CraftPlayer)originator).getHandle(); + ServerLevel world = ((CraftWorld)block.getWorld()).getHandle(); + Object item = platform.getItemUtils().getHandle(platform.getItemUtils().makeReal(blockItem)); + if (originatorHandle == null || world == null || item == null) { + return false; + } + BlockPos blockPosition = new BlockPos(block.getX(), block.getY(), block.getZ()); + Vec3 vec3D = new Vec3(target.getX(), target.getY(), target.getZ()); + Direction direction; + try { + direction = Direction.valueOf(facing.name()); + } catch (Exception ex) { + platform.getLogger().log(Level.SEVERE, "Could not translate to NMS direction: " + facing); + return false; + } + BlockHitResult hitResult = new BlockHitResult(vec3D, direction, blockPosition, false); + BlockPlaceContext actionContext = new BlockPlaceContext(originatorHandle, InteractionHand.MAIN_HAND, (net.minecraft.world.item.ItemStack)item, hitResult); + net.minecraft.world.level.block.state.BlockState state = nmsBlock.getStateForPlacement(actionContext); + if (state == null) return false; + CraftBlock cBlock = (CraftBlock)block; + CraftBlock.setTypeAndData(cBlock.getHandle(), cBlock.getPosition(), cBlock.getNMS(), state, physics); + return true; + } + + @Override + public boolean forceUpdate(Block block, boolean physics) { + if (block == null) return false; + net.minecraft.world.level.block.state.BlockState blockState = ((CraftBlock)block).getNMS(); + if (blockState == null) return false; + net.minecraft.world.level.block.Block nmsBlock = blockState.getBlock(); + net.minecraft.world.level.block.state.BlockState blockData = nmsBlock.defaultBlockState(); + ServerLevel world = ((CraftWorld)block.getWorld()).getHandle(); + BlockPos blockPosition = new BlockPos(block.getX(), block.getY(), block.getZ()); + world.setBlock(blockPosition, blockData, 11); + return false; + } + + @Override + public Class getProjectileClass(String projectileTypeName) { + return projectileClasses.get(projectileTypeName.toLowerCase()); + } + + @Override + public Entity spawnFireworkEffect(Material fireworkMaterial, Server server, Location location, FireworkEffect effect, int power, Vector direction, Integer expectedLifespan, Integer ticksFlown, boolean silent) { + Entity entity = null; + try { + if (fireworkMaterial == null) { + return null; + } + ServerLevel level = ((CraftWorld)location.getWorld()).getHandle(); + ItemStack itemStack = new ItemStack(fireworkMaterial); + FireworkMeta meta = (FireworkMeta) itemStack.getItemMeta(); + meta.addEffect(effect); + meta.setPower(power); + itemStack.setItemMeta(meta); + + Object item = platform.getItemUtils().getHandle(platform.getItemUtils().makeReal(itemStack)); + final FireworkRocketEntity fireworkHandle = new FireworkRocketEntity(level, location.getX(), location.getY(), location.getZ(), (net.minecraft.world.item.ItemStack)item); + fireworkHandle.setSilent(silent); + + if (direction != null) { + fireworkHandle.setDeltaMovement(new Vec3(direction.getX(), direction.getY(), direction.getZ())); + } + + if (ticksFlown != null) { + fireworkHandle.life = ticksFlown; + } + if (expectedLifespan != null) { + fireworkHandle.lifetime = expectedLifespan; + } + + if (direction == null) { + BlockPos blockPos = new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()); + ClientboundAddEntityPacket fireworkPacket = new ClientboundAddEntityPacket(fireworkHandle, CompatibilityConstants.FIREWORK_TYPE, blockPos); + int fireworkId = fireworkHandle.getId(); + SynchedEntityData watcher = fireworkHandle.getEntityData(); + ClientboundSetEntityDataPacket metadataPacket = new ClientboundSetEntityDataPacket(fireworkId, watcher.packDirty()); + ClientboundEntityEventPacket statusPacket = new ClientboundEntityEventPacket(fireworkHandle, (byte)17); + ClientboundRemoveEntitiesPacket destroyPacket = new ClientboundRemoveEntitiesPacket(fireworkId); + + Collection players = server.getOnlinePlayers(); + sendPacket(server, location, players, fireworkPacket); + sendPacket(server, location, players, metadataPacket); + sendPacket(server, location, players, statusPacket); + sendPacket(server, location, players, destroyPacket); + return null; + } + + level.addFreshEntity(fireworkHandle, CreatureSpawnEvent.SpawnReason.CUSTOM); + entity = fireworkHandle.getBukkitEntity(); + } catch (Exception ex) { + ex.printStackTrace(); + } + + return entity; + } + + @Override + public boolean loadAllTagsFromNBT(ConfigurationSection tags, Object tag) + { + if (!(tag instanceof CompoundTag)) return false; + CompoundTag compoundTag = (CompoundTag) tag; + Set keys = platform.getNBTUtils().getTagKeys(tag); + if (keys == null) return false; + for (String tagName : keys) { + Tag metaBase = compoundTag.get(tagName); + if (metaBase != null) { + if (metaBase instanceof CompoundTag) { + ConfigurationSection newSection = tags.createSection(tagName); + loadAllTagsFromNBT(newSection, metaBase); + } else { + try { + tags.set(tagName, platform.getNBTUtils().getTagValue(metaBase)); + } catch (Exception ex) { + platform.getLogger().log(Level.SEVERE, "Failed to load NBT tags", ex); + return false; + } + } + } + } + return true; + } + + @Override + public boolean setRawLore(ItemStack itemStack, List lore) { + ItemUtils itemUtils = platform.getItemUtils(); + net.minecraft.world.item.ItemStack mcItemStack = (net.minecraft.world.item.ItemStack)itemUtils.getHandle(itemStack); + if (mcItemStack == null) { + return false; + } + List components = new ArrayList<>(); + for (String line : lore) { + components.add(toNMSComponent(line)); + } + ItemLore loreComponent = new ItemLore(components); + mcItemStack.set(DataComponents.LORE, loreComponent); + return true; + } + + @Override + public List getRawLore(ItemStack itemStack) { + List lines = new ArrayList<>(); + ItemUtils itemUtils = platform.getItemUtils(); + net.minecraft.world.item.ItemStack mcItemStack = (net.minecraft.world.item.ItemStack)itemUtils.getHandle(itemStack); + if (mcItemStack == null) return lines; + ItemLore itemLore = mcItemStack.get(DataComponents.LORE); + for (Component component : itemLore.lines()) { + lines.add(fromNMSComponent(component)); + } + return lines; + } + + @Override + protected boolean sendActionBarPackets(Player player, String message) { + Component component = Component.literal(message); + ClientboundSystemChatPacket packet = new ClientboundSystemChatPacket(component, true); + try { + sendPacket(player, packet); + } catch (Exception ex) { + platform.getLogger().log(Level.SEVERE, "Error updating action bar", ex); + return false; + } + return true; + } + + @Override + public boolean setBossBarTitle(BossBar bossBar, String title, String font) { + if (ChatUtils.isDefaultFont(font)) { + setBossBarTitle(bossBar, title); + return true; + } + SpigotUtils spigot = platform.getSpigotUtils(); + if (spigot == null) { + // Can't do fonts without chat components + return false; + } + setBossBarTitleComponents(bossBar, spigot.serializeBossBar(title, font), title); + return true; + } + + @Override + protected void setBossBarTitleComponents(BossBar bossBar, String serialized, String fallback) { + Object handle = ReflectionUtils.getHandle(platform.getLogger(), bossBar); + if (handle == null || !(handle instanceof ServerBossEvent)) { + bossBar.setTitle(fallback); + return; + } + ServerBossEvent bossEvent = (ServerBossEvent)handle; + Component component = toNMSComponent(serialized); + if (component == null) { + bossBar.setTitle(fallback); + } else { + bossEvent.setName(component); + } + } + + private Component toNMSComponent(String serialized) { + if (serialized == null || serialized.isEmpty()) { + return null; + } + Component component; + try { + if (serialized.isEmpty()) { + component = CommonComponents.EMPTY; + } else if (serialized.startsWith("{")) { + component = CraftChatMessage.fromJSON(serialized); + } else { + component = Component.literal(serialized); + } + } catch (Exception ex) { + platform.getLogger().log(Level.WARNING, "Invalid JSON message: " + serialized, ex); + component = CommonComponents.EMPTY; + } + return component; + } + + private String fromNMSComponent(Component component) { + return CraftChatMessage.toJSON(component); + } + + @Override + public Runnable getTaskRunnable(BukkitTask task) { + return (Runnable)ReflectionUtils.getPrivate(platform.getLogger(), task, CraftTask.class, "rTask"); + } + + @Override + public boolean isSwingingArm(Entity entity) { + if (entity == null) return false; + net.minecraft.world.entity.Entity nms = ((CraftEntity)entity).getHandle(); + if (nms == null || !(nms instanceof net.minecraft.world.entity.LivingEntity)) { + return false; + } + return ((net.minecraft.world.entity.LivingEntity)nms).swinging; + } + + @Override + public boolean setLastDamaged(Entity damaged, Entity damager) { + if (damager == null) { + return false; + } + net.minecraft.world.entity.Entity nmsDamager = ((CraftEntity)damager).getHandle(); + if (nmsDamager == null || !(nmsDamager instanceof net.minecraft.world.entity.LivingEntity)) { + return false; + } + net.minecraft.world.entity.LivingEntity livingDamager = (net.minecraft.world.entity.LivingEntity)nmsDamager; + net.minecraft.world.entity.Entity nmsDamaged = damaged == null ? null : ((CraftEntity)damaged).getHandle(); + livingDamager.setLastHurtMob(nmsDamaged); + return true; + } + + @Override + public boolean setLastDamagedBy(Entity damaged, Entity damager) { + if (damager == null) { + return false; + } + net.minecraft.world.entity.Entity nmsDamaged = ((CraftEntity)damager).getHandle(); + if (nmsDamaged == null || !(nmsDamaged instanceof net.minecraft.world.entity.LivingEntity)) { + return false; + } + net.minecraft.world.entity.LivingEntity livingDamaged = (net.minecraft.world.entity.LivingEntity)nmsDamaged; + net.minecraft.world.entity.Entity nmsDamager = ((CraftEntity)damager).getHandle(); + net.minecraft.world.entity.LivingEntity livingDamager = nmsDamager instanceof net.minecraft.world.entity.LivingEntity + ? (net.minecraft.world.entity.LivingEntity)nmsDamaged : null; + livingDamaged.setLastHurtByMob(livingDamager); + return true; + } +} diff --git a/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/ItemUtils.java b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/ItemUtils.java new file mode 100644 index 0000000000..1c05b9e7ad --- /dev/null +++ b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/ItemUtils.java @@ -0,0 +1,201 @@ +package com.elmakers.mine.bukkit.utility.platform.v1_21_11; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Optional; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.craftbukkit.v1_21_R7.CraftWorld; +import org.bukkit.craftbukkit.v1_21_R7.inventory.CraftItemStack; +import org.bukkit.inventory.ItemStack; + +import com.elmakers.mine.bukkit.utility.ReflectionUtils; +import com.elmakers.mine.bukkit.utility.platform.Platform; +import com.elmakers.mine.bukkit.utility.platform.base_v1_21_4.ItemUtilsBase_v1_21_4; + +import net.minecraft.core.component.DataComponents; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.StringTag; +import net.minecraft.nbt.Tag; +import net.minecraft.util.ProblemReporter; +import net.minecraft.world.ItemStackWithSlot; +import net.minecraft.world.item.component.CustomData; +import net.minecraft.world.level.storage.TagValueInput; +import net.minecraft.world.level.storage.ValueInput; + +public class ItemUtils extends ItemUtilsBase_v1_21_4 { + public ItemUtils(Platform platform) { + super(platform); + } + + @Override + public Object getHandle(org.bukkit.inventory.ItemStack stack) { + if (stack == null || !(stack instanceof CraftItemStack)) { + return null; + } + return ReflectionUtils.getHandle(platform.getLogger(), stack, CraftItemStack.class); + } + + @Override + public CompoundTag getTag(Object mcItemStack) { + if (mcItemStack == null || !(mcItemStack instanceof net.minecraft.world.item.ItemStack)) return null; + net.minecraft.world.item.ItemStack itemStack = (net.minecraft.world.item.ItemStack)mcItemStack; + CustomData customData = itemStack.get(DataComponents.CUSTOM_DATA); + return customData == null ? null : (CompoundTag)ReflectionUtils.getPrivate(platform.getLogger(), customData, CustomData.class, "tag"); + } + + @Override + public CompoundTag getTag(ItemStack itemStack) { + CompoundTag tag = null; + try { + Object mcItemStack = getHandle(itemStack); + if (mcItemStack == null) { + if (itemStack.hasItemMeta()) { + itemStack = makeReal(itemStack); + mcItemStack = getHandle(itemStack); + } + } + if (mcItemStack == null) return null; + net.minecraft.world.item.ItemStack stack = (net.minecraft.world.item.ItemStack)mcItemStack; + tag = getTag(stack); + } catch (Throwable ex) { + ex.printStackTrace(); + } + return tag; + } + + @Override + public CompoundTag getOrCreateTag(Object mcItemStack) { + if (mcItemStack == null || !(mcItemStack instanceof net.minecraft.world.item.ItemStack)) return null; + net.minecraft.world.item.ItemStack itemStack = (net.minecraft.world.item.ItemStack)mcItemStack; + CustomData customData = itemStack.get(DataComponents.CUSTOM_DATA); + CompoundTag tag = null; + if (customData == null) { + tag = new CompoundTag(); + // This makes a copy + customData = CustomData.of(tag); + tag = (CompoundTag)ReflectionUtils.getPrivate(platform.getLogger(), customData, CustomData.class, "tag"); + ((net.minecraft.world.item.ItemStack)mcItemStack).set(DataComponents.CUSTOM_DATA, customData); + } else { + tag = (CompoundTag)ReflectionUtils.getPrivate(platform.getLogger(), customData, CustomData.class, "tag"); + } + return tag; + } + + @Override + public CompoundTag getOrCreateTag(ItemStack itemStack) { + CompoundTag tag = null; + try { + Object mcItemStack = getHandle(itemStack); + if (mcItemStack == null) { + if (itemStack.hasItemMeta()) { + itemStack = makeReal(itemStack); + mcItemStack = getHandle(itemStack); + } + } + if (mcItemStack == null) return null; + net.minecraft.world.item.ItemStack stack = (net.minecraft.world.item.ItemStack)mcItemStack; + tag = getOrCreateTag(stack); + } catch (Throwable ex) { + ex.printStackTrace(); + } + return tag; + } + + protected net.minecraft.world.item.ItemStack getNMSCopy(ItemStack stack) { + net.minecraft.world.item.ItemStack nms = null; + try { + nms = CraftItemStack.asNMSCopy(stack); + } catch (Throwable ex) { + ex.printStackTrace(); + } + return nms; + } + + @Override + public ItemStack getCopy(ItemStack stack) { + if (stack == null) return null; + net.minecraft.world.item.ItemStack craft = getNMSCopy(stack); + return CraftItemStack.asCraftMirror(craft); + } + + @Override + public boolean isEmpty(ItemStack itemStack) { + if (itemStack == null || itemStack.getType() == Material.AIR) return true; + Object handle = getHandle(itemStack); + if (handle == null || !(handle instanceof net.minecraft.world.item.ItemStack)) return false; + net.minecraft.world.item.ItemStack mcItem = (net.minecraft.world.item.ItemStack)handle; + return mcItem.isEmpty(); + } + + protected StringTag getTagString(String value) { + return StringTag.valueOf(value); + } + + @Override + public Object setStringList(Object nbtBase, String tag, Collection values) { + if (nbtBase == null || !(nbtBase instanceof CompoundTag)) return null; + CompoundTag compoundTag = (CompoundTag)nbtBase; + ListTag listTag = new ListTag(); + + for (String value : values) { + StringTag nbtString = getTagString(value); + listTag.add(nbtString); + } + + compoundTag.put(tag, listTag); + return listTag; + } + + @Override + public List getStringList(Object nbtBase, String key) { + List list = new ArrayList<>(); + if (nbtBase == null || !(nbtBase instanceof CompoundTag)) return list; + CompoundTag compoundTag = (CompoundTag)nbtBase; + Optional listTagOptional = compoundTag.getList(key); + + if (listTagOptional.isPresent()) { + ListTag listTag = listTagOptional.get(); + int size = listTag.size(); + for (int i = 0; i < size; i++) { + Tag entry = listTag.get(i); + Optional optionalString = entry.asString(); + if (!optionalString.isPresent()) continue; + list.add(optionalString.get()); + } + } + return list; + } + + @Override + public ItemStack getItem(Object itemTag) { + // This was only called by getItems, which no longer uses this method + throw new RuntimeException("The getItem method is no longer supported"); + + } + + @Override + public ItemStack[] getItems(Object rootTag, String tagName) { + if (rootTag == null || !(rootTag instanceof CompoundTag)) return null; + CompoundTag compoundTag = (CompoundTag)rootTag; + + try { + CraftWorld world = (CraftWorld)Bukkit.getWorlds().get(0); + ProblemReporter discard = ProblemReporter.DISCARDING; + ValueInput valueInput = TagValueInput.create(discard, world.getHandle().registryAccess(), compoundTag); + ValueInput.TypedInputList list = valueInput.listOrEmpty(tagName, ItemStackWithSlot.CODEC); + List itemList = new ArrayList<>(); + for (ItemStackWithSlot stackWithSlot : list) { + ItemStack item = CraftItemStack.asCraftMirror(stackWithSlot.stack()); + itemList.add(item); + } + return itemList.toArray(new ItemStack[itemList.size()]); + } catch (Exception ex) { + ex.printStackTrace(); + } + return null; + } +} diff --git a/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/MobUtils.java b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/MobUtils.java new file mode 100644 index 0000000000..28715df6f1 --- /dev/null +++ b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/MobUtils.java @@ -0,0 +1,865 @@ +package com.elmakers.mine.bukkit.utility.platform.v1_21_11; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.function.BooleanSupplier; +import java.util.logging.Level; + +import net.minecraft.world.entity.animal.Animal; +import net.minecraft.world.entity.animal.axolotl.Axolotl; +import net.minecraft.world.entity.animal.bee.Bee; +import net.minecraft.world.entity.animal.chicken.Chicken; +import net.minecraft.world.entity.animal.cow.Cow; +import net.minecraft.world.entity.animal.cow.MushroomCow; +import net.minecraft.world.entity.animal.dolphin.Dolphin; +import net.minecraft.world.entity.animal.equine.*; +import net.minecraft.world.entity.animal.feline.Cat; +import net.minecraft.world.entity.animal.feline.Ocelot; +import net.minecraft.world.entity.animal.fish.*; +import net.minecraft.world.entity.animal.fox.Fox; +import net.minecraft.world.entity.animal.goat.Goat; +import net.minecraft.world.entity.animal.golem.AbstractGolem; +import net.minecraft.world.entity.animal.golem.IronGolem; +import net.minecraft.world.entity.animal.golem.SnowGolem; +import net.minecraft.world.entity.animal.panda.Panda; +import net.minecraft.world.entity.animal.parrot.Parrot; +import net.minecraft.world.entity.animal.parrot.ShoulderRidingEntity; +import net.minecraft.world.entity.animal.pig.Pig; +import net.minecraft.world.entity.animal.polarbear.PolarBear; +import net.minecraft.world.entity.animal.rabbit.Rabbit; +import net.minecraft.world.entity.animal.sheep.Sheep; +import net.minecraft.world.entity.animal.squid.GlowSquid; +import net.minecraft.world.entity.animal.squid.Squid; +import net.minecraft.world.entity.animal.turtle.Turtle; +import net.minecraft.world.entity.animal.wolf.Wolf; +import net.minecraft.world.entity.boss.enderdragon.EnderDragon; +import net.minecraft.world.entity.boss.wither.WitherBoss; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.monster.*; +import net.minecraft.world.entity.monster.illager.*; +import net.minecraft.world.entity.monster.skeleton.AbstractSkeleton; +import net.minecraft.world.entity.monster.skeleton.Skeleton; +import net.minecraft.world.entity.monster.skeleton.Stray; +import net.minecraft.world.entity.monster.skeleton.WitherSkeleton; +import net.minecraft.world.entity.monster.spider.CaveSpider; +import net.minecraft.world.entity.monster.spider.Spider; +import net.minecraft.world.entity.monster.zombie.*; +import net.minecraft.world.entity.npc.villager.Villager; +import net.minecraft.world.entity.npc.wanderingtrader.WanderingTrader; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.MemoryConfiguration; +import org.bukkit.craftbukkit.v1_21_R7.entity.CraftEntity; +import org.bukkit.entity.Entity; + +import com.elmakers.mine.bukkit.api.magic.Mage; +import com.elmakers.mine.bukkit.api.magic.MageController; +import com.elmakers.mine.bukkit.api.requirements.Requirement; +import com.elmakers.mine.bukkit.mob.GoalConfiguration; +import com.elmakers.mine.bukkit.mob.GoalType; +import com.elmakers.mine.bukkit.utility.StringUtils; +import com.elmakers.mine.bukkit.utility.platform.ItemUtils; +import com.elmakers.mine.bukkit.utility.platform.base.MobUtilsBase; +import com.elmakers.mine.bukkit.utility.platform.v1_21_11.goal.IdleGoal; +import com.elmakers.mine.bukkit.utility.platform.v1_21_11.goal.MagicCheckOwnerGoal; +import com.elmakers.mine.bukkit.utility.platform.v1_21_11.goal.MagicFindOwnerGoal; +import com.elmakers.mine.bukkit.utility.platform.v1_21_11.goal.MagicFollowMobGoal; +import com.elmakers.mine.bukkit.utility.platform.v1_21_11.goal.MagicFollowOwnerGoal; +import com.elmakers.mine.bukkit.utility.platform.v1_21_11.goal.MagicGoal; +import com.elmakers.mine.bukkit.utility.platform.v1_21_11.goal.MagicOwnerHurtByTargetGoal; +import com.elmakers.mine.bukkit.utility.platform.v1_21_11.goal.MagicOwnerHurtTargetGoal; +import com.elmakers.mine.bukkit.utility.platform.v1_21_11.goal.MagicPanicGoal; +import com.elmakers.mine.bukkit.utility.platform.v1_21_11.goal.MagicTemptGoal; +import com.elmakers.mine.bukkit.utility.platform.v1_21_11.goal.RequirementsGoal; +import com.elmakers.mine.bukkit.utility.platform.v1_21_11.goal.SpinGoal; +import com.elmakers.mine.bukkit.utility.platform.v1_21_11.goal.TriggerGoal; + +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.PathfinderMob; +import net.minecraft.world.entity.TamableAnimal; +import net.minecraft.world.entity.ai.goal.AvoidEntityGoal; +import net.minecraft.world.entity.ai.goal.BegGoal; +import net.minecraft.world.entity.ai.goal.BreakDoorGoal; +import net.minecraft.world.entity.ai.goal.BreathAirGoal; +import net.minecraft.world.entity.ai.goal.BreedGoal; +import net.minecraft.world.entity.ai.goal.EatBlockGoal; +import net.minecraft.world.entity.ai.goal.FleeSunGoal; +import net.minecraft.world.entity.ai.goal.FloatGoal; +import net.minecraft.world.entity.ai.goal.FollowBoatGoal; +import net.minecraft.world.entity.ai.goal.FollowFlockLeaderGoal; +import net.minecraft.world.entity.ai.goal.FollowMobGoal; +import net.minecraft.world.entity.ai.goal.FollowOwnerGoal; +import net.minecraft.world.entity.ai.goal.FollowParentGoal; +import net.minecraft.world.entity.ai.goal.Goal; +import net.minecraft.world.entity.ai.goal.GoalSelector; +import net.minecraft.world.entity.ai.goal.GolemRandomStrollInVillageGoal; +import net.minecraft.world.entity.ai.goal.InteractGoal; +import net.minecraft.world.entity.ai.goal.LandOnOwnersShoulderGoal; +import net.minecraft.world.entity.ai.goal.LeapAtTargetGoal; +import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal; +import net.minecraft.world.entity.ai.goal.MeleeAttackGoal; +import net.minecraft.world.entity.ai.goal.MoveBackToVillageGoal; +import net.minecraft.world.entity.ai.goal.MoveThroughVillageGoal; +import net.minecraft.world.entity.ai.goal.MoveTowardsRestrictionGoal; +import net.minecraft.world.entity.ai.goal.MoveTowardsTargetGoal; +import net.minecraft.world.entity.ai.goal.OcelotAttackGoal; +import net.minecraft.world.entity.ai.goal.OfferFlowerGoal; +import net.minecraft.world.entity.ai.goal.OpenDoorGoal; +import net.minecraft.world.entity.ai.goal.PanicGoal; +import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal; +import net.minecraft.world.entity.ai.goal.RandomStrollGoal; +import net.minecraft.world.entity.ai.goal.RandomSwimmingGoal; +import net.minecraft.world.entity.ai.goal.RestrictSunGoal; +import net.minecraft.world.entity.ai.goal.RunAroundLikeCrazyGoal; +import net.minecraft.world.entity.ai.goal.StrollThroughVillageGoal; +import net.minecraft.world.entity.ai.goal.SwellGoal; +import net.minecraft.world.entity.ai.goal.TryFindWaterGoal; +import net.minecraft.world.entity.ai.goal.WaterAvoidingRandomFlyingGoal; +import net.minecraft.world.entity.ai.goal.WaterAvoidingRandomStrollGoal; +import net.minecraft.world.entity.ai.goal.WrappedGoal; +import net.minecraft.world.entity.ai.goal.ZombieAttackGoal; +import net.minecraft.world.entity.ai.goal.target.DefendVillageTargetGoal; +import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal; +import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal; +import net.minecraft.world.entity.ai.goal.target.OwnerHurtByTargetGoal; +import net.minecraft.world.entity.ai.goal.target.OwnerHurtTargetGoal; +import net.minecraft.world.entity.ai.goal.target.TargetGoal; +import net.minecraft.world.entity.ambient.AmbientCreature; +import net.minecraft.world.entity.ambient.Bat; +import net.minecraft.world.entity.monster.hoglin.Hoglin; +import net.minecraft.world.entity.monster.piglin.Piglin; +import net.minecraft.world.entity.monster.piglin.PiglinBrute; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; + +public class MobUtils extends MobUtilsBase { + private final Platform platform; + + public MobUtils(Platform platform) { + this.platform = platform; + } + + private net.minecraft.world.entity.Entity getNMS(Entity entity) { + if (entity == null) + return null; + CraftEntity craft = (CraftEntity) entity; + return craft.getHandle(); + } + + private Mob getMob(Entity entity) { + net.minecraft.world.entity.Entity nms = getNMS(entity); + if (!(nms instanceof Mob)) { + return null; + } + return (Mob) nms; + } + + @Override + public boolean removeGoals(Entity entity) { + Mob mob = getMob(entity); + if (mob == null) { + return false; + } + mob.goalSelector.getAvailableGoals().clear(); + return true; + } + + @Override + public boolean removeTargetGoals(Entity entity) { + Mob mob = getMob(entity); + if (mob == null) { + return false; + } + mob.targetSelector.getAvailableGoals().clear(); + return true; + } + + protected boolean removeGoal(GoalSelector selector, Mob mob, Entity entity, GoalType goalType) { + // TODO: Is there a cleaner way? + try { + Goal targetGoal = getGoal(goalType, entity, mob, new MemoryConfiguration()); + Collection available = selector.getAvailableGoals(); + List found = new ArrayList<>(); + for (WrappedGoal wrappedGoal : available) { + if (targetGoal.getClass().isAssignableFrom(wrappedGoal.getGoal().getClass())) { + found.add(wrappedGoal.getGoal()); + } + } + for (Goal removeGoal : found) { + selector.removeGoal(removeGoal); + } + } catch (Exception ex) { + platform.getLogger().log(Level.WARNING, "Error removing goal: " + goalType + " from " + entity.getType(), + ex); + return false; + } + return true; + } + + @Override + public boolean removeGoal(Entity entity, GoalType goalType) { + Mob mob = getMob(entity); + if (mob == null) { + return false; + } + return removeGoal(mob.goalSelector, mob, entity, goalType); + } + + @Override + public boolean removeTargetGoal(Entity entity, GoalType goalType) { + Mob mob = getMob(entity); + if (mob == null) { + return false; + } + return removeGoal(mob.targetSelector, mob, entity, goalType); + } + + public String getGoalParentDescriptions(Goal goal) { + List parentClasses = null; + Class superClass = goal.getClass().getSuperclass(); + while (superClass != null && superClass != Goal.class && superClass != MagicGoal.class + && superClass != TargetGoal.class) { + if (parentClasses == null) { + parentClasses = new ArrayList<>(); + } + parentClasses.add(superClass.getSimpleName()); + superClass = superClass.getSuperclass(); + } + if (parentClasses == null || parentClasses.isEmpty()) + return null; + return ChatColor.DARK_GRAY + " -> " + ChatColor.GRAY + + StringUtils.join(parentClasses, ChatColor.DARK_GRAY + " -> " + ChatColor.GRAY); + } + + protected Collection getGoalDescriptions(GoalSelector selector) { + List descriptions = new ArrayList<>(); + Collection available = selector.getAvailableGoals(); + for (WrappedGoal wrappedGoal : available) { + Goal goal = wrappedGoal.getGoal(); + String description = goal.toString(); + String parentDescription = getGoalParentDescriptions(goal); + if (parentDescription != null) { + description += " " + parentDescription; + } + if (wrappedGoal.isRunning()) { + description = ChatColor.AQUA + description; + } else { + description = ChatColor.GOLD + description; + } + descriptions.add(ChatColor.BLUE + Integer.toString(wrappedGoal.getPriority()) + ChatColor.DARK_GRAY + ": " + + description); + } + return descriptions; + } + + @Override + public Collection getGoalDescriptions(Entity entity) { + Mob mob = getMob(entity); + if (mob == null) { + return null; + } + return getGoalDescriptions(mob.goalSelector); + } + + @Override + public Collection getTargetGoalDescriptions(Entity entity) { + Mob mob = getMob(entity); + if (mob == null) { + return null; + } + return getGoalDescriptions(mob.targetSelector); + } + + protected boolean addGoal(GoalSelector selector, Mob mob, Entity entity, GoalType goalType, int priority, + ConfigurationSection config) { + try { + Goal goal = getGoal(goalType, entity, mob, config); + if (goal == null) { + return false; + } + selector.addGoal(priority, goal); + } catch (Exception ex) { + platform.getLogger().log(Level.WARNING, "Error creating goal: " + goalType + " on " + entity.getType(), ex); + return false; + } + return true; + } + + @Override + public boolean addGoal(Entity entity, GoalType goalType, int priority, ConfigurationSection config) { + Mob mob = getMob(entity); + if (mob == null) { + return false; + } + return addGoal(mob.goalSelector, mob, entity, goalType, priority, config); + } + + @Override + public boolean addTargetGoal(Entity entity, GoalType goalType, int priority, ConfigurationSection config) { + Mob mob = getMob(entity); + if (mob == null) { + return false; + } + return addGoal(mob.targetSelector, mob, entity, goalType, priority, config); + } + + private Goal getGoal(GoalType goalType, Entity entity, Mob mob, ConfigurationSection config) { + final String classType = config.getString("entity_class", "player"); + final double speed = config.getDouble("speed", 1); + final double sprintSpeed = config.getDouble("sprint_speed", 1); + final float distance = (float) config.getDouble("distance", 16); + final boolean doors = config.getBoolean("doors", true); + final boolean interruptable = config.getBoolean("interruptable", true); + final boolean see = config.getBoolean("see", true); + final boolean reach = config.getBoolean("reach", false); + final float startDistance = (float) config.getDouble("start_distance", 5); + final float stopDistance = (float) config.getDouble("stop_distance", 1); + final float radius = (float) config.getDouble("radius", 16); + final PathfinderMob pathfinder = mob instanceof PathfinderMob ? (PathfinderMob) mob : null; + int interval = config.getInt("interval", 1000); + // Interval is specified in ms, but needed in ticks + interval = interval / 50; + MageController controller = platform.getController(); + Mage mage; + List goals; + switch (goalType) { + case AVOID_ENTITY: + if (pathfinder == null) + return null; + return new AvoidEntityGoal<>(pathfinder, getMobClass(classType), distance, sprintSpeed, sprintSpeed); + case BEG: + if (mob instanceof Wolf) { + return new BegGoal((Wolf) mob, distance); + } + return null; + case BREAK_DOOR: + return new BreakDoorGoal(mob, difficulty -> true); + case BREATHE_AIR: + if (pathfinder == null) + return null; + return new BreathAirGoal(pathfinder); + case BREED: + if (mob instanceof Animal) { + return new BreedGoal((Animal) mob, speed); + } + return null; + case EAT_BLOCK: + return new EatBlockGoal(mob); + case FLEE_SUN: + if (pathfinder == null) + return null; + return new FleeSunGoal(pathfinder, speed); + case FLOAT: + return new FloatGoal(mob); + case FOLLOW_BOAT: + if (pathfinder == null) + return null; + return new FollowBoatGoal(pathfinder); + case FOLLOW_FLOCK_LEADER: + if (mob instanceof AbstractSchoolingFish) { + return new FollowFlockLeaderGoal((AbstractSchoolingFish) mob); + } + return null; + case FOLLOW_MOB: + return new FollowMobGoal(mob, speed, distance, radius); + case FOLLOW_OWNER: + if (mob instanceof TamableAnimal) { + return new FollowOwnerGoal((TamableAnimal) mob, speed, startDistance, stopDistance); + } + // Intentional fall-through + case MAGIC_FOLLOW_OWNER: + return new MagicFollowOwnerGoal(platform, mob, speed, startDistance, stopDistance, interval, config); + case FOLLOW_PARENT: + if (mob instanceof Animal) { + return new FollowParentGoal((Animal) mob, speed); + } + return null; + case GOLEM_RANDOM_STROLL_IN_VILLAGE: + if (pathfinder == null) + return null; + return new GolemRandomStrollInVillageGoal(pathfinder, speed); + case INTERACT: + return new InteractGoal(mob, getMobClass(classType), distance, + (float) config.getDouble("probability", 1)); + case LAND_ON_OWNERS_SHOULDER: + if (mob instanceof ShoulderRidingEntity) { + return new LandOnOwnersShoulderGoal((ShoulderRidingEntity) mob); + } + return null; + case LEAP_AT_TARGET: + return new LeapAtTargetGoal(mob, (float) config.getDouble("y_offset", 0.4)); + case LOOK_AT_PLAYER: + return new LookAtPlayerGoal(mob, getMobClass(classType), distance, + (float) config.getDouble("probability", 1), config.getBoolean("horizontal")); + case MELEE_ATTACK: + if (pathfinder == null) + return null; + return new MeleeAttackGoal(pathfinder, speed, config.getBoolean("follow", true)); + case MOVE_BACK_TO_VILLAGE: + if (pathfinder == null) + return null; + return new MoveBackToVillageGoal(pathfinder, speed, config.getBoolean("check", true)); + case MOVE_THROUGH_VILLAGE: + if (pathfinder == null) + return null; + return new MoveThroughVillageGoal(pathfinder, speed, config.getBoolean("night", true), (int) distance, + (BooleanSupplier) () -> doors); + case MOVE_TOWARDS_RESTRICTION: + if (pathfinder == null) + return null; + return new MoveTowardsRestrictionGoal(pathfinder, speed); + case MOVE_TOWARDS_TARGET: + if (pathfinder == null) + return null; + return new MoveTowardsTargetGoal(pathfinder, speed, distance); + case OCELOT_ATTACK: + return new OcelotAttackGoal(mob); + case OFFER_FLOWER: + if (mob instanceof IronGolem) { + return new OfferFlowerGoal((IronGolem) mob); + } + return null; + case OPEN_DOOR: + return new OpenDoorGoal(mob, config.getBoolean("close", false)); + case PANIC_FIRE: + if (pathfinder == null) + return null; + return new PanicGoal(pathfinder, speed); + case PANIC: + case MAGIC_PANIC: + if (pathfinder == null) + return null; + return new MagicPanicGoal(pathfinder, speed, config.getInt("panic", 3000), config.getInt("calm", 5000), + interruptable); + case RANDOM_LOOK_AROUND: + return new RandomLookAroundGoal(mob); + case RANDOM_STROLL: + if (pathfinder == null) + return null; + return new RandomStrollGoal(pathfinder, speed, interval); + case RANDOM_SWIMMING: + if (pathfinder == null) + return null; + return new RandomSwimmingGoal(pathfinder, speed, interval); + case RESTRICT_SUN: + if (pathfinder == null) + return null; + return new RestrictSunGoal(pathfinder); + case RUN_AROUND_LIKE_CRAZY: + if (mob instanceof Horse) { + return new RunAroundLikeCrazyGoal((Horse) mob, speed); + } + return null; + case STROLL_THROUGH_VILLAGE: + if (pathfinder == null) + return null; + return new StrollThroughVillageGoal(pathfinder, interval); + case SWELL: + if (mob instanceof Creeper) { + return new SwellGoal((Creeper) mob); + } + return null; + case TEMPT: + if (pathfinder == null) + return null; + String itemKey = config.getString("item", "EMERALD"); + try { + Material material = Material.valueOf(itemKey.toUpperCase()); + org.bukkit.inventory.ItemStack itemStack = new org.bukkit.inventory.ItemStack(material); + ItemUtils itemUtils = platform.getItemUtils(); + itemStack = itemUtils.makeReal(itemStack); + ItemStack nms = (ItemStack) itemUtils.getHandle(itemStack); + if (nms == null) { + platform.getLogger().warning("Invalid item from material in temp goal: " + itemKey); + return null; + } + boolean scare = config.getBoolean("scare", false); + return new MagicTemptGoal(pathfinder, speed, Ingredient.of(nms.getItem()), scare, distance); + } catch (Exception ex) { + platform.getLogger().warning("Invalid material in temp goal: " + itemKey); + return null; + } + case TRY_FIND_WATER: + if (pathfinder == null) + return null; + return new TryFindWaterGoal(pathfinder); + case WATER_AVOIDING_RANDOM_FLYING: + if (pathfinder == null) + return null; + return new WaterAvoidingRandomFlyingGoal(pathfinder, speed); + case WATER_AVOIDING_RANDOM_STROLL: + if (pathfinder == null) + return null; + return new WaterAvoidingRandomStrollGoal(pathfinder, speed); + case ZOMBIE_ATTACK: + if (mob instanceof Zombie) { + return new ZombieAttackGoal((Zombie) mob, speed, config.getBoolean("follow", true)); + } + return null; + + // Target + case DEFEND_VILLAGE_TARGET: + if (mob instanceof IronGolem) { + return new DefendVillageTargetGoal((IronGolem) mob); + } + return null; + case HURT_BY_TARGET: + if (pathfinder == null) + return null; + return new HurtByTargetGoal(pathfinder); + case NEAREST_ATTACKABLE_TARGET: + return new NearestAttackableTargetGoal<>(mob, getMobClass(classType), see, reach); + case OWNER_HURT_BY_TARGET: + if (mob instanceof TamableAnimal) { + return new OwnerHurtByTargetGoal((TamableAnimal) mob); + } + // Intentional fall-through + case MAGIC_OWNER_HURT_BY_TARGET: + return new MagicOwnerHurtByTargetGoal(platform, mob, entity, see, reach); + case OWNER_HURT_TARGET: + if (mob instanceof TamableAnimal) { + return new OwnerHurtTargetGoal((TamableAnimal) mob); + } + // Intentional fall-through + case MAGIC_OWNER_HURT_TARGET: + return new MagicOwnerHurtTargetGoal(platform, mob, entity, see, reach); + + // Magic add-ons + case FOLLOW_ENTITY: + case MAGIC_FOLLOW_MOB: + Class mobClass = getMobClass(classType); + if (mobClass == null) { + platform.getLogger().warning("Unsupported entity_class in magic_follow_mob goal: " + classType); + return null; + } + return new MagicFollowMobGoal(mob, speed, radius, distance, interval, mobClass); + case REQUIREMENT: + case REQUIREMENTS: + if (pathfinder == null) + return null; + mage = controller.getMage(entity); + Collection requirements = controller.getRequirements(config); + goals = getGoals(entity, mob, config, "magic requirement goal"); + return new RequirementsGoal(mage, goals, interruptable, requirements); + case GROUP: + goals = getGoals(entity, mob, config, "magic group goal"); + return new MagicGoal(goals, interruptable); + case TRIGGER: + mage = controller.getMage(entity); + goals = getGoals(entity, mob, config, "magic trigger goal"); + return new TriggerGoal(mage, goals, interruptable, config.getString("trigger", "goal"), interval); + case FIND_OWNER: + return new MagicFindOwnerGoal(platform, mob, radius, getMobClass(classType)); + case CHECK_OWNER: + return new MagicCheckOwnerGoal(platform, mob); + case IDLE: + return new IdleGoal(); + case SPIN: + return new SpinGoal(mob, (float) config.getDouble("degrees", 10)); + default: + platform.getLogger().warning("Unsupported goal type: " + goalType); + return null; + } + } + + private List getGoals(Entity entity, Mob mob, ConfigurationSection config, String logContext) { + List goals = new ArrayList<>(); + List goalConfigurations = GoalConfiguration.fromList(config, "goals", platform.getLogger(), + logContext); + if (goalConfigurations != null) { + Collections.sort(goalConfigurations); + for (GoalConfiguration goalConfig : goalConfigurations) { + try { + Goal goal = getGoal(goalConfig.getGoalType(), entity, mob, goalConfig.getConfiguration()); + if (goal != null) { + goals.add(goal); + } + } catch (Exception ex) { + platform.getLogger().log(Level.WARNING, + "Error creating goal: " + goalConfig.getGoalType() + " on mob " + entity.getType(), ex); + } + } + } + if (goals.isEmpty()) { + goals.add(new IdleGoal()); + } + return goals; + } + + private Class getMobClass(String classType) { + switch (classType) { + case "ambient_creature": + case "ambientcreature": + + // Ambient + case "ambient": + return AmbientCreature.class; + case "bat": + return Bat.class; + + // Axolotl + case "axolotl": + return Axolotl.class; + + // Goat + case "goat": + return Goat.class; + + // Horse + case "abstractchestedhorse": + case "abstract_chested_horse": + case "chestedhorse": + case "chested_horse": + return AbstractChestedHorse.class; + case "abstract_horse": + case "abstracthorse": + case "any_horse": + return AbstractHorse.class; + case "donkey": + return Donkey.class; + case "horse": + return Horse.class; + case "llama": + return Llama.class; + case "mule": + return Mule.class; + case "skeleton_horse": + case "skeletonhorse": + return Skeleton.class; + case "trader_llama": + case "traderllama": + return TraderLlama.class; + case "zombiehorse": + case "zombie_horse": + return ZombieHorse.class; + + // Animal + case "abstractfish": + case "abstract_fish": + case "any_fish": + case "fish": + return AbstractFish.class; + case "abstractgolem": + case "abstract_golem": + case "golem": + return AbstractGolem.class; + case "abstractschoolingfish": + case "abstract_schooling_fish": + case "schooling_fish": + return AbstractSchoolingFish.class; + case "animal": + return Animal.class; + case "bee": + return Bee.class; + case "cat": + return Cat.class; + case "chicken": + return Chicken.class; + case "cod": + return Cod.class; + case "cow": + return Cow.class; + case "dolphin": + return Dolphin.class; + case "fox": + return Fox.class; + case "iron_golem": + case "irongolem": + return IronGolem.class; + case "mushroomcow": + case "mushroom_cow": + case "mooshroom": + return MushroomCow.class; + case "ocelot": + return Ocelot.class; + case "panda": + return Panda.class; + case "parrot": + return Parrot.class; + case "pig": + return Pig.class; + case "polarbear": + case "polar_bear": + return PolarBear.class; + case "pufferfish": + return Pufferfish.class; + case "rabbit": + return Rabbit.class; + case "salmon": + return Salmon.class; + case "sheep": + return Sheep.class; + case "snowgolem": + case "snow_golem": + case "snowman": + return SnowGolem.class; + case "squid": + return Squid.class; + case "tropicalfish": + case "tropical_fish": + return TropicalFish.class; + case "turle": + case "sea_turtle": + return Turtle.class; + case "wolf": + return Wolf.class; + + // Boss + case "dragon": + case "enderdragon": + case "ender_dragon": + return EnderDragon.class; + case "wither": + case "wither_boss": + case "witherboss": + return WitherBoss.class; + + // Decoration + case "armorstand": + case "armor_stand": + return ArmorStand.class; + + // Hoglin + case "hoglin": + return Hoglin.class; + + // Piglin + case "piglin": + return Piglin.class; + case "piglinbrute": + case "piglin_brute": + return PiglinBrute.class; + + // Monster + case "abstractillager": + case "abstract_illager": + case "illager": + return AbstractIllager.class; + case "abstractskeleton": + case "abstract_skeleton": + case "any_skeleton": + return AbstractSkeleton.class; + case "blaze": + return Blaze.class; + case "cavespider": + case "cave_spider": + return CaveSpider.class; + case "creeper": + return Creeper.class; + case "drowned": + return Drowned.class; + case "elderguardian": + case "elder_guardian": + return ElderGuardian.class; + case "enderman": + return EnderMan.class; + case "endermite": + return Endermite.class; + case "evoker": + return Evoker.class; + case "ghast": + return Ghast.class; + case "giant": + return Giant.class; + case "guardian": + return Guardian.class; + case "husk": + return Husk.class; + case "illusioner": + return Illusioner.class; + case "magmacube": + case "magma_cube": + return MagmaCube.class; + case "monster": + return Monster.class; + case "phantom": + return Phantom.class; + case "pillager": + return Pillager.class; + case "ravager": + return Ravager.class; + case "shulker": + return Shulker.class; + case "silverfish": + return Silverfish.class; + case "skeleton": + return Skeleton.class; + case "slime": + return Slime.class; + case "spellcasterillager": + case "spellcaster_illager": + case "spellcaster": + return SpellcasterIllager.class; + case "spider": + return Spider.class; + case "stray": + return Stray.class; + case "strider": + return Strider.class; + case "vex": + return Vex.class; + case "vindicator": + return Vindicator.class; + case "witch": + return Witch.class; + case "witherskeleton": + case "wither_skeleton": + return WitherSkeleton.class; + case "zoglin": + return Zoglin.class; + case "zombie": + return Zombie.class; + case "zombievillager": + case "zombie_villager": + return ZombieVillager.class; + case "zombifiedpiglin": + case "zombified_piglin": + return ZombifiedPiglin.class; + + // NPC + case "villager": + return Villager.class; + case "wanderingtrader": + case "wandering_trader": + return WanderingTrader.class; + + // Player + case "player": + return Player.class; + + // Base + // Why are you here? + case "glowsquid": + return GlowSquid.class; + case "mob": + return Mob.class; + case "livingentity": + case "living_entity": + return LivingEntity.class; + + default: + platform.getLogger().warning("Invalid entity_class in goal config: " + classType); + return LivingEntity.class; + } + } + + @Override + public boolean setPathfinderTarget(Entity entity, Entity target, double speed) { + if (entity == null || target == null) + return false; + net.minecraft.world.entity.Entity nmsEntity = ((CraftEntity) entity).getHandle(); + net.minecraft.world.entity.Entity nmstarget = ((CraftEntity) target).getHandle(); + if (!(nmsEntity instanceof Mob)) { + return false; + } + + Mob mob = (Mob) nmsEntity; + mob.getNavigation().moveTo(nmstarget, speed); + return true; + } +} diff --git a/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/NBTUtils.java b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/NBTUtils.java new file mode 100644 index 0000000000..8667beff5b --- /dev/null +++ b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/NBTUtils.java @@ -0,0 +1,565 @@ +package com.elmakers.mine.bukkit.utility.platform.v1_21_11; + +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.logging.Level; + +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.craftbukkit.v1_21_R7.entity.CraftEntity; +import org.bukkit.entity.Entity; +import org.bukkit.inventory.ItemStack; + +import com.elmakers.mine.bukkit.utility.ReflectionUtils; +import com.elmakers.mine.bukkit.utility.platform.Platform; +import com.elmakers.mine.bukkit.utility.platform.base.NBTUtilsBase; + +import net.minecraft.core.component.DataComponents; +import net.minecraft.nbt.ByteArrayTag; +import net.minecraft.nbt.ByteTag; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.DoubleTag; +import net.minecraft.nbt.FloatTag; +import net.minecraft.nbt.IntArrayTag; +import net.minecraft.nbt.IntTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.LongArrayTag; +import net.minecraft.nbt.LongTag; +import net.minecraft.nbt.NbtAccounter; +import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.ShortTag; +import net.minecraft.nbt.StringTag; +import net.minecraft.nbt.Tag; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.item.component.CustomData; +import net.minecraft.world.item.component.TypedEntityData; + +public class NBTUtils extends NBTUtilsBase { + public NBTUtils(Platform platform) { + super(platform); + } + + @Override + public Object getTag(ItemStack stack, String tag) { + if (platform.getItemUtils().isEmpty(stack)) return null; + Object tagObject = platform.getItemUtils().getTag(stack); + if (tagObject == null || !(tagObject instanceof CompoundTag)) return null; + return ((CompoundTag)tagObject).get(tag); + } + + @Override + public Object getTag(Object nbtBase, String tag) { + if (nbtBase == null || !(nbtBase instanceof CompoundTag)) return null; + return ((CompoundTag)nbtBase).get(tag); + } + + @Override + public Set getAllKeys(Object nbtBase) { + if (nbtBase == null || !(nbtBase instanceof CompoundTag)) return null; + return ((CompoundTag)nbtBase).keySet(); + } + + @Override + public boolean contains(Object nbtBase, String tag) { + if (nbtBase == null || !(nbtBase instanceof CompoundTag)) return false; + return ((CompoundTag)nbtBase).contains(tag); + } + + @Override + public Object createTag(Object nbtBase, String tag) { + if (nbtBase == null || !(nbtBase instanceof CompoundTag)) return null; + + CompoundTag compoundTag = (CompoundTag)nbtBase; + Optional childTag = compoundTag.getCompound(tag); + if (childTag.isPresent()) { + return childTag.get(); + } + CompoundTag newTag = new CompoundTag(); + compoundTag.put(tag, newTag); + return newTag; + } + + @Override + public Object createTag(ItemStack stack, String tag) { + if (platform.getItemUtils().isEmpty(stack)) return null; + Object outputObject = getTag(stack, tag); + if (outputObject == null || !(outputObject instanceof CompoundTag)) { + Object craft = platform.getItemUtils().getHandle(stack); + if (craft == null) return null; + + CompoundTag tagObject = (CompoundTag)platform.getItemUtils().getTag(craft); + if (tagObject == null) { + tagObject = new CompoundTag(); + // This makes a copy + CustomData customData = CustomData.of(tagObject); + tagObject = (CompoundTag) ReflectionUtils.getPrivate(platform.getLogger(), customData, CustomData.class, "tag"); + ((net.minecraft.world.item.ItemStack)craft).set(DataComponents.CUSTOM_DATA, customData); + } + outputObject = new CompoundTag(); + tagObject.put(tag, (CompoundTag)outputObject); + } + return outputObject; + } + + @Override + public byte[] getByteArray(Object tag, String key) { + if (tag == null || !(tag instanceof CompoundTag)) return null; + Optional optional = ((CompoundTag)tag).getByteArray(key); + return optional.orElse(null); + } + + @Override + public int[] getIntArray(Object tag, String key) { + if (tag == null || !(tag instanceof CompoundTag)) return null; + Optional optional = ((CompoundTag)tag).getIntArray(key); + return optional.orElse(null); + } + + @Override + public String getString(Object node, String tag) { + if (node == null || !(node instanceof CompoundTag)) return null; + Optional optional = ((CompoundTag)node).getString(tag); + return optional.orElse(null); + } + + @Override + public String getString(ItemStack stack, String tag) { + if (platform.getItemUtils().isEmpty(stack)) return null; + String meta = null; + Object tagObject = platform.getItemUtils().getTag(stack); + if (tagObject == null || !(tagObject instanceof CompoundTag)) return null; + Optional optional = ((CompoundTag)tagObject).getString(tag); + return optional.orElse(null); + } + + @Override + public Byte getOptionalByte(Object node, String tag) { + if (node == null || !(node instanceof CompoundTag)) return null; + Optional optional = ((CompoundTag)node).getByte(tag); + return optional.orElse(null); + } + + @Override + public Integer getOptionalInt(Object node, String tag) { + if (node == null || !(node instanceof CompoundTag)) return null; + Optional optional = ((CompoundTag)node).getInt(tag); + return optional.orElse(null); + } + + @Override + public Short getOptionalShort(Object node, String tag) { + if (node == null || !(node instanceof CompoundTag)) return null; + Optional optional = ((CompoundTag)node).getShort(tag); + return optional.orElse(null); + } + + @Override + public Double getOptionalDouble(Object node, String tag) { + if (node == null || !(node instanceof CompoundTag)) return null; + Optional optional = ((CompoundTag)node).getDouble(tag); + return optional.orElse(null); + } + + @Override + public Boolean getOptionalBoolean(Object node, String tag) { + if (node == null || !(node instanceof CompoundTag)) return null; + Optional optional = ((CompoundTag)node).getBoolean(tag); + return optional.orElse(null); + } + + @Override + public void setLong(Object node, String tag, long value) { + if (node == null || !(node instanceof CompoundTag)) return; + ((CompoundTag)node).putLong(tag, value); + } + + @Override + public void setBoolean(Object node, String tag, boolean value) { + if (node == null || !(node instanceof CompoundTag)) return; + ((CompoundTag)node).putBoolean(tag, value); + } + + @Override + public void setDouble(Object node, String tag, double value) { + if (node == null || !(node instanceof CompoundTag)) return; + ((CompoundTag)node).putDouble(tag, value); + } + + @Override + public void setInt(Object node, String tag, int value) { + if (node == null || !(node instanceof CompoundTag)) return; + ((CompoundTag)node).putInt(tag, value); + } + + @Override + public void setMetaShort(Object node, String tag, short value) { + if (node == null || !(node instanceof CompoundTag)) return; + ((CompoundTag)node).putShort(tag, value); + } + + @Override + public void removeMeta(Object node, String tag) { + if (node == null || !(node instanceof CompoundTag)) return; + ((CompoundTag)node).remove(tag); + } + + @Override + public void setTag(Object node, String tag, Object child) { + if (node == null || !(node instanceof CompoundTag)) return; + if (child == null) { + ((CompoundTag)node).remove(tag); + } else if (child instanceof Tag) { + ((CompoundTag)node).put(tag, (Tag)child); + } + } + + @Override + public boolean setTag(ItemStack stack, String tag, Object child) { + if (platform.getItemUtils().isEmpty(stack)) return false; + Object craft = platform.getItemUtils().getHandle(stack); + if (craft == null) return false; + Object node = platform.getItemUtils().getOrCreateTag(craft); + if (node == null || !(node instanceof CompoundTag)) return false; + if (child == null) { + ((CompoundTag)node).remove(tag); + } else { + ((CompoundTag)node).put(tag, (Tag)child); + } + return true; + } + + @Override + public void setString(Object node, String tag, String value) { + if (node == null || !(node instanceof CompoundTag)) return; + ((CompoundTag)node).putString(tag, value); + } + + @Override + public void setString(ItemStack stack, String tag, String value) { + if (platform.getItemUtils().isEmpty(stack)) return; + Object craft = platform.getItemUtils().getHandle(stack); + if (craft == null) return; + Object tagObject = platform.getItemUtils().getOrCreateTag(craft); + if (tagObject == null || !(tagObject instanceof CompoundTag)) return; + ((CompoundTag)tagObject).putString(tag, value); + } + + @Override + public void setIntArray(Object tag, String key, int[] value) { + if (tag == null || !(tag instanceof CompoundTag)) return; + ((CompoundTag)tag).put(key, new IntArrayTag(value)); + } + + @Override + public void setByteArray(Object tag, String key, byte[] value) { + if (tag == null || !(tag instanceof CompoundTag)) return; + ((CompoundTag)tag).put(key, new ByteArrayTag(value)); + } + + @Override + public void setEmptyList(Object tag, String key) { + if (tag == null || !(tag instanceof CompoundTag)) return; + ((CompoundTag)tag).put(key, new ListTag()); + } + + @Override + public void addToList(Object listObject, Object node) { + if (listObject == null || !(listObject instanceof ListTag) || !(node instanceof Tag)) return; + ListTag list = (ListTag)listObject; + list.add((Tag)node); + } + + @Override + public Object readTagFromStream(InputStream input) { + CompoundTag tag = null; + try { + tag = NbtIo.readCompressed(input, NbtAccounter.unlimitedHeap()); + } catch (Exception ex) { + platform.getLogger().log(Level.WARNING, "Error reading from NBT input stream", ex); + } + return tag; + } + + @Override + public boolean writeTagToStream(Object tag, OutputStream output) { + if (tag == null || !(tag instanceof CompoundTag)) return false; + try { + NbtIo.writeCompressed((CompoundTag)tag, output); + } catch (Exception ex) { + platform.getLogger().log(Level.WARNING, "Error writing NBT output stream", ex); + return false; + } + return true; + } + + @Override + public Collection getTagList(Object tag, String key) { + Collection list = new ArrayList<>(); + if (tag == null || !(tag instanceof CompoundTag)) { + return list; + } + + Optional optional = ((CompoundTag)tag).getList(key); + + if (optional.isPresent()) { + ListTag listTag = optional.get(); + int size = listTag.size(); + for (int i = 0; i < size; i++) { + Tag entry = listTag.get(i); + list.add(entry); + } + } + return list; + } + + @Override + public Object newCompoundTag() { + return new CompoundTag(); + } + + @Override + public boolean setSpawnEggEntityData(ItemStack spawnEgg, Entity entity, Object entityData) { + if (platform.getItemUtils().isEmpty(spawnEgg)) return false; + if (entityData == null || !(entityData instanceof CompoundTag)) return false; + + Object handle = platform.getItemUtils().getHandle(spawnEgg); + if (handle == null) return false; + net.minecraft.world.item.ItemStack itemStack = (net.minecraft.world.item.ItemStack)handle; + CraftEntity craft = (CraftEntity) entity; + net.minecraft.world.entity.Entity nmsEntity = craft.getHandle(); + TypedEntityData> typedEntityData = TypedEntityData.of(nmsEntity.getType(), (CompoundTag)entityData); + itemStack.set(DataComponents.ENTITY_DATA, typedEntityData); + return true; + } + + @Override + public boolean addTagsToNBT(Map tags, Object node) { + if (node == null) { + platform.getLogger().warning("Trying to save tags to a null node"); + return false; + } + if (!(node instanceof CompoundTag)) { + platform.getLogger().warning("Trying to save tags to a non-CompoundTag"); + return false; + } + + CompoundTag compoundTag = (CompoundTag)node; + for (Map.Entry tag : tags.entrySet()) { + Object value = tag.getValue(); + try { + Tag wrappedTag = wrapInTag(value); + if (wrappedTag == null) continue; + compoundTag.put(tag.getKey(), wrappedTag); + } catch (Exception ex) { + platform.getLogger().log(Level.WARNING, "Error saving item data tag " + tag.getKey(), ex); + } + } + + return true; + } + + @Override + public boolean saveTagsToNBT(Map tags, Object node, Set tagNames) { + if (node == null) { + platform.getLogger().warning("Trying to save tags to a null node"); + return false; + } + if (!(node instanceof CompoundTag)) { + platform.getLogger().warning("Trying to save tags to a non-CompoundTag"); + return false; + } + + CompoundTag compoundTag = (CompoundTag)node; + if (tagNames == null) { + tagNames = tags.keySet(); + } + + // Remove tags that were not included + Set currentTags = getTagKeys(node); + if (currentTags != null && !tagNames.containsAll(currentTags)) { + // Need to copy this, getKeys returns a live list and bad things can happen. + currentTags = new HashSet<>(currentTags); + } else { + currentTags = null; + } + + for (String tagName : tagNames) { + if (currentTags != null) currentTags.remove(tagName); + Object value = tags.get(tagName); + try { + Tag wrappedTag = wrapInTag(value); + if (wrappedTag == null) continue; + compoundTag.put(tagName, wrappedTag); + } catch (Exception ex) { + platform.getLogger().log(Level.WARNING, "Error saving item data tag " + tagName, ex); + } + } + + // Finish removing any remaining properties + if (currentTags != null) { + for (String currentTag : currentTags) { + platform.getNBTUtils().removeMeta(node, currentTag); + } + } + + return true; + } + + @Override + public Tag wrapInTag(Object value) { + if (value == null) return null; + Tag wrappedValue = null; + if (value instanceof Boolean) { + wrappedValue = ByteTag.valueOf((byte)((boolean)value ? 1 : 0)); + } else if (value instanceof Double) { + wrappedValue = DoubleTag.valueOf((Double)value); + } else if (value instanceof Float) { + wrappedValue = FloatTag.valueOf((Float) value); + } else if (value instanceof Integer) { + wrappedValue = IntTag.valueOf((Integer)value); + } else if (value instanceof Long) { + wrappedValue = LongTag.valueOf((Long) value); + } else if (value instanceof ConfigurationSection) { + wrappedValue = new CompoundTag(); + saveTagsToNBT((ConfigurationSection)value, wrappedValue, null); + } else if (value instanceof Map) { + wrappedValue = new CompoundTag(); + @SuppressWarnings("unchecked") + Map valueMap = (Map)value; + addTagsToNBT(valueMap, wrappedValue); + } else if (value instanceof Collection) { + @SuppressWarnings("unchecked") + Collection list = (Collection)value; + ListTag listMeta = new ListTag(); + if (list.size() > 1 && list instanceof List) { + @SuppressWarnings("unchecked") + List checkList = (List)value; + Object first = checkList.get(0); + Object second = checkList.get(1); + if (first instanceof String && !(second instanceof String)) { + list = new ArrayList<>(); + for (int i = 1; i < checkList.size(); i++) { + if (first.equals("I")) { + list.add(convertToInteger(checkList.get(i))); + } else if (first.equals("L")) { + list.add(convertToLong(checkList.get(i))); + } else if (first.equals("B")) { + list.add(convertToByte(checkList.get(i))); + } else { + list.add(checkList.get(i)); + } + } + if (first.equals("B")) { + wrappedValue = new ByteArrayTag(makeByteArray((List)list)); + } else if (first.equals("I")) { + wrappedValue = new IntArrayTag(makeIntArray((List)list)); + } else if (first.equals("L")) { + wrappedValue = new LongArrayTag(makeLongArray((List)list)); + } + } + } + if (wrappedValue == null) { + for (Object item : list) { + if (item != null) { + platform.getNBTUtils().addToList(listMeta, wrapInTag(item)); + } + } + wrappedValue = listMeta; + } + } else { + wrappedValue = StringTag.valueOf(value.toString()); + } + + return wrappedValue; + } + + @Override + public Set getTagKeys(Object tag) { + if (tag == null || !(tag instanceof CompoundTag)) { + return null; + } + return ((CompoundTag)tag).keySet(); + } + + @Override + public Object getMetaObject(Object tag, String key) { + if (tag == null || !(tag instanceof CompoundTag)) { + return null; + } + + try { + Tag metaBase = ((CompoundTag)tag).get(key); + return getTagValue(metaBase); + } catch (Exception ex) { + ex.printStackTrace(); + } + return null; + } + + @Override + public Object getTagValue(Object tag) throws IllegalAccessException, InvocationTargetException { + if (tag == null) return null; + Object value; + if (tag instanceof DoubleTag) { + value = ((DoubleTag)tag).asDouble().orElse(null); + } else if (tag instanceof IntTag) { + value = ((IntTag)tag).asInt().orElse(null); + } else if (tag instanceof LongTag) { + value = ((LongTag)tag).asLong().orElse(null); + } else if (tag instanceof FloatTag) { + value = ((FloatTag)tag).asFloat().orElse(null); + } else if (tag instanceof ShortTag) { + value = ((ShortTag)tag).asShort().orElse(null); + } else if (tag instanceof ByteTag) { + // This is kind of nasty. Really need a type-juggling container class for config properties. + value = ((ByteTag)tag).asByte().orElse(null); + if (value != null) { + if (value.equals((byte)0)) { + value = false; + } else if (value.equals((byte)1)) { + value = true; + } + } + } else if (tag instanceof ListTag) { + List converted = new ArrayList<>(); + for (Tag baseTag : (ListTag)tag) { + Object convertedBase = getTagValue(baseTag); + if (convertedBase != null) { + converted.add(convertedBase); + } + } + value = converted; + } else if (tag instanceof StringTag) { + value = ((StringTag)tag).asString().orElse(null); + } else if (tag instanceof IntArrayTag) { + value = ((IntArrayTag)tag).asIntArray().orElse(null); + } else if (tag instanceof ByteArrayTag) { + value = ((ByteArrayTag)tag).asByteArray().orElse(null); + } else if (tag instanceof LongArrayTag) { + value = ((LongArrayTag)tag).asLongArray().orElse(null); + } else if (tag instanceof CompoundTag) { + Map compoundMap = new HashMap<>(); + Set keys = getTagKeys(tag); + for (String key : keys) { + Tag baseTag = ((CompoundTag)tag).get(key); + Object convertedBase = getTagValue(baseTag); + if (convertedBase != null) { + compoundMap.put(key, convertedBase); + } + } + value = compoundMap; + } else { + value = null; // ??? + } + + return value; + } +} diff --git a/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/Platform.java b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/Platform.java new file mode 100644 index 0000000000..b4d34a9fa2 --- /dev/null +++ b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/Platform.java @@ -0,0 +1,31 @@ +package com.elmakers.mine.bukkit.utility.platform.v1_21_11; + +import com.elmakers.mine.bukkit.api.magic.MageController; +import com.elmakers.mine.bukkit.utility.platform.base.PlatformBase; + +public class Platform extends PlatformBase { + + public Platform(MageController controller) { + super(controller); + } + + @Override + protected com.elmakers.mine.bukkit.utility.platform.NBTUtils createNBTUtils() { + return new NBTUtils(this); + } + + @Override + protected com.elmakers.mine.bukkit.utility.platform.ItemUtils createItemUtils() { + return new ItemUtils(this); + } + + @Override + protected com.elmakers.mine.bukkit.utility.platform.CompatibilityUtils createCompatibilityUtils() { + return new CompatibilityUtils(this); + } + + @Override + protected com.elmakers.mine.bukkit.utility.platform.MobUtils createMobUtils() { + return new MobUtils(this); + } +} diff --git a/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/IdleGoal.java b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/IdleGoal.java new file mode 100644 index 0000000000..95c85f39e8 --- /dev/null +++ b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/IdleGoal.java @@ -0,0 +1,10 @@ +package com.elmakers.mine.bukkit.utility.platform.v1_21_11.goal; + +import net.minecraft.world.entity.ai.goal.Goal; + +public class IdleGoal extends Goal { + @Override + public boolean canUse() { + return true; + } +} diff --git a/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicCheckOwnerGoal.java b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicCheckOwnerGoal.java new file mode 100644 index 0000000000..596b6fdca8 --- /dev/null +++ b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicCheckOwnerGoal.java @@ -0,0 +1,24 @@ +package com.elmakers.mine.bukkit.utility.platform.v1_21_11.goal; + +import com.elmakers.mine.bukkit.utility.platform.Platform; + +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Mob; + +public class MagicCheckOwnerGoal extends MagicOwnerGoal { + public MagicCheckOwnerGoal(Platform platform, Mob tamed) { + super(platform, tamed); + } + + @Override + public boolean canUse() { + tamed.checkOwner(); + LivingEntity owner = tamed.getOwner(); + if (owner == null || owner.isDeadOrDying()) { + tamed.setOwner(null); + tamed.stop(); + } + // Note that this goal never becomes active, it is just here to clear the owner + return false; + } +} diff --git a/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicFindOwnerGoal.java b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicFindOwnerGoal.java new file mode 100644 index 0000000000..987fed0c16 --- /dev/null +++ b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicFindOwnerGoal.java @@ -0,0 +1,47 @@ +package com.elmakers.mine.bukkit.utility.platform.v1_21_11.goal; + +import java.util.List; +import java.util.function.Predicate; + +import com.elmakers.mine.bukkit.utility.platform.Platform; + +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Mob; + +public class MagicFindOwnerGoal extends MagicOwnerGoal { + private final double radius; + private final Predicate followPredicate; + + public MagicFindOwnerGoal(Platform platform, Mob mob, double radius, Class followType) { + super(platform, mob); + this.radius = radius; + this.followPredicate = (checkMob) -> checkMob != null && checkMob != mob && followType.isAssignableFrom(checkMob.getClass()); + } + + @Override + public boolean canUse() { + // We have to clear the owner cache each time to check and see if we still have an owner. + tamed.stop(); + tamed.checkOwner(); + if (tamed.getOwner() != null) { + return false; + } + + double closestSquared = 0; + LivingEntity closest = null; + List nearby = this.mob.level().getEntitiesOfClass(LivingEntity.class, this.mob.getBoundingBox().inflate(radius), this.followPredicate); + for (LivingEntity mob : nearby) { + double distance = mob.distanceToSqr(this.mob); + if (closest == null || distance < closestSquared) { + closest = mob; + closestSquared = distance; + } + } + if (closest != null) { + tamed.setOwner(closest); + } + + // This goal never activates, it's just here to set an owner + return false; + } +} diff --git a/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicFollowMobGoal.java b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicFollowMobGoal.java new file mode 100644 index 0000000000..7dd89bd330 --- /dev/null +++ b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicFollowMobGoal.java @@ -0,0 +1,82 @@ +package com.elmakers.mine.bukkit.utility.platform.v1_21_11.goal; + +import java.util.EnumSet; +import java.util.List; +import java.util.function.Predicate; + +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.ai.goal.Goal; +import net.minecraft.world.entity.ai.navigation.PathNavigation; + +public class MagicFollowMobGoal extends Goal { + private final Mob mob; + private final double speedModifier; + private final PathNavigation navigation; + private final int interval; + private final double radius; + private final double stopDistanceSquared; + private final Predicate followPredicate; + + // State + private int ticksRemaining = 0; + private LivingEntity followingMob; + + public MagicFollowMobGoal(Mob mob, double speedModifier, double radius, float stopDistance, int interval, Class followType) { + this.mob = mob; + this.radius = radius; + this.interval = interval; + this.speedModifier = speedModifier; + this.navigation = mob.getNavigation(); + this.stopDistanceSquared = stopDistance * stopDistance; + this.followPredicate = (checkMob) -> checkMob != null && checkMob != mob && followType.isAssignableFrom(checkMob.getClass()); + this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK)); + } + + @Override + public boolean canUse() { + // I could not figure out how to use followType directly here + List nearby = this.mob.level().getEntitiesOfClass(LivingEntity.class, this.mob.getBoundingBox().inflate(radius), this.followPredicate); + for (LivingEntity mob : nearby) { + if (!mob.isInvisible()) { + this.followingMob = mob; + return true; + } + } + + return false; + } + + @Override + public boolean canContinueToUse() { + if (followingMob == null) { + return false; + } + if (navigation.isDone()) { + return false; + } + return mob.distanceToSqr(followingMob) > stopDistanceSquared; + } + + @Override + public void start() { + this.ticksRemaining = 0; + } + + @Override + public void stop() { + super.stop(); + this.navigation.stop(); + } + + @Override + public void tick() { + if (followingMob != null && !mob.isLeashed()) { + this.mob.getLookControl().setLookAt(this.followingMob, 10.0F, this.mob.getMaxHeadXRot()); + if (--this.ticksRemaining <= 0) { + this.ticksRemaining = interval; + navigation.moveTo(this.followingMob, this.speedModifier); + } + } + } +} diff --git a/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicFollowOwnerGoal.java b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicFollowOwnerGoal.java new file mode 100644 index 0000000000..12f62c7846 --- /dev/null +++ b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicFollowOwnerGoal.java @@ -0,0 +1,126 @@ +package com.elmakers.mine.bukkit.utility.platform.v1_21_11.goal; + +import java.util.EnumSet; + +import org.bukkit.configuration.ConfigurationSection; + +import com.elmakers.mine.bukkit.utility.platform.Platform; +import com.elmakers.mine.bukkit.utility.random.RandomUtils; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.ai.navigation.PathNavigation; + +public class MagicFollowOwnerGoal extends MagicOwnerGoal { + private final double speedModifier; + private final PathNavigation navigation; + private final int interval; + private final double stopDistanceSquared; + private final double startDistanceSquared; + private final double teleportDistanceSquared; + + // State + private int ticksRemaining = 0; + + public MagicFollowOwnerGoal(Platform platform, Mob tamed, double speedModifier, float startDistance, float stopDistance, int interval, ConfigurationSection config) { + super(platform, tamed); + this.speedModifier = speedModifier; + this.navigation = tamed.getNavigation(); + this.startDistanceSquared = startDistance * startDistance; + this.stopDistanceSquared = stopDistance * stopDistance; + float teleportDistance = (float)config.getDouble("teleport_distance", 12); + if (!config.getBoolean("teleport", true)) { + teleportDistance = 0; + } + this.teleportDistanceSquared = teleportDistance * teleportDistance; + this.interval = interval; + this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK)); + } + + @Override + public boolean canUse() { + if (!super.canUse()) { + return false; + } + if (mob.distanceToSqr(tamed.getOwner()) < startDistanceSquared) { + return false; + } + return true; + } + + @Override + public boolean canContinueToUse() { + if (navigation.isDone()) { + return false; + } + if (tamed.isStay()) { + return false; + } + return mob.distanceToSqr(tamed.getOwner()) > stopDistanceSquared; + } + + @Override + public void start() { + this.ticksRemaining = 0; + } + + @Override + public void stop() { + super.stop(); + this.navigation.stop(); + } + + @Override + public void tick() { + this.mob.getLookControl().setLookAt(tamed.getOwner(), 10.0F, this.mob.getMaxHeadXRot()); + if (ticksRemaining-- <= 0) { + ticksRemaining = this.interval; + if (!this.mob.isLeashed() && !this.mob.isPassenger()) { + if (teleportDistanceSquared > 0 && this.mob.distanceToSqr(this.tamed.getOwner()) >= teleportDistanceSquared) { + this.teleportToOwner(); + } else { + this.navigation.moveTo(this.tamed.getOwner(), this.speedModifier); + } + } + } + } + + private void teleportToOwner() { + BlockPos blockPosition = this.tamed.getOwner().blockPosition(); + for (int i = 0; i < 10; ++i) { + int dx = RandomUtils.getRandomIntInclusive(-3, 3); + int dy = RandomUtils.getRandomIntInclusive(-1, 1); + int dz = RandomUtils.getRandomIntInclusive(-3, 3); + if (this.tryTeleportTo(blockPosition.getX() + dx, blockPosition.getY() + dy, blockPosition.getZ() + dz)) { + break; + } + } + } + + private boolean tryTeleportTo(int x, int y, int z) { + LivingEntity owner = tamed.getOwner(); + if (Math.abs(x - owner.getX()) < 2.0D && Math.abs(z - owner.getZ()) < 2.0D) { + return false; + } else if (!this.canTeleportTo(new BlockPos(x, y, z))) { + return false; + } else { + this.mob.snapTo(x + 0.5D, y, z + 0.5D, this.mob.getYRot(), this.mob.getXRot()); + this.navigation.stop(); + return true; + } + } + + private boolean canTeleportTo(BlockPos blockPosition) { + // TODO: The PathType enum doesn't seem accessible + /** + PathType pathType = WalkNodeEvaluator.getPathTypeStatic(mob, blockPosition.mutable()); + if (pathType != PathType.WALKABLE) { + return false; + } + */ + + blockPosition = blockPosition.subtract(this.mob.blockPosition()); + return mob.level().noCollision(this.mob, this.mob.getBoundingBox().move(blockPosition)); + } +} diff --git a/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicGoal.java b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicGoal.java new file mode 100644 index 0000000000..87ad5030c9 --- /dev/null +++ b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicGoal.java @@ -0,0 +1,142 @@ +package com.elmakers.mine.bukkit.utility.platform.v1_21_11.goal; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.EnumSet; +import java.util.List; + +import org.bukkit.ChatColor; + +import com.elmakers.mine.bukkit.utility.StringUtils; + +import net.minecraft.world.entity.ai.goal.Goal; + +public class MagicGoal extends Goal { + private final Collection goals; + private final boolean interruptable; + private Goal currentGoal; + + public MagicGoal(Collection goals, boolean interruptable) { + this(goals, interruptable, null); + } + + public MagicGoal(Collection goals, boolean interruptable, List flagKeys) { + this.goals = goals; + this.interruptable = interruptable; + + // You might think, "gee, why not just use the flags of the sub-goals?" + // Indeed! + // But unfortunately, as of 1.17.1 the Goal.getFlags method does not properly + // remapped and so will cause a runtime error. + // So we'll assume move/look for now + // TODO: Add parameters to all custom goals to control flags, maybe? + EnumSet flags; + if (flagKeys == null) { + flags = EnumSet.of(Flag.MOVE, Flag.LOOK); + } else { + flags = EnumSet.noneOf(Flag.class); + for (String flagKey : flagKeys) { + try { + Flag flag = Flag.valueOf(flagKey.toUpperCase()); + flags.add(flag); + } catch (Exception ignore) { + } + } + } + this.setFlags(flags); + } + + @Override + public boolean canUse() { + // Note that goals will perform important logic in their canUse and other + // methods that look like they would just be queries + // So we need to call them even if we don't care about the result. + for (Goal goal : goals) { + if (goal.canUse() && currentGoal == null) { + currentGoal = goal; + } + } + return currentGoal != null; + } + + @Override + public boolean canContinueToUse() { + boolean interrupt = false; + boolean continuing = false; + for (Goal goal : goals) { + if (goal == currentGoal) { + boolean canContinue = goal.canContinueToUse(); + if (canContinue) { + continuing = true; + } else { + interrupt = true; + } + } else { + // A higher-priority goal can interrupt + boolean canUse = goal.canUse(); + if (canUse && !continuing && (currentGoal == null || currentGoal.isInterruptable())) { + interrupt = true; + } + } + if (interrupt) { + return false; + } + } + return true; + } + + @Override + public boolean isInterruptable() { + for (Goal goal : goals) { + if (!goal.isInterruptable() && goal == currentGoal) { + return false; + } + } + return interruptable; + } + + @Override + public void start() { + if (currentGoal != null) { + currentGoal.start(); + } + } + + @Override + public void stop() { + if (currentGoal != null) { + currentGoal.stop(); + currentGoal = null; + } + } + + @Override + public void tick() { + if (currentGoal != null) { + currentGoal.tick(); + } + } + + protected String getSubDescription() { + List goalDescriptions = new ArrayList<>(); + for (Goal goal : goals) { + String goalDescription = goal.toString(); + if (goal == currentGoal) { + goalDescription = ChatColor.AQUA + goalDescription; + } else { + goalDescription = ChatColor.DARK_AQUA + goalDescription; + } + goalDescriptions.add(goalDescription); + } + return ChatColor.DARK_GRAY + " [" + StringUtils.join(goalDescriptions, " ") + ChatColor.DARK_GRAY + "]"; + } + + protected String getDescription() { + return "Group"; + } + + @Override + public String toString() { + return getDescription() + getSubDescription(); + } +} diff --git a/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicOwnerGoal.java b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicOwnerGoal.java new file mode 100644 index 0000000000..6877fa86ed --- /dev/null +++ b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicOwnerGoal.java @@ -0,0 +1,26 @@ +package com.elmakers.mine.bukkit.utility.platform.v1_21_11.goal; + +import com.elmakers.mine.bukkit.utility.platform.Platform; + +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.ai.goal.Goal; + +public abstract class MagicOwnerGoal extends Goal { + protected final Mob mob; + protected final MagicTamed tamed; + + public MagicOwnerGoal(Platform platform, Mob mob) { + this.tamed = new MagicTamed(platform, mob); + this.mob = mob; + } + + @Override + public boolean canUse() { + return tamed.canUse(); + } + + @Override + public void stop() { + tamed.stop(); + } +} diff --git a/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicOwnerHurtByTargetGoal.java b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicOwnerHurtByTargetGoal.java new file mode 100644 index 0000000000..56e54e1816 --- /dev/null +++ b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicOwnerHurtByTargetGoal.java @@ -0,0 +1,47 @@ +package com.elmakers.mine.bukkit.utility.platform.v1_21_11.goal; + +import java.lang.reflect.Method; + +import org.bukkit.entity.Entity; +import org.bukkit.event.entity.EntityTargetEvent; + +import com.elmakers.mine.bukkit.utility.platform.Platform; + +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Mob; + +public class MagicOwnerHurtByTargetGoal extends MagicOwnerTargetGoal { + + public MagicOwnerHurtByTargetGoal(Platform platform, Mob mob, Entity entity, boolean see, boolean reach) { + super(platform, mob, entity, see, reach); + } + + @Override + public boolean canUse() { + if (!super.canUse()) { + return false; + } + return canAttack(tamed.getOwner().getLastHurtByMob(), tamed.getOwner().getLastHurtByMobTimestamp()); + } + + @Override + public void start() { + try { + this.mob.setTarget(this.target, EntityTargetEvent.TargetReason.OWNER_ATTACKED_TARGET, true); + } catch (Error ignore) { + // Must be paper? + try { + Method setTargetMethod = this.mob.getClass().getMethod("setTarget", LivingEntity.class, EntityTargetEvent.TargetReason.class); + setTargetMethod.invoke(this.target, EntityTargetEvent.TargetReason.OWNER_ATTACKED_TARGET); + } catch (Exception e) { + // Fall back to API + ((org.bukkit.entity.Mob)(this.mob.getBukkitEntity())).setTarget((org.bukkit.entity.LivingEntity)this.target.getBukkitEntity()); + } + } LivingEntity owner = this.tamed.getOwner(); + if (owner != null) { + this.lastTargetUpdate = owner.getLastHurtByMobTimestamp(); + } + + super.start(); + } +} diff --git a/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicOwnerHurtTargetGoal.java b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicOwnerHurtTargetGoal.java new file mode 100644 index 0000000000..3ebfc8368e --- /dev/null +++ b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicOwnerHurtTargetGoal.java @@ -0,0 +1,48 @@ +package com.elmakers.mine.bukkit.utility.platform.v1_21_11.goal; + +import java.lang.reflect.Method; + +import org.bukkit.entity.Entity; +import org.bukkit.event.entity.EntityTargetEvent; + +import com.elmakers.mine.bukkit.utility.platform.Platform; + +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Mob; + +public class MagicOwnerHurtTargetGoal extends MagicOwnerTargetGoal { + + public MagicOwnerHurtTargetGoal(Platform platform, Mob mob, Entity entity, boolean see, boolean reach) { + super(platform, mob, entity, see, reach); + } + + @Override + public boolean canUse() { + if (!super.canUse()) { + return false; + } + return canAttack(tamed.getOwner().getLastHurtMob(), tamed.getOwner().getLastHurtMobTimestamp()); + } + + @Override + public void start() { + try { + this.mob.setTarget(this.target, EntityTargetEvent.TargetReason.OWNER_ATTACKED_TARGET, true); + } catch (Error ignore) { + // Must be paper? + try { + Method setTargetMethod = this.mob.getClass().getMethod("setTarget", LivingEntity.class, EntityTargetEvent.TargetReason.class); + setTargetMethod.invoke(this.target, EntityTargetEvent.TargetReason.OWNER_ATTACKED_TARGET); + } catch (Exception e) { + // Fall back to API + ((org.bukkit.entity.Mob)(this.mob.getBukkitEntity())).setTarget((org.bukkit.entity.LivingEntity)this.target.getBukkitEntity()); + } + } + LivingEntity owner = this.tamed.getOwner(); + if (owner != null) { + this.lastTargetUpdate = owner.getLastHurtMobTimestamp(); + } + + super.start(); + } +} diff --git a/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicOwnerTargetGoal.java b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicOwnerTargetGoal.java new file mode 100644 index 0000000000..7b0c410706 --- /dev/null +++ b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicOwnerTargetGoal.java @@ -0,0 +1,53 @@ +package com.elmakers.mine.bukkit.utility.platform.v1_21_11.goal; + +import java.util.EnumSet; + +import org.bukkit.entity.Entity; + +import com.elmakers.mine.bukkit.utility.platform.Platform; + +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.TamableAnimal; +import net.minecraft.world.entity.ai.goal.target.TargetGoal; +import net.minecraft.world.entity.ai.targeting.TargetingConditions; + +public abstract class MagicOwnerTargetGoal extends TargetGoal { + protected final MagicTamed tamed; + protected final Mob mob; + + // State + protected LivingEntity target; + protected int lastTargetUpdate; + + public MagicOwnerTargetGoal(Platform platform, Mob mob, Entity entity, boolean see, boolean reach) { + super(mob, see, reach); + this.mob = mob; + this.tamed = new MagicTamed(platform, mob); + this.setFlags(EnumSet.of(Flag.TARGET)); + } + + protected boolean canAttack(LivingEntity newTarget, int newTimestamp) { + this.target = newTarget; + if (lastTargetUpdate == newTimestamp) { + return false; + } + if (mob instanceof TamableAnimal) { + TamableAnimal tamable = (TamableAnimal)mob; + if (!tamable.wantsToAttack(target, tamed.getOwner())) { + return false; + } + } + return this.canAttack(this.target, TargetingConditions.DEFAULT); + } + + @Override + public boolean canUse() { + return tamed.canUse(); + } + + @Override + public void stop() { + tamed.stop(); + } +} diff --git a/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicPanicGoal.java b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicPanicGoal.java new file mode 100644 index 0000000000..cbdb041876 --- /dev/null +++ b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicPanicGoal.java @@ -0,0 +1,56 @@ +package com.elmakers.mine.bukkit.utility.platform.v1_21_11.goal; + +import net.minecraft.world.entity.PathfinderMob; +import net.minecraft.world.entity.ai.goal.PanicGoal; + +public class MagicPanicGoal extends PanicGoal { + private final int panicTime; + private final int calmTime; + private final boolean interruptable; + + // State + private long lastPanic; + + public MagicPanicGoal(PathfinderMob mob, double speed, int panicTime, int calmTime, boolean interruptable) { + super(mob, speed); + this.panicTime = panicTime; + this.calmTime = calmTime; + this.interruptable = interruptable; + } + + @Override + public boolean isInterruptable() { + return interruptable; + } + + @Override + public boolean canUse() { + if (this.mob.getLastHurtByMob() == null) { + return false; + } + long now = System.currentTimeMillis(); + long endPanic = lastPanic + panicTime; + if (now > endPanic && now < endPanic + calmTime) { + return false; + } + if (this.mob.isOnFire()) { + return super.canUse(); + } + + return this.findRandomPosition(); + } + + @Override + public void start() { + super.start(); + this.lastPanic = System.currentTimeMillis(); + } + + @Override + public boolean canContinueToUse() { + if (System.currentTimeMillis() > lastPanic + panicTime) { + return false; + } + return super.canContinueToUse(); + } +} diff --git a/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicTamed.java b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicTamed.java new file mode 100644 index 0000000000..fa39b94873 --- /dev/null +++ b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicTamed.java @@ -0,0 +1,66 @@ +package com.elmakers.mine.bukkit.utility.platform.v1_21_11.goal; + +import java.util.UUID; + +import org.bukkit.GameMode; +import org.bukkit.craftbukkit.v1_21_R7.entity.CraftEntity; +import org.bukkit.entity.Player; + +import com.elmakers.mine.bukkit.magic.MagicMetaKeys; +import com.elmakers.mine.bukkit.utility.platform.Platform; + +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Mob; + +public class MagicTamed { + protected final Platform platform; + protected final Mob mob; + + // State + protected LivingEntity owner; + + public MagicTamed(Platform platform, Mob tamed) { + this.platform = platform; + this.mob = tamed; + } + + protected void checkOwner() { + if (owner == null) { + UUID ownerUUID = platform.getCompatibilityUtils().getOwnerId(mob.getBukkitEntity()); + if (ownerUUID != null) { + CraftEntity bukkitEntity = (CraftEntity)platform.getCompatibilityUtils().getEntity(ownerUUID); + if (bukkitEntity != null && bukkitEntity.getHandle() instanceof LivingEntity) { + owner = (LivingEntity)bukkitEntity.getHandle(); + } + } + } + } + + public void setOwner(LivingEntity owner) { + this.owner = owner; + platform.getCompatibilityUtils().setOwner(mob.getBukkitEntity(), owner == null ? null : owner.getBukkitEntity()); + } + + public boolean isStay() { + return platform.getEnityMetadataUtils().getBoolean(mob.getBukkitEntity(), MagicMetaKeys.STAY); + } + + public boolean canUse() { + checkOwner(); + if (owner == null || (owner instanceof Player && ((Player)owner).getGameMode() == GameMode.SPECTATOR)) { + return false; + } + if (isStay()) { + return false; + } + return true; + } + + public void stop() { + this.owner = null; + } + + public LivingEntity getOwner() { + return owner; + } +} diff --git a/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicTemptGoal.java b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicTemptGoal.java new file mode 100644 index 0000000000..75cf6d7fea --- /dev/null +++ b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/MagicTemptGoal.java @@ -0,0 +1,128 @@ +package com.elmakers.mine.bukkit.utility.platform.v1_21_11.goal; + +import java.util.EnumSet; +import java.util.function.Predicate; +import javax.annotation.Nullable; + +import org.bukkit.craftbukkit.v1_21_R7.entity.CraftLivingEntity; +import org.bukkit.craftbukkit.v1_21_R7.event.CraftEventFactory; +import org.bukkit.event.entity.EntityTargetEvent.TargetReason; +import org.bukkit.event.entity.EntityTargetLivingEntityEvent; + +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.PathfinderMob; +import net.minecraft.world.entity.ai.attributes.Attributes; +import net.minecraft.world.entity.ai.goal.Goal; +import net.minecraft.world.entity.ai.targeting.TargetingConditions; +import net.minecraft.world.item.ItemStack; + +// Based on net.minecraft.world.entity.ai.goal.TemptGoal, fixed to not throw an error for mobs missing the tempt_range attribute +public class MagicTemptGoal extends Goal { + private static final TargetingConditions TEMPT_TARGETING = TargetingConditions.forNonCombat().ignoreLineOfSight(); + private final TargetingConditions targetingConditions; + protected final PathfinderMob mob; + private final double speedModifier; + private double px; + private double py; + private double pz; + private double pRotX; + private double pRotY; + private double temptRange; + @Nullable + protected LivingEntity b; + private int calmDown; + private boolean isRunning; + private final Predicate items; + private final boolean canScare; + + public MagicTemptGoal(PathfinderMob entitycreature, double d0, Predicate predicate, boolean flag, double temptRange) { + this.mob = entitycreature; + this.speedModifier = d0; + this.items = predicate; + this.canScare = flag; + this.temptRange = temptRange; + this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK)); + this.targetingConditions = TEMPT_TARGETING.copy().selector((entityliving, worldserver) -> { + return this.shouldFollow(entityliving); + }); + } + + public boolean canUse() { + if (this.calmDown > 0) { + --this.calmDown; + return false; + } else { + double mobTempRange = mob.getAttributes().hasAttribute(Attributes.TEMPT_RANGE) ? this.mob.getAttributeValue(Attributes.TEMPT_RANGE) : this.temptRange; + this.b = getServerLevel(this.mob).getNearestPlayer(this.targetingConditions.range(mobTempRange), this.mob); + if (this.b != null) { + EntityTargetLivingEntityEvent event = CraftEventFactory.callEntityTargetLivingEvent(this.mob, this.b, TargetReason.TEMPT); + if (event.isCancelled()) { + return false; + } + + this.b = event.getTarget() == null ? null : ((CraftLivingEntity)event.getTarget()).getHandle(); + } + + return this.b != null; + } + } + + private boolean shouldFollow(LivingEntity entityliving) { + return this.items.test(entityliving.getMainHandItem()) || this.items.test(entityliving.getOffhandItem()); + } + + public boolean canContinueToUse() { + if (this.canScare()) { + if (this.mob.distanceToSqr(this.b) < 36.0) { + if (this.b.distanceToSqr(this.px, this.py, this.pz) > 0.010000000000000002) { + return false; + } + + if (Math.abs((double)this.b.getXRot() - this.pRotX) > 5.0 || Math.abs((double)this.b.getYRot() - this.pRotY) > 5.0) { + return false; + } + } else { + this.px = this.b.getX(); + this.py = this.b.getY(); + this.pz = this.b.getZ(); + } + + this.pRotX = (double)this.b.getXRot(); + this.pRotY = (double)this.b.getYRot(); + } + + return this.canUse(); + } + + protected boolean canScare() { + return this.canScare; + } + + public void start() { + this.px = this.b.getX(); + this.py = this.b.getY(); + this.pz = this.b.getZ(); + this.isRunning = true; + } + + public void stop() { + this.b = null; + this.mob.getNavigation().stop(); + this.calmDown = reducedTickDelay(100); + this.isRunning = false; + } + + public void tick() { + this.mob.getLookControl().setLookAt(this.b, (float)(this.mob.getMaxHeadYRot() + 20), (float)this.mob.getMaxHeadXRot()); + if (this.mob.distanceToSqr(this.b) < 6.25) { + this.mob.getNavigation().stop(); + } else { + this.mob.getNavigation().moveTo(this.b, this.speedModifier); + } + + } + + public boolean isRunning() { + return this.isRunning; + } +} diff --git a/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/RequirementsGoal.java b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/RequirementsGoal.java new file mode 100644 index 0000000000..572cd1cf57 --- /dev/null +++ b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/RequirementsGoal.java @@ -0,0 +1,49 @@ +package com.elmakers.mine.bukkit.utility.platform.v1_21_11.goal; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.bukkit.ChatColor; + +import com.elmakers.mine.bukkit.api.magic.Mage; +import com.elmakers.mine.bukkit.api.magic.MageController; +import com.elmakers.mine.bukkit.api.requirements.Requirement; +import com.elmakers.mine.bukkit.utility.StringUtils; + +import net.minecraft.world.entity.ai.goal.Goal; + +public class RequirementsGoal extends MagicGoal { + private final Collection requirements; + private final Mage mage; + + public RequirementsGoal(Mage mage, Collection goals, boolean interruptable, Collection requirements) { + super(goals, interruptable); + this.mage = mage; + this.requirements = requirements; + } + + @Override + public boolean canUse() { + return super.canUse() && checkRequirements(); + } + + @Override + public boolean canContinueToUse() { + return super.canContinueToUse() && checkRequirements(); + } + + protected boolean checkRequirements() { + MageController controller = mage.getController(); + return controller.checkRequirements(mage.getContext(), requirements) == null; + } + + @Override + protected String getDescription() { + List requirementDescriptions = new ArrayList<>(); + for (Requirement requirement : requirements) { + requirementDescriptions.add(requirement.toString()); + } + return "Requirements" + ChatColor.GRAY + "(" + ChatColor.DARK_AQUA + StringUtils.join(requirementDescriptions, " ") + ChatColor.GRAY + ")"; + } +} diff --git a/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/SpinGoal.java b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/SpinGoal.java new file mode 100644 index 0000000000..f629854c06 --- /dev/null +++ b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/SpinGoal.java @@ -0,0 +1,25 @@ +package com.elmakers.mine.bukkit.utility.platform.v1_21_11.goal; + +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.ai.goal.Goal; + +public class SpinGoal extends Goal { + private final Mob mob; + private final float degrees; + + public SpinGoal(Mob mob, float degrees) { + this.mob = mob; + this.degrees = degrees; + } + + @Override + public boolean canUse() { + return true; + } + + @Override + public void tick() { + super.tick(); + mob.snapTo(mob.blockPosition(), mob.getYRot() + degrees, mob.getXRot()); + } +} diff --git a/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/TriggerGoal.java b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/TriggerGoal.java new file mode 100644 index 0000000000..d98c7abc08 --- /dev/null +++ b/CompatibilityLib/v1_21_11/src/main/java/com/elmakers/mine/bukkit/utility/platform/v1_21_11/goal/TriggerGoal.java @@ -0,0 +1,39 @@ +package com.elmakers.mine.bukkit.utility.platform.v1_21_11.goal; + +import java.util.Collection; + +import org.bukkit.ChatColor; + +import com.elmakers.mine.bukkit.api.magic.Mage; + +import net.minecraft.world.entity.ai.goal.Goal; + +public class TriggerGoal extends MagicGoal { + private final Mage mage; + private final String trigger; + private final int interval; + private long lastTrigger; + + public TriggerGoal(Mage mage, Collection goals, boolean interruptable, String trigger, int interval) { + super(goals, interruptable); + this.mage = mage; + this.interval = interval * 50; + this.lastTrigger = System.currentTimeMillis(); + this.trigger = trigger; + } + + @Override + public void tick() { + super.tick(); + long now = System.currentTimeMillis(); + if (now > lastTrigger + interval) { + lastTrigger = now; + mage.trigger(trigger); + } + } + + @Override + protected String getDescription() { + return "Trigger" + ChatColor.GRAY + "(" + ChatColor.DARK_AQUA + trigger + ChatColor.GRAY + ")"; + } +} diff --git a/Magic/pom.xml b/Magic/pom.xml index 8270e93962..a5ca737f31 100644 --- a/Magic/pom.xml +++ b/Magic/pom.xml @@ -314,7 +314,7 @@ fr.neatmonster nocheatplus - 3.12.1-SNAPSHOT + 3.16.1-SNAPSHOT provided @@ -344,7 +344,7 @@ me.libraryaddict.disguises libsdisguises - 11.0.0 + 11.0.13 provided @@ -578,6 +578,10 @@ Lumine Releases https://mvn.lumine.io/repository/maven-public/ + + libsdisguises-public + https://mvn.lib.co.nz/public/ +