diff --git a/pom.xml b/pom.xml index a29c55494..fdab99147 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ me.lokka30 LevelledMobs - 3.8.2 b721 + 3.9.0 b727 jar LevelledMobs The Ultimate RPG Mob Levelling Plugin @@ -33,38 +33,6 @@ net.md-5 specialsource-maven-plugin 1.2.2 - - - package - - remap - - remap-obf - - org.spigotmc:minecraft-server:1.19.2-R0.1-SNAPSHOT:txt:maps-mojang - true - org.spigotmc:spigot:1.19.2-R0.1-SNAPSHOT:jar:remapped-mojang - - true - remapped-obf - - - - package - - remap - - remap-spigot - - - ${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar - - org.spigotmc:minecraft-server:1.19.2-R0.1-SNAPSHOT:csrg:maps-spigot - org.spigotmc:spigot:1.19.2-R0.1-SNAPSHOT:jar:remapped-obf - - - - org.apache.maven.plugins @@ -188,13 +156,6 @@ 1.19.2-R0.1-SNAPSHOT provided - - org.spigotmc - spigot - 1.19.2-R0.1-SNAPSHOT - remapped-mojang - provided - org.bstats bstats-bukkit @@ -228,7 +189,7 @@ com.google.code.gson gson - 2.9.0 + 2.10 provided diff --git a/src/main/java/me/lokka30/levelledmobs/LevelledMobs.java b/src/main/java/me/lokka30/levelledmobs/LevelledMobs.java index d9fbb680b..1aae085e5 100644 --- a/src/main/java/me/lokka30/levelledmobs/LevelledMobs.java +++ b/src/main/java/me/lokka30/levelledmobs/LevelledMobs.java @@ -31,6 +31,7 @@ import me.lokka30.levelledmobs.misc.NamespacedKeys; import me.lokka30.levelledmobs.misc.NametagTimerChecker; import me.lokka30.levelledmobs.misc.YmlParsingHelper; +import me.lokka30.levelledmobs.nms.Definitions; import me.lokka30.levelledmobs.rules.RulesManager; import me.lokka30.levelledmobs.rules.RulesParsingManager; import me.lokka30.levelledmobs.util.ConfigUtils; @@ -76,6 +77,7 @@ public final class LevelledMobs extends JavaPlugin { public YmlParsingHelper helperSettings; public long playerLevellingMinRelevelTime; public int maxPlayersRecorded; + private Definitions definitions; private static LevelledMobs instance; // Configuration @@ -101,6 +103,7 @@ public void onLoad() { public void onEnable() { final QuickTimer timer = new QuickTimer(); + this.definitions = new Definitions(); this.nametagQueueManager = new NametagQueueManager(this); this.mobsQueueManager = new MobsQueueManager(this); this.companion = new Companion(this); @@ -207,6 +210,10 @@ public static LevelledMobs getInstance(){ return instance; } + public @NotNull Definitions getDefinitions(){ + return this.definitions; + } + @Override public void onDisable() { final QuickTimer disableTimer = new QuickTimer(); diff --git a/src/main/java/me/lokka30/levelledmobs/commands/subcommands/RulesSubcommand.java b/src/main/java/me/lokka30/levelledmobs/commands/subcommands/RulesSubcommand.java index 7b2e36bb0..3b5cdcf09 100644 --- a/src/main/java/me/lokka30/levelledmobs/commands/subcommands/RulesSubcommand.java +++ b/src/main/java/me/lokka30/levelledmobs/commands/subcommands/RulesSubcommand.java @@ -157,7 +157,7 @@ private void forceRelevel(final CommandSender sender) { for (final World world : Bukkit.getWorlds()) { worldCount++; for (final Entity entity : world.getEntities()) { - if (!(entity instanceof LivingEntity)) { + if (!(entity instanceof LivingEntity) || entity instanceof Player) { continue; } diff --git a/src/main/java/me/lokka30/levelledmobs/listeners/EntityDamageListener.java b/src/main/java/me/lokka30/levelledmobs/listeners/EntityDamageListener.java index ed117c1e3..1ec0300b9 100644 --- a/src/main/java/me/lokka30/levelledmobs/listeners/EntityDamageListener.java +++ b/src/main/java/me/lokka30/levelledmobs/listeners/EntityDamageListener.java @@ -201,9 +201,9 @@ private void processRangedDamage2(@NotNull final LivingEntityWrapper shooter, main.mobsQueueManager.addToQueue(new QueueItem(shooter, event)); } - final double newDamage = - event.getDamage() + main.mobDataManager.getAdditionsForLevel(shooter, - Addition.CUSTOM_RANGED_ATTACK_DAMAGE, event.getDamage()); + final float newDamage = + (float) event.getDamage() + main.mobDataManager.getAdditionsForLevel(shooter, + Addition.CUSTOM_RANGED_ATTACK_DAMAGE, (float) event.getDamage()); Utils.debugLog(main, DebugType.RANGED_DAMAGE_MODIFICATION, String.format( "&7Source: &b%s&7 (lvl &b%s&7), damage: &b%s&7, new damage: &b%s&7", shooter.getNameIfBaby(), shooter.getMobLevel(), event.getDamage(), newDamage)); @@ -238,7 +238,7 @@ private void processOtherRangedDamage(@NotNull final EntityDamageByEntityEvent e final LivingEntityWrapper lmEntity = LivingEntityWrapper.getInstance(livingEntity, main); event.setDamage( main.mobDataManager.getAdditionsForLevel(lmEntity, Addition.CUSTOM_RANGED_ATTACK_DAMAGE, - event.getDamage())); // use ranged attack damage value + (float) event.getDamage())); // use ranged attack damage value Utils.debugLog(main, DebugType.RANGED_DAMAGE_MODIFICATION, "New guardianDamage: &b" + event.getDamage()); lmEntity.free(); diff --git a/src/main/java/me/lokka30/levelledmobs/managers/LevelManager.java b/src/main/java/me/lokka30/levelledmobs/managers/LevelManager.java index 4472244a2..b7b835cd4 100644 --- a/src/main/java/me/lokka30/levelledmobs/managers/LevelManager.java +++ b/src/main/java/me/lokka30/levelledmobs/managers/LevelManager.java @@ -544,7 +544,7 @@ public void setLevelledItemDrops(final LivingEntityWrapper lmEntity, if (!doNotMultiplyDrops && !dropsToMultiply.isEmpty()) { // Get currentDrops added per level valu final double additionValue = main.mobDataManager.getAdditionsForLevel(lmEntity, - Addition.CUSTOM_ITEM_DROP, 2.0); + Addition.CUSTOM_ITEM_DROP, 2.0f); if (additionValue == -1) { Utils.debugLog(main, DebugType.SET_LEVELLED_ITEM_DROPS, String.format( "&7Mob: &b%s&7, mob-lvl: &b%s&7, removing any drops present", @@ -611,7 +611,7 @@ public void multiplyDrop(final LivingEntityWrapper lmEntity, // look thru the animal's inventory for leather. That is the only item that will get duplicated for (final ItemStack item : chestItems) { if (item.getType() == Material.LEATHER) { - return Collections.singletonList(item); + return List.of(item); } } @@ -669,7 +669,7 @@ public void removeVanillaDrops(@NotNull final LivingEntityWrapper lmEntity, public int getLevelledExpDrops(@NotNull final LivingEntityWrapper lmEntity, final double xp) { if (lmEntity.isLevelled()) { final double dropAddition = main.mobDataManager.getAdditionsForLevel(lmEntity, - Addition.CUSTOM_XP_DROP, 3.0); + Addition.CUSTOM_XP_DROP, 3.0f); double newXp = 0; if (dropAddition > -1) { newXp = Math.round(xp + (xp * dropAddition)); @@ -1145,7 +1145,7 @@ private void checkLevelledEntity(@NotNull final LivingEntityWrapper lmEntity, final boolean preserveMobName = !main.nametagQueueManager.nmsHandler.isUsingProtocolLib; final NametagResult nametag = main.levelManager.getNametag(lmEntity, false, preserveMobName); main.nametagQueueManager.addToQueue( - new QueueItem(lmEntity, nametag, Collections.singletonList(player))); + new QueueItem(lmEntity, nametag, List.of(player))); } } diff --git a/src/main/java/me/lokka30/levelledmobs/managers/MobDataManager.java b/src/main/java/me/lokka30/levelledmobs/managers/MobDataManager.java index 4dbb8041e..ea7f7a1d7 100644 --- a/src/main/java/me/lokka30/levelledmobs/managers/MobDataManager.java +++ b/src/main/java/me/lokka30/levelledmobs/managers/MobDataManager.java @@ -13,6 +13,7 @@ import me.lokka30.levelledmobs.misc.CachedModalList; import me.lokka30.levelledmobs.misc.DebugType; import me.lokka30.levelledmobs.misc.LivingEntityWrapper; +import me.lokka30.levelledmobs.rules.FineTuningAttributes; import me.lokka30.levelledmobs.rules.VanillaBonusEnum; import me.lokka30.levelledmobs.util.Utils; import org.bukkit.Material; @@ -90,9 +91,9 @@ void setAdditionsForLevel(@NotNull final LivingEntityWrapper lmEntity, final @NotNull Attribute attribute, final Addition addition) { final boolean useStaticValues = main.helperSettings.getBoolean(main.settingsCfg, "attributes-use-preset-base-values"); - final double defaultValue = useStaticValues ? - (double) Objects.requireNonNull(getAttributeDefaultValue(lmEntity, attribute)) : - Objects.requireNonNull(lmEntity.getLivingEntity().getAttribute(attribute)) + final float defaultValue = useStaticValues ? + (float) Objects.requireNonNull(getAttributeDefaultValue(lmEntity, attribute)) : + (float) Objects.requireNonNull(lmEntity.getLivingEntity().getAttribute(attribute)) .getBaseValue(); final double additionValue = getAdditionsForLevel(lmEntity, addition, defaultValue); @@ -172,115 +173,44 @@ void setAdditionsForLevel(@NotNull final LivingEntityWrapper lmEntity, } } - public final double getAdditionsForLevel(final LivingEntityWrapper lmEntity, - final Addition addition, final double defaultValue) { - final double maxLevel = main.rulesManager.getRuleMobMaxLevel(lmEntity); - - double attributeValue = 0; - double attributeMax = 0; - - if (lmEntity.getFineTuningAttributes() != null) { - switch (addition) { - case CUSTOM_XP_DROP: - if (lmEntity.getFineTuningAttributes().xpDrop != null) { - attributeValue = lmEntity.getFineTuningAttributes().xpDrop; - } - if (attributeValue == -1.0) { - return -1; - } - break; - case CUSTOM_ITEM_DROP: - if (lmEntity.getFineTuningAttributes().itemDrop != null) { - attributeValue = lmEntity.getFineTuningAttributes().itemDrop; - } - if (attributeValue == -1.0) { - return -1; - } - break; - case ATTRIBUTE_MAX_HEALTH: - if (lmEntity.getFineTuningAttributes().maxHealth != null) { - attributeValue = lmEntity.getFineTuningAttributes().maxHealth; - } - break; - case ATTRIBUTE_ATTACK_DAMAGE: - if (lmEntity.getFineTuningAttributes().attackDamage != null) { - attributeValue = lmEntity.getFineTuningAttributes().attackDamage; - } - break; - case ATTRIBUTE_MOVEMENT_SPEED: - if (lmEntity.getFineTuningAttributes().movementSpeed != null) { - attributeValue = lmEntity.getFineTuningAttributes().movementSpeed; - } - break; - case CUSTOM_RANGED_ATTACK_DAMAGE: - if (lmEntity.getFineTuningAttributes().rangedAttackDamage != null) { - attributeValue = lmEntity.getFineTuningAttributes().rangedAttackDamage; - } - break; - case CREEPER_BLAST_DAMAGE: - if (lmEntity.getFineTuningAttributes().creeperExplosionRadius != null) { - attributeValue = lmEntity.getFineTuningAttributes().creeperExplosionRadius; - } - break; - case ATTRIBUTE_HORSE_JUMP_STRENGTH: - if (lmEntity.getFineTuningAttributes().horseJumpStrength != null) { - attributeValue = lmEntity.getFineTuningAttributes().horseJumpStrength; - } - break; - case ATTRIBUTE_ARMOR_BONUS: - attributeMax = 30.0; - if (lmEntity.getFineTuningAttributes().armorBonus != null) { - attributeValue = lmEntity.getFineTuningAttributes().armorBonus; - } - break; - case ATTRIBUTE_ARMOR_TOUGHNESS: - attributeMax = 50.0; - if (lmEntity.getFineTuningAttributes().armorToughness != null) { - attributeValue = lmEntity.getFineTuningAttributes().armorToughness; - } - break; - case ATTRIBUTE_ATTACK_KNOCKBACK: - attributeMax = 5.0; - if (lmEntity.getFineTuningAttributes().attackKnockback != null) { - attributeValue = lmEntity.getFineTuningAttributes().attackKnockback; - } - break; - case ATTRIBUTE_FLYING_SPEED: - if (lmEntity.getFineTuningAttributes().flyingSpeed != null) { - attributeValue = lmEntity.getFineTuningAttributes().flyingSpeed; - } - break; - case ATTRIBUTE_KNOCKBACK_RESISTANCE: - attributeMax = 1.0; - if (lmEntity.getFineTuningAttributes().knockbackResistance != null) { - attributeValue = lmEntity.getFineTuningAttributes().knockbackResistance; - } - break; - case ATTRIBUTE_ZOMBIE_SPAWN_REINFORCEMENTS: - attributeMax = 1.0; - if (lmEntity.getFineTuningAttributes().zombieReinforcements != null) { - attributeValue = lmEntity.getFineTuningAttributes().zombieReinforcements; - } - break; - case ATTRIBUTE_FOLLOW_RANGE: - if (lmEntity.getFineTuningAttributes().followRange != null) { - attributeValue = lmEntity.getFineTuningAttributes().followRange; - } - break; + public final float getAdditionsForLevel(final LivingEntityWrapper lmEntity, + final Addition addition, final float defaultValue) { + final float maxLevel = main.rulesManager.getRuleMobMaxLevel(lmEntity); + + //double attributeValue = 0; + final FineTuningAttributes fineTuning = lmEntity.getFineTuningAttributes(); + FineTuningAttributes.Multiplier multiplier = null; + float attributeMax = 0; + + if (fineTuning != null) { + multiplier = fineTuning.getItem(addition); + switch (addition){ + case ATTRIBUTE_ARMOR_BONUS -> attributeMax = 30.0f; + case ATTRIBUTE_ARMOR_TOUGHNESS -> attributeMax = 50.0f; + case ATTRIBUTE_ATTACK_KNOCKBACK -> attributeMax = 5.0f; + case ATTRIBUTE_KNOCKBACK_RESISTANCE, + ATTRIBUTE_ZOMBIE_SPAWN_REINFORCEMENTS -> attributeMax = 1.0f; } } - if (maxLevel == 0 || attributeValue == 0) { - return 0.0; + if (maxLevel == 0 || multiplier == null) { + return 0.0f; } - // only used for 5 specific attributes - if (attributeMax > 0.0) { - return (lmEntity.getMobLevel() / maxLevel) * (attributeMax * attributeValue); - } else - // normal formula for most attributes - { - return (defaultValue * attributeValue) * ((lmEntity.getMobLevel()) / maxLevel); + final float multiplierValue = multiplier.value(); + + if (fineTuning.useStacked || multiplier.useStacked()){ + return (float) lmEntity.getMobLevel() * multiplierValue; + } + else { + // only used for 5 specific attributes + if (attributeMax > 0.0) { + return (lmEntity.getMobLevel() / maxLevel) * (attributeMax * multiplierValue); + } else + // normal formula for most attributes + { + return (defaultValue * multiplierValue) * ((lmEntity.getMobLevel()) / maxLevel); + } } } } diff --git a/src/main/java/me/lokka30/levelledmobs/misc/LivingEntityWrapper.java b/src/main/java/me/lokka30/levelledmobs/misc/LivingEntityWrapper.java index f1bf506ee..453d13ed9 100644 --- a/src/main/java/me/lokka30/levelledmobs/misc/LivingEntityWrapper.java +++ b/src/main/java/me/lokka30/levelledmobs/misc/LivingEntityWrapper.java @@ -63,7 +63,7 @@ private LivingEntityWrapper(final @NotNull LevelledMobs main) { // privates: private LivingEntity livingEntity; - @NotNull private Set applicableGroups; + private @NotNull Set applicableGroups; private boolean hasCache; private boolean isBuildingCache; private boolean groupsAreBuilt; @@ -74,9 +74,9 @@ private LivingEntityWrapper(final @NotNull LevelledMobs main) { private long nametagCooldownTime; private String sourceSpawnerName; private String sourceSpawnEggName; - @NotNull private final List applicableRules; + private @NotNull final List applicableRules; private List spawnedWGRegions; - @NotNull private final List mobExternalTypes; + private @NotNull final List mobExternalTypes; private FineTuningAttributes fineTuningAttributes; private LevelledMobSpawnReason spawnReason; private Player playerForLevelling; @@ -105,7 +105,7 @@ private LivingEntityWrapper(final @NotNull LevelledMobs main) { public Player playerForPermissionsCheck; public CommandSender summonedSender; - @NotNull public static LivingEntityWrapper getInstance(final LivingEntity livingEntity, + public @NotNull static LivingEntityWrapper getInstance(final LivingEntity livingEntity, final @NotNull LevelledMobs main) { final LivingEntityWrapper lew; @@ -369,7 +369,7 @@ private void cachePrevChanceResults() { } } - @Nullable public Map getPrevChanceRuleResults() { + public @Nullable Map getPrevChanceRuleResults() { return this.prevChanceRuleResults; } @@ -377,11 +377,11 @@ public LivingEntity getLivingEntity() { return this.livingEntity; } - @NotNull public String getTypeName() { + public @NotNull String getTypeName() { return this.livingEntity.getType().toString(); } - @NotNull public Set getApplicableGroups() { + public @NotNull Set getApplicableGroups() { if (!groupsAreBuilt) { this.applicableGroups = buildApplicableGroupsForMob(); groupsAreBuilt = true; @@ -398,7 +398,7 @@ public long getNametagCooldownTime() { return this.nametagCooldownTime; } - @Nullable public Player getPlayerForLevelling() { + public @Nullable Player getPlayerForLevelling() { synchronized (playerLock) { return this.playerForLevelling; } @@ -411,7 +411,7 @@ public void setPlayerForLevelling(final Player player) { this.playerForPermissionsCheck = player; } - @Nullable public FineTuningAttributes getFineTuningAttributes() { + public @Nullable FineTuningAttributes getFineTuningAttributes() { if (!hasCache) { buildCache(); } @@ -419,7 +419,7 @@ public void setPlayerForLevelling(final Player player) { return this.fineTuningAttributes; } - @NotNull public List getApplicableRules() { + public @NotNull List getApplicableRules() { if (!hasCache) { buildCache(); } @@ -445,7 +445,7 @@ public boolean isLevelled() { return this.livingEntity.getType(); } - @NotNull public PersistentDataContainer getPDC() { + public @NotNull PersistentDataContainer getPDC() { return livingEntity.getPersistentDataContainer(); } @@ -466,7 +466,7 @@ public boolean isBabyMob() { return false; } - @NotNull public LevelledMobSpawnReason getSpawnReason() { + public @NotNull LevelledMobSpawnReason getSpawnReason() { if (this.spawnReason != null) { return this.spawnReason; } @@ -633,7 +633,7 @@ public void setSourceSpawnerName(final String name) { } } - @Nullable public String getSourceSpawnerName() { + public @Nullable String getSourceSpawnerName() { if (this.sourceSpawnerName != null) { return this.sourceSpawnerName; } @@ -657,7 +657,7 @@ public void setSourceSpawnerName(final String name) { return this.sourceSpawnerName; } - @Nullable public String getSourceSpawnEggName() { + public @Nullable String getSourceSpawnEggName() { if (this.sourceSpawnEggName != null) { return this.sourceSpawnEggName; } @@ -680,7 +680,7 @@ public void setSourceSpawnerName(final String name) { return this.sourceSpawnEggName; } - @NotNull public String getNameIfBaby() { + public @NotNull String getNameIfBaby() { return this.isBabyMob() ? "BABY_" + getTypeName() : getTypeName(); @@ -697,7 +697,7 @@ public void setMobExternalType( } } - @NotNull public List getMobExternalTypes() { + public @NotNull List getMobExternalTypes() { return this.mobExternalTypes; } @@ -717,7 +717,7 @@ public boolean hasOverridenEntityName() { } } - @Nullable public String getOverridenEntityName() { + public @Nullable String getOverridenEntityName() { if (!getPDCLock()) { return null; } @@ -730,7 +730,7 @@ public boolean hasOverridenEntityName() { } } - @NotNull public String getWGRegionName() { + public @NotNull String getWGRegionName() { if (this.spawnedWGRegions == null || this.spawnedWGRegions.isEmpty()) { return ""; } @@ -843,7 +843,7 @@ public boolean isWasSummoned() { return this.wasSummoned; } - @NotNull private Set buildApplicableGroupsForMob() { + private @NotNull Set buildApplicableGroupsForMob() { final Set groups = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); for (final Map.Entry> mobGroup : main.customMobGroups.entrySet()) { diff --git a/src/main/java/me/lokka30/levelledmobs/misc/NametagTimerChecker.java b/src/main/java/me/lokka30/levelledmobs/misc/NametagTimerChecker.java index 91185dcd7..238b5e96e 100644 --- a/src/main/java/me/lokka30/levelledmobs/misc/NametagTimerChecker.java +++ b/src/main/java/me/lokka30/levelledmobs/misc/NametagTimerChecker.java @@ -1,6 +1,5 @@ package me.lokka30.levelledmobs.misc; - import java.time.Duration; import java.time.Instant; import java.util.HashMap; @@ -10,6 +9,7 @@ import java.util.Queue; import java.util.WeakHashMap; import me.lokka30.levelledmobs.LevelledMobs; +import me.lokka30.levelledmobs.result.NametagResult; import me.lokka30.levelledmobs.rules.NametagVisibilityEnum; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; @@ -107,8 +107,9 @@ public void checkNametags() { final LivingEntityWrapper lmEntity = LivingEntityWrapper.getInstance( livingEntity, main); - main.levelManager.updateNametag(lmEntity, - main.levelManager.getNametag(lmEntity, false), List.of(player)); + + final NametagResult nametag = main.levelManager.getNametag(lmEntity, false, true); + main.levelManager.updateNametag(lmEntity, nametag, List.of(player)); lmEntity.free(); } diff --git a/src/main/java/me/lokka30/levelledmobs/nms/ComponentUtils.java b/src/main/java/me/lokka30/levelledmobs/nms/ComponentUtils.java new file mode 100644 index 000000000..c1cc520ec --- /dev/null +++ b/src/main/java/me/lokka30/levelledmobs/nms/ComponentUtils.java @@ -0,0 +1,88 @@ +package me.lokka30.levelledmobs.nms; + +import me.lokka30.levelledmobs.LevelledMobs; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class ComponentUtils { + public static void append( + final @NotNull Object component, + final @Nullable Object appendingComponent + ) { + if (appendingComponent == null) return; + final Definitions def = LevelledMobs.getInstance().getDefinitions(); + + try { + if (component.getClass() != def.clazz_IChatMutableComponent) { + throw new Exception("Invalid type: " + component.getClass().getName()); + } + + if (appendingComponent.getClass() != def.clazz_IChatMutableComponent){ + throw new Exception("Invalid type: " + appendingComponent.getClass().getName()); + } + + def.method_ComponentAppend.invoke(component, appendingComponent); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static @NotNull Object getEmptyComponent() { + final Object result = getTextComponent(null); + assert result != null; + return result; + } + + public static @Nullable Object getTextComponent( + final @Nullable String text + ) { + final Definitions def = LevelledMobs.getInstance().getDefinitions(); + try { + if (text == null && def.getServerVersionInfo().getMinecraftVersion() >= 1.19) { + // #empty() + return def.method_EmptyComponent.invoke(null); + } else { + // #nullToEmpty(text) + return def.method_TextComponent.invoke(null, text); + } + } catch (Exception e){ + e.printStackTrace(); + return null; + } + } + + public static @Nullable Object getTranslatableComponent( + final @NotNull String key + ) { + return getTranslatableComponent(key, (Object) null); + } + + public static @Nullable Object getTranslatableComponent( + final @NotNull String key, + final @Nullable Object... args + ) { + final Definitions def = LevelledMobs.getInstance().getDefinitions(); + try { + if (def.getServerVersionInfo().getMinecraftVersion() >= 1.19){ + if (args == null || args.length == 0) { + return def.method_Translatable.invoke(null, key); + } + else { + return def.method_TranslatableWithArgs.invoke(null, key, args); + } + } + else{ + if (args == null || args.length == 0) { + return def.clazz_TranslatableComponent.getConstructor(String.class).newInstance(key); + } + else { + return def.clazz_TranslatableComponent.getConstructor( + String.class, Object[].class).newInstance(key, args); + } + } + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } +} diff --git a/src/main/java/me/lokka30/levelledmobs/nms/Definitions.java b/src/main/java/me/lokka30/levelledmobs/nms/Definitions.java new file mode 100644 index 000000000..c09518167 --- /dev/null +++ b/src/main/java/me/lokka30/levelledmobs/nms/Definitions.java @@ -0,0 +1,379 @@ +package me.lokka30.levelledmobs.nms; + +import net.kyori.adventure.text.Component; +import org.bukkit.entity.LivingEntity; +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Optional; + +public class Definitions { + public Definitions(){ + this.ver = new ServerVersionInfo(); + build(); + } + + private final ServerVersionInfo ver; + private boolean hasKiori; + private boolean isOneNinteenThreeOrNewer; + + // classes: + Class clazz_IChatMutableComponent; + Class clazz_IChatBaseComponent; + Class clazz_TranslatableComponent; + Class clazz_CraftLivingEntity; + Class clazz_CraftEntity; + Class clazz_Entity; + Class clazz_DataWatcher; + Class clazz_DataWatcher_Item; + Class clazz_DataWatcherRegistry; + Class clazz_DataWatcherObject; + Class clazz_DataWatcherSerializer; + Class clazz_ClientboundSetEntityDataPacket; + Class clazz_CraftPlayer; + Class clazz_Packet; + Class clazz_PlayerConnection; + Class clazz_ServerPlayerConnection; + Class clazz_NetworkManager; + Class clazz_EntityPlayer; + Class clazz_PaperAdventure; + Class clazz_EntityTypes; + + // methods: + Method method_ComponentAppend; + Method method_EmptyComponent; + Method method_TextComponent; + Method method_Translatable; + Method method_TranslatableWithArgs; + Method method_getHandle; + Method method_getEntityData; + Method method_set; + Method method_getId; + Method method_PlayergetHandle; + Method method_Send; + Method method_getAll; + Method method_define; + Method method_getAccessor; + Method method_getValue; + Method method_AsVanilla; + Method method_EntityTypeByString; + Method method_GetDescriptionId; + Method method_getNonDefaultValues; + Method method_SynchedEntityData_Define; + Method method_DataWatcher_GetItem; + Method method_DataWatcherItem_Value; + + // fields + Field field_OPTIONAL_COMPONENT; + Field field_BOOLEAN; + Field field_Connection; + Field field_Int2ObjectMap; + + // Constructors + Constructor ctor_EntityDataAccessor; + Constructor ctor_SynchedEntityData; + Constructor ctor_Packet; + + private void build(){ + this.isOneNinteenThreeOrNewer = ver.getMinecraftVersion() == 1.19 && ver.getRevision()>= 3 || + ver.getMinecraftVersion() >= 1.20; + + try { + buildClasses(); + + // build methods + getMethodComponentAppend(); + getMethodTextComponents(); + getMethodTranslatable(); + buildSimpleMethods(); + buildFields(); + buildConstructors(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void buildClasses() throws ClassNotFoundException { + this.clazz_IChatMutableComponent = Class.forName( + "net.minecraft.network.chat.IChatMutableComponent"); + + this.clazz_IChatBaseComponent = Class.forName( + "net.minecraft.network.chat.IChatBaseComponent"); + + this.clazz_CraftEntity = Class.forName( + "org.bukkit.craftbukkit." + ver.getNMSVersion() + ".entity.CraftEntity"); + + this.clazz_CraftLivingEntity = Class.forName( + "org.bukkit.craftbukkit." + ver.getNMSVersion() + ".entity.CraftLivingEntity"); + + // net.minecraft.network.syncher.SynchedEntityData + this.clazz_DataWatcher = Class.forName( + "net.minecraft.network.syncher.DataWatcher"); + + this.clazz_DataWatcher_Item = Class.forName( + "net.minecraft.network.syncher.DataWatcher$Item"); + + this.clazz_DataWatcherRegistry = Class.forName( + "net.minecraft.network.syncher.DataWatcherRegistry"); + + this.clazz_Entity = Class.forName( + "net.minecraft.world.entity.Entity"); + + this.clazz_DataWatcherObject = Class.forName( + "net.minecraft.network.syncher.DataWatcherObject"); + + this.clazz_DataWatcherSerializer = Class.forName( + "net.minecraft.network.syncher.DataWatcherSerializer"); + + this.clazz_ClientboundSetEntityDataPacket = Class.forName( + "net.minecraft.network.protocol.game.PacketPlayOutEntityMetadata"); + + this.clazz_CraftPlayer = Class.forName( + "org.bukkit.craftbukkit." + ver.getNMSVersion() + ".entity.CraftPlayer"); + + this.clazz_Packet = Class.forName( + "net.minecraft.network.protocol.Packet"); + + this.clazz_NetworkManager = Class.forName( + "net.minecraft.network.NetworkManager"); + + //net.minecraft.server.network.ServerPlayerConnection -> + // void send(net.minecraft.network.protocol.Packet) -> + this.clazz_PlayerConnection = Class.forName( + "net.minecraft.server.network.PlayerConnection"); + + this.clazz_ServerPlayerConnection = Class.forName( + "net.minecraft.server.network.ServerPlayerConnection"); + + // net.minecraft.server.level.ServerPlayer -> + this.clazz_EntityPlayer = Class.forName( + "net.minecraft.server.level.EntityPlayer"); + + if (ver.getMinecraftVersion() < 1.19) { + // this is basically TranslatableComponent + this.clazz_TranslatableComponent = Class.forName( + "net.minecraft.network.chat.ChatMessage"); + } + + try { + this.clazz_PaperAdventure = Class.forName( + "io.papermc.paper.adventure.PaperAdventure"); + this.hasKiori = true; + } + catch (ClassNotFoundException ignored){ } + + this.clazz_EntityTypes = Class.forName( + "net.minecraft.world.entity.EntityTypes"); + } + + private void getMethodComponentAppend() throws NoSuchMethodException { + // net.minecraft.network.chat.MutableComponent append(net.minecraft.network.chat.Component) -> + // 1.18 = b, 1.19.0 = a, 1.19.1 = b + String methodName = ver.getRevision() == 0 || ver.getMinecraftVersion() == 1.18 + ? "a" : "b"; + + if (ver.getMinecraftVersion() <= 1.17) + methodName = "addSibling"; + + this.method_ComponentAppend = clazz_IChatMutableComponent.getDeclaredMethod( + methodName, this.clazz_IChatBaseComponent); + } + + private void getMethodTextComponents() throws NoSuchMethodException{ + // net.minecraft.network.chat.Component -> + // net.minecraft.network.chat.MutableComponent empty() + + if (ver.getMinecraftVersion() >= 1.19) { + // 1.19.0 = g, 1.19.1 = h + final String methodName = ver.getRevision() == 0 ? "g" : "h"; + + // net.minecraft.network.chat.Component -> + // net.minecraft.network.chat.MutableComponent empty() + this.method_EmptyComponent = clazz_IChatBaseComponent.getDeclaredMethod(methodName); + } + + // 1.18 doesn't have #empty(), instead use #nullToEmpty() + // net.minecraft.network.chat.Component -> qk: + // net.minecraft.network.chat.Component nullToEmpty(java.lang.String) -> a + this.method_TextComponent = clazz_IChatBaseComponent.getDeclaredMethod("a", String.class); + + } + + private void getMethodTranslatable() throws NoSuchMethodException { + if (ver.getMinecraftVersion() < 1.19) { + // 1.18 instantiates an object, so this method doesn't apply + return; + } + + // net.minecraft.network.chat.Component -> + // net.minecraft.network.chat.MutableComponent translatable(java.lang.String) + // net.minecraft.network.chat.MutableComponent translatable(java.lang.String,java.lang.Object[]) + + this.method_Translatable = clazz_IChatBaseComponent.getDeclaredMethod("a"); + this.method_TranslatableWithArgs = clazz_IChatBaseComponent.getDeclaredMethod("a", String.class, Object[].class); + } + + @SuppressWarnings("deprecation") + public @NotNull String getTranslationKey(final @NotNull LivingEntity livingEntity){ + // only needed for spigot. paper has a built-in method + + // net.minecraft.world.entity.EntityType -> + // 300:300:java.util.Optional byString(java.lang.String) -> a + // public static Optional> byString(String s) + + Optional optionalResult; + try { + optionalResult = (Optional)this.method_EntityTypeByString.invoke(null, livingEntity.getType().getName()); + + if (optionalResult.isEmpty()) { + return ""; + } + + // net.minecraft.world.entity.EntityTypes + return (String) method_GetDescriptionId.invoke(optionalResult.get()); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + + return ""; + } + + private void buildSimpleMethods() throws NoSuchMethodException { + this.method_getHandle = clazz_CraftLivingEntity.getDeclaredMethod("getHandle"); + + // net.minecraft.network.syncher.SynchedEntityData getEntityData() -> + + String methodName = this.isOneNinteenThreeOrNewer ? + "al" : "ai"; + if (ver.getMinecraftVersion() <= 1.17) + methodName = "getDataWatcher"; + + // net.minecraft.network.syncher.SynchedEntityData getEntityData() -> + this.method_getEntityData = clazz_Entity.getMethod(methodName); + + methodName = ver.getMinecraftVersion() >= 1.18 ? + "b" : "set"; + + // set(net.minecraft.network.syncher.EntityDataAccessor,java.lang.Object) -> + this.method_set = clazz_DataWatcher.getMethod(methodName, clazz_DataWatcherObject, Object.class); + + // int getId() -> + methodName = this.isOneNinteenThreeOrNewer ? + "ah" : "ae"; + if (ver.getMinecraftVersion() <= 1.17) + methodName = "getId"; + + this.method_getId = clazz_Entity.getDeclaredMethod(methodName); + + this.method_PlayergetHandle = clazz_CraftPlayer.getDeclaredMethod("getHandle"); + + // net.minecraft.server.network.ServerGamePacketListenerImpl -> + // void send(net.minecraft.network.protocol.Packet) -> + + methodName = ver.getMinecraftVersion() >= 1.18 ? + "a" : "sendPacket"; + this.method_Send = clazz_ServerPlayerConnection.getDeclaredMethod(methodName, clazz_Packet); + + methodName = ver.getMinecraftVersion() >= 1.18 ? + "c" : "getAll"; + // java.util.List getAll() -> + this.method_getAll = clazz_DataWatcher.getDeclaredMethod(methodName); + + methodName = ver.getMinecraftVersion() >= 1.18 ? + "a" : "register"; + + // net.minecraft.network.syncher.SynchedEntityData -> + // define(net.minecraft.network.syncher.EntityDataAccessor,java.lang.Object) -> + this.method_define = clazz_DataWatcher.getDeclaredMethod(methodName, clazz_DataWatcherObject, Object.class); + + // net.minecraft.network.syncher.EntityDataAccessor getAccessor() -> + this.method_getAccessor = clazz_DataWatcher_Item.getDeclaredMethod("a"); + // java.lang.Object getValue() -> + this.method_getValue = clazz_DataWatcher_Item.getDeclaredMethod("b"); + + // net.minecraft.network.Connection getConnection() -> + //this.method_getConnection = clazz_CraftPlayer.getDeclaredMethod("networkManager"); + + if (this.hasKiori) + this.method_AsVanilla = clazz_PaperAdventure.getDeclaredMethod("asVanilla", Component.class); + + // java.util.Optional byString(java.lang.String) -> a + this.method_EntityTypeByString = clazz_EntityTypes.getDeclaredMethod("a", String.class); + + // java.lang.String getDescriptionId() -> g + this.method_GetDescriptionId = clazz_EntityTypes.getDeclaredMethod("g"); + + if (this.getIsOneNinteenThreeOrNewer()){ + // new methods here were added in 1.19.3 + + // java.util.List getNonDefaultValues() -> c + this.method_getNonDefaultValues = clazz_DataWatcher.getDeclaredMethod("c"); + + // define(net.minecraft.network.syncher.EntityDataAccessor,java.lang.Object) -> a + this.method_SynchedEntityData_Define = clazz_DataWatcher.getMethod("a", clazz_DataWatcherObject, Object.class); + + // private DataWatcher.Item getItem(DataWatcherObject datawatcherobject) + // net.minecraft.network.syncher.SynchedEntityData$DataItem getItem(net.minecraft.network.syncher.EntityDataAccessor) -> b + this.method_DataWatcher_GetItem = clazz_DataWatcher.getDeclaredMethod("b", clazz_DataWatcherObject); + this.method_DataWatcher_GetItem.setAccessible(true); + + // net.minecraft.network.syncher.SynchedEntityData$DataItem -> abq$a: + // net.minecraft.network.syncher.SynchedEntityData$DataValue value() -> e + this.method_DataWatcherItem_Value = clazz_DataWatcher_Item.getDeclaredMethod("e"); + } + } + + private void buildFields() throws NoSuchFieldException { + // net.minecraft.network.syncher.EntityDataSerializer OPTIONAL_COMPONENT + this.field_OPTIONAL_COMPONENT = clazz_DataWatcherRegistry.getDeclaredField("f"); + + // net.minecraft.network.syncher.EntityDataSerializer BOOLEAN + this.field_BOOLEAN = clazz_DataWatcherRegistry.getDeclaredField("i"); + + // net.minecraft.server.level.ServerPlayer -> + // net.minecraft.server.network.ServerGamePacketListenerImpl connection -> + this.field_Connection = clazz_EntityPlayer.getDeclaredField("b"); + + if (this.isOneNinteenThreeOrNewer){ + // private final Int2ObjectMap> itemsById + this.field_Int2ObjectMap = clazz_DataWatcher.getDeclaredField("e"); + this.field_Int2ObjectMap.setAccessible(true); + } + } + + private void buildConstructors() throws NoSuchMethodException { + this.ctor_EntityDataAccessor = clazz_DataWatcherObject.getConstructor( + int.class, clazz_DataWatcherSerializer); + + this.ctor_SynchedEntityData = clazz_DataWatcher.getConstructor(clazz_Entity); + + if (this.isOneNinteenThreeOrNewer) { + // starting with 1.19.3 use this one: + // public net.minecraft.network.protocol.game.PacketPlayOutEntityMetadata(int,java.util.List>) + + this.ctor_Packet = clazz_ClientboundSetEntityDataPacket.getConstructor( + int.class, List.class); + } + else{ + // up to 1.19.2 use this one: + this.ctor_Packet = clazz_ClientboundSetEntityDataPacket.getConstructor( + int.class, clazz_DataWatcher, boolean.class); + } + } + + public boolean getHasKiori(){ + return this.hasKiori; + } + + public @NotNull ServerVersionInfo getServerVersionInfo(){ + return this.ver; + } + + public boolean getIsOneNinteenThreeOrNewer(){ + return this.isOneNinteenThreeOrNewer; + } +} \ No newline at end of file diff --git a/src/main/java/me/lokka30/levelledmobs/nms/KyoriNametags.java b/src/main/java/me/lokka30/levelledmobs/nms/KyoriNametags.java index 57793b90a..fe2ad66fb 100644 --- a/src/main/java/me/lokka30/levelledmobs/nms/KyoriNametags.java +++ b/src/main/java/me/lokka30/levelledmobs/nms/KyoriNametags.java @@ -10,7 +10,7 @@ import java.lang.reflect.Method; public class KyoriNametags { - public static @NotNull net.minecraft.network.chat.Component generateComponent( + public static @NotNull Object generateComponent( final @NotNull LivingEntity livingEntity, final @NotNull NametagResult nametagResult){ final String nametag = nametagResult.getNametagNonNull(); @@ -30,13 +30,12 @@ public class KyoriNametags { try{ final Class clazz = Class.forName("io.papermc.paper.adventure.PaperAdventure"); final Method asVanilla = clazz.getDeclaredMethod("asVanilla", Component.class); - final Object mcComponent = asVanilla.invoke(clazz, result); - return (net.minecraft.network.chat.Component) mcComponent; + return asVanilla.invoke(clazz, result); } catch (Exception e){ e.printStackTrace(); } - return net.minecraft.network.chat.Component.empty(); + return ComponentUtils.getEmptyComponent(); } } diff --git a/src/main/java/me/lokka30/levelledmobs/nms/MiscUtils.java b/src/main/java/me/lokka30/levelledmobs/nms/MiscUtils.java index 0257659e6..8273e370c 100644 --- a/src/main/java/me/lokka30/levelledmobs/nms/MiscUtils.java +++ b/src/main/java/me/lokka30/levelledmobs/nms/MiscUtils.java @@ -1,7 +1,6 @@ package me.lokka30.levelledmobs.nms; import me.lokka30.levelledmobs.LevelledMobs; -import net.minecraft.nbt.CompoundTag; import org.bukkit.entity.LivingEntity; import org.jetbrains.annotations.NotNull; @@ -9,61 +8,19 @@ public class MiscUtils { public static @NotNull String getNBTDump(final @NotNull LivingEntity livingEntity, final @NotNull LevelledMobs main){ - final ServerVersionInfo versionInfo = main.nametagQueueManager.nmsHandler.versionInfo; - if (versionInfo.getMinecraftVersion() <= 1.16){ - return getNBTDump_1_16(livingEntity, versionInfo.getNMSVersion()); - } +// final ServerVersionInfo versionInfo = main.nametagQueueManager.nmsHandler.versionInfo; +// if (versionInfo.getMinecraftVersion() <= 1.16){ +// return getNBTDump_1_16(livingEntity, versionInfo.getNMSVersion()); +// } + final Definitions def = LevelledMobs.getInstance().getDefinitions(); try { - final Class clazz_CraftLivingEntity; - - clazz_CraftLivingEntity = Class.forName( - "org.bukkit.craftbukkit." + versionInfo.getNMSVersion() + ".entity.CraftLivingEntity"); - - final Method method_getHandle = clazz_CraftLivingEntity.getDeclaredMethod("getHandle"); - final net.minecraft.world.entity.LivingEntity internalLivingEntity = (net.minecraft.world.entity.LivingEntity) method_getHandle.invoke( - livingEntity); - + //final Method method_getHandle = def.clazz_CraftLivingEntity.getDeclaredMethod("getHandle"); + final Object internalLivingEntity = def.method_getHandle.invoke(livingEntity); final Class compoundTagClazz = Class.forName("net.minecraft.nbt.NBTTagCompound"); final Object compoundTag = compoundTagClazz.getConstructor().newInstance(); - - if (versionInfo.getMinecraftVersion() >= 1.18){ - internalLivingEntity.saveWithoutId((CompoundTag) compoundTag); - } - else { - final Class entityClazz = Class.forName("net.minecraft.world.entity.Entity"); - final Method saveWithoutId = entityClazz.getDeclaredMethod("save", compoundTagClazz); - saveWithoutId.invoke(internalLivingEntity, compoundTag); - } - return compoundTag.toString(); - } catch (Exception e) { - e.printStackTrace(); - } - - return ""; - } - - private static @NotNull String getNBTDump_1_16(final @NotNull LivingEntity livingEntity, final String nmsVersion){ - final String compoundTagName = "net.minecraft.server.v1_16_R3.NBTTagCompound"; - final String methodName = "save"; - - try { - final Class clazz_CraftLivingEntity; - - clazz_CraftLivingEntity = Class.forName( - "org.bukkit.craftbukkit." + nmsVersion + ".entity.CraftLivingEntity"); - final Method method_getHandle = clazz_CraftLivingEntity.getDeclaredMethod("getHandle"); - - // net.minecraft.server.v1_16_R3.EntityLiving - final Object internalLivingEntity = method_getHandle.invoke(livingEntity); - - final Class compoundTagClazz = Class.forName(compoundTagName); - final Object compoundTag = compoundTagClazz.getConstructor().newInstance(); - - final Class clazz_Entity = Class.forName("net.minecraft.server." + nmsVersion + ".Entity"); - final Method saveWithoutId = clazz_Entity.getDeclaredMethod(methodName, compoundTagClazz); + final Method saveWithoutId = def.clazz_Entity.getDeclaredMethod("e", compoundTagClazz); saveWithoutId.invoke(internalLivingEntity, compoundTag); - return compoundTag.toString(); } catch (Exception e) { e.printStackTrace(); @@ -71,4 +28,34 @@ public class MiscUtils { return ""; } + +// private static @NotNull String getNBTDump_1_16(final @NotNull LivingEntity livingEntity, final String nmsVersion){ +// final String compoundTagName = "net.minecraft.server.v1_16_R3.NBTTagCompound"; +// final String methodName = "save"; +// final Definitions def = LevelledMobs.getInstance().getDefinitions(); +// +// try { +// final Class clazz_CraftLivingEntity; +// +// clazz_CraftLivingEntity = Class.forName( +// "org.bukkit.craftbukkit." + nmsVersion + ".entity.CraftLivingEntity"); +// final Method method_getHandle = clazz_CraftLivingEntity.getDeclaredMethod("getHandle"); +// +// // net.minecraft.server.v1_16_R3.EntityLiving +// final Object internalLivingEntity = method_getHandle.invoke(livingEntity); +// +// final Class compoundTagClazz = Class.forName(compoundTagName); +// final Object compoundTag = compoundTagClazz.getConstructor().newInstance(); +// +// //final Class clazz_Entity = Class.forName("net.minecraft.server." + nmsVersion + ".Entity"); +// final Method saveWithoutId = def.clazz_Entity.getDeclaredMethod(methodName, compoundTagClazz); +// saveWithoutId.invoke(internalLivingEntity, compoundTag); +// +// return compoundTag.toString(); +// } catch (Exception e) { +// e.printStackTrace(); +// } +// +// return ""; +// } } diff --git a/src/main/java/me/lokka30/levelledmobs/nms/NMSHandler.java b/src/main/java/me/lokka30/levelledmobs/nms/NMSHandler.java index e1a35b057..87ebe1f34 100644 --- a/src/main/java/me/lokka30/levelledmobs/nms/NMSHandler.java +++ b/src/main/java/me/lokka30/levelledmobs/nms/NMSHandler.java @@ -35,11 +35,13 @@ public NMSHandler(final @NotNull LevelledMobs main) { // supported is paper >= 1.18 or spigot >= 1.19 // otherwise protocollib is used - if (hasPaper && versionInfo.getMinecraftVersion() >= 1.18 || - !hasPaper && versionInfo.getMinecraftVersion() >= 1.19) { + //if (hasPaper && versionInfo.getMinecraftVersion() >= 1.18 || + // !hasPaper && versionInfo.getMinecraftVersion() >= 1.19) { + + if (versionInfo.getMinecraftVersion() >= 1.17) { // 1.18 and newer we support with direct nms (Paper) // or 1.19 spigot and newer - this.currentUtil = new NametagSender(versionInfo, hasPaper); + this.currentUtil = new NametagSender(); Utils.logger.info( String.format("Using NMS version %s for nametag support", versionInfo.getNMSVersion())); } else if (ExternalCompatibilityManager.hasProtocolLibInstalled()) { diff --git a/src/main/java/me/lokka30/levelledmobs/nms/NametagSender.java b/src/main/java/me/lokka30/levelledmobs/nms/NametagSender.java index 5edf23778..abc34a71c 100644 --- a/src/main/java/me/lokka30/levelledmobs/nms/NametagSender.java +++ b/src/main/java/me/lokka30/levelledmobs/nms/NametagSender.java @@ -1,18 +1,15 @@ package me.lokka30.levelledmobs.nms; import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; import java.util.Optional; +import me.lokka30.levelledmobs.LevelledMobs; import me.lokka30.levelledmobs.result.NametagResult; import me.lokka30.microlib.messaging.MessageUtils; -import net.minecraft.network.chat.Component; -import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket; -import net.minecraft.network.syncher.EntityDataAccessor; -import net.minecraft.network.syncher.EntityDataSerializers; -import net.minecraft.network.syncher.SynchedEntityData; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.entity.Entity; +import org.bukkit.Bukkit; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; @@ -24,123 +21,175 @@ * @author stumper66 * @since 3.6.0 */ +@SuppressWarnings("unchecked") public class NametagSender implements NMSUtil { - public NametagSender(final @NotNull ServerVersionInfo versionInfo, final boolean hasKiori) { - this.versionInfo = versionInfo; - this.nmsVersion = versionInfo.getNMSVersion(); - this.hasKiori = hasKiori; - buildReflection(); + public NametagSender() { + this.def = LevelledMobs.getInstance().getDefinitions(); } - private final ServerVersionInfo versionInfo; - private final String nmsVersion; - private final boolean hasKiori; - private Method resolveStringMethod; - private Method emptyComponentMethod; - private Method appendComponentMethod; - private Method nullToEmptyMethod; - private Class clazz_CraftChatMessage; - private Class clazz_TranslatableComponent; + private final Definitions def; - private void buildReflection(){ - String methodName; + public void sendNametag(final @NotNull LivingEntity livingEntity, final @NotNull NametagResult nametag, + final @NotNull Player player, final boolean doAlwaysVisible) { + + if (!player.isOnline() || !player.isValid()) return; + + final Runnable runnable = () -> sendNametagNonAsync(livingEntity, nametag, player, doAlwaysVisible); + + Bukkit.getScheduler().runTask(LevelledMobs.getInstance(), runnable); + } + + private void sendNametagNonAsync(final @NotNull LivingEntity livingEntity, final @NotNull NametagResult nametag, + final @NotNull Player player, final boolean doAlwaysVisible) { try { - // we're only here if we have: - // Paper 1.18.0 + - // Spigot 1.19.0 + + // livingEntity.getHandle() + final Object internalLivingEntity = def.method_getHandle.invoke(livingEntity); + // internalLivingEntity.getEntityData() + final Object entityDataPreClone = def.method_getEntityData.invoke(internalLivingEntity); + final Object entityData = cloneEntityData(entityDataPreClone, internalLivingEntity); + + if (entityData == null){ + return; + } + + //final Object entityData = entityDataPreClone; + final Object optionalComponent = def.field_OPTIONAL_COMPONENT.get(def.clazz_DataWatcherRegistry); - this.clazz_CraftChatMessage = Class.forName( - "org.bukkit.craftbukkit." + nmsVersion + ".util.CraftChatMessage"); + // final EntityDataAccessor> customNameAccessor = + // //new EntityDataAccessor<>(2, EntityDataSerializers.OPTIONAL_COMPONENT); + final Object customNameAccessor = def.ctor_EntityDataAccessor.newInstance(2, optionalComponent); + final Optional customName = buildNametagComponent(livingEntity, nametag); - this.resolveStringMethod = clazz_CraftChatMessage.getDeclaredMethod("fromString", - String.class); + //final Optional customName = entityData.set(customNameAccessor, customName); + def.method_set.invoke(entityData, customNameAccessor, customName); - final Class clazz_IChatMutableComponent = Class.forName( - "net.minecraft.network.chat.IChatMutableComponent"); + final Object BOOLEAN = def.field_BOOLEAN.get(def.clazz_DataWatcherRegistry); + final Object customNameVisibleAccessor = def.ctor_EntityDataAccessor.newInstance(3, BOOLEAN); - final Class clazz_IChatBaseComponent = Class.forName( - "net.minecraft.network.chat.IChatBaseComponent"); + // entityData.set(customNameVisibleAccessor, !nametag.isNullOrEmpty() && doAlwaysVisible); + def.method_set.invoke(entityData, customNameVisibleAccessor, doAlwaysVisible); - if (versionInfo.getMinecraftVersion() < 1.19) { - // this is basically TranslatableComponent - this.clazz_TranslatableComponent = Class.forName( - "net.minecraft.network.chat.ChatMessage"); + final int livingEntityId = (int)def.method_getId.invoke(internalLivingEntity); + + Object packet; + if (def.getIsOneNinteenThreeOrNewer()){ + // List> + // java.util.List getAllNonDefaultValues() -> c + final List getAllNonDefaultValues = getNametagFields(entityData); + packet = def.ctor_Packet + .newInstance(livingEntityId, getAllNonDefaultValues); + } + else{ + packet = def.ctor_Packet + .newInstance(livingEntityId, entityData, true); } - // 1.19.0 = a, 1.19.1 = b - methodName = versionInfo.getRevision() == 0 || versionInfo.getMinecraftVersion() == 1.18 - ? "a" : "b"; + final Object serverPlayer = def.method_PlayergetHandle.invoke(player); + final Object connection = def.field_Connection.get(serverPlayer); + + // serverPlayer.connection.send(packet); + def.method_Send.invoke(connection, packet); + } catch (IllegalAccessException | InvocationTargetException | InstantiationException e) { + e.printStackTrace(); + } + } + + // returns SynchedEntityData (DataWatcher) + // args: SynchedEntityData, LivingEntity (nms) + private @Nullable Object cloneEntityData( + final @NotNull Object entityDataPreClone, + final @NotNull Object internalLivingEntity + ) throws InvocationTargetException, InstantiationException, IllegalAccessException { - // net.minecraft.network.chat.MutableComponent append(net.minecraft.network.chat.Component) -> - this.appendComponentMethod = clazz_IChatMutableComponent.getDeclaredMethod(methodName, clazz_IChatBaseComponent); + if (!def.getIsOneNinteenThreeOrNewer()) { + return cloneEntityDataLegacy(entityDataPreClone, internalLivingEntity); + } - if (versionInfo.getMinecraftVersion() >= 1.19) { - // 1.19.0 = g, 1.19.1 = h - methodName = versionInfo.getRevision() == 0 ? "g" : "h"; + // constructor: + // public net.minecraft.network.syncher.DataWatcher(net.minecraft.world.entity.Entity) + final Object entityData = def.ctor_SynchedEntityData.newInstance(internalLivingEntity); - // net.minecraft.network.chat.Component -> - // net.minecraft.network.chat.MutableComponent empty() - this.emptyComponentMethod = clazz_IChatBaseComponent.getDeclaredMethod(methodName); + try{ + final Map itemsById = (Map) + def.field_Int2ObjectMap.get(entityDataPreClone); + if (itemsById.isEmpty()) { + return null; } - else if (versionInfo.getMinecraftVersion() == 1.18){ - // 1.18 doesn't have #empty(), instead use #nullToEmpty() - // net.minecraft.network.chat.Component -> qk: - // net.minecraft.network.chat.Component nullToEmpty(java.lang.String) -> a - this.nullToEmptyMethod = clazz_IChatBaseComponent.getDeclaredMethod("a", String.class); + + for (final Object objDataItem : itemsById.values()){ + final Object accessor = def.method_getAccessor.invoke(objDataItem); + final Object value = def.method_getValue.invoke(objDataItem); + def.method_define.invoke(entityData, accessor, value); } - } catch (ClassNotFoundException | NoSuchMethodException e) { + return entityData; + } + catch (Exception e){ e.printStackTrace(); } + + return entityData; } - public void sendNametag(final @NotNull LivingEntity livingEntity, @NotNull NametagResult nametag, - @NotNull Player player, final boolean doAlwaysVisible) { - // org.bukkit.craftbukkit.v1_18_R1.entity.CraftLivingEntity - if (!player.isOnline() || !player.isValid()) return; + private @NotNull Object cloneEntityDataLegacy( + final @NotNull Object entityDataPreClone, + final @NotNull Object internalLivingEntity + ) throws InvocationTargetException, InstantiationException, IllegalAccessException { - try { - final Class clazz_CraftLivingEntity = Class.forName( - "org.bukkit.craftbukkit." + nmsVersion + ".entity.CraftLivingEntity"); - final Method method_getHandle = clazz_CraftLivingEntity.getDeclaredMethod("getHandle"); - final net.minecraft.world.entity.LivingEntity internalLivingEntity = (net.minecraft.world.entity.LivingEntity) method_getHandle.invoke( - livingEntity); - - final SynchedEntityData entityData = cloneEntityData( - internalLivingEntity.getEntityData(), internalLivingEntity); - final EntityDataAccessor> customNameAccessor = - new EntityDataAccessor<>(2, EntityDataSerializers.OPTIONAL_COMPONENT); - - final Optional customName = buildNametagComponent(livingEntity, nametag); - entityData.set(customNameAccessor, customName); - - final EntityDataAccessor customNameVisibleAccessor = - new EntityDataAccessor<>(3, EntityDataSerializers.BOOLEAN); - - entityData.set(customNameVisibleAccessor, !nametag.isNullOrEmpty() && doAlwaysVisible); - - final ClientboundSetEntityDataPacket packet = new ClientboundSetEntityDataPacket( - internalLivingEntity.getId(), entityData, true - ); - - final Class clazz_CraftPlayer = Class.forName( - "org.bukkit.craftbukkit." + nmsVersion + ".entity.CraftPlayer"); - final Method method_PlayergetHandle = clazz_CraftPlayer.getDeclaredMethod("getHandle"); - final ServerPlayer serverPlayer = (ServerPlayer) method_PlayergetHandle.invoke(player); - serverPlayer.connection.send(packet); - } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException | - ClassNotFoundException e) { + final Object entityData = def.ctor_SynchedEntityData.newInstance(internalLivingEntity); + if (def.method_getAll.invoke(entityDataPreClone) == null){ + return entityData; + } + + // SynchedEntityData.DataItem + // List> getAll() + for (final Object dataItem : (List)def.method_getAll.invoke(entityDataPreClone)){ + // entityData.define(dataItem.getAccessor(), dataItem.getValue()); + final Object accessor = def.method_getAccessor.invoke(dataItem); + final Object value = def.method_getValue.invoke(dataItem); + def.method_define.invoke(entityData, accessor, value); + } + + return entityData; + } + + private @NotNull List getNametagFields(final @NotNull Object entityData){ + final List results = new LinkedList<>(); + try{ + final Map itemsById = (Map) + def.field_Int2ObjectMap.get(entityData); + if (itemsById.isEmpty()) { + return results; + } + + for (final int objDataId : itemsById.keySet()){ + if (objDataId < 2 || objDataId > 3) continue; + final Object objDataItem = itemsById.get(objDataId); + final Object accessor = def.method_getAccessor.invoke(objDataItem); + + // DataWatcher.Item + final Object dataWatcherItem = def.method_DataWatcher_GetItem.invoke(entityData, accessor); + results.add(def.method_DataWatcherItem_Value.invoke(dataWatcherItem)); + //results.add(objDataItem); + } + } + catch (Exception e){ e.printStackTrace(); } + + return results; } - private @NotNull Optional buildNametagComponent(final @NotNull LivingEntity livingEntity, - final @NotNull NametagResult nametag){ - if (nametag.isNullOrEmpty()) - return Optional.empty(); + private Optional buildNametagComponent( + final @NotNull LivingEntity livingEntity, + final @NotNull NametagResult nametag + ) { + if (nametag.isNullOrEmpty()) { + return Optional.of(ComponentUtils.getEmptyComponent()); + } - if (hasKiori){ + if (def.getHasKiori()){ // paper servers go here: return Optional.of(KyoriNametags.generateComponent(livingEntity, nametag)); } @@ -152,17 +201,9 @@ public void sendNametag(final @NotNull LivingEntity livingEntity, @NotNull Namet final int displayNameIndex = mobName.indexOf(displayName); if (displayNameIndex < 0) { - Component comp = null; - try { - comp = ((Component[]) this.resolveStringMethod.invoke( - this.clazz_CraftChatMessage, resolveText(nametag.getNametagNonNull())))[0]; - } catch (IllegalAccessException | InvocationTargetException e) { - e.printStackTrace(); - } - if (comp == null) - return Optional.empty(); - else - return Optional.of(comp); + final Object comp = ComponentUtils.getTextComponent(nametag.getNametagNonNull()); + return comp == null ? + Optional.empty() : Optional.of(comp); } final String leftText = displayNameIndex > 0 ? @@ -172,62 +213,27 @@ public void sendNametag(final @NotNull LivingEntity livingEntity, @NotNull Namet resolveText(mobName.substring(displayNameIndex + displayName.length())) : null; - final Component mobNameComponent = nametag.overriddenName == null ? - getTranslatableComponent(getTranslationKey(livingEntity.getType())) : - Component.nullToEmpty(nametag.overriddenName); - Object comp = null; - try { - // MutableComponent comp = Component.empty(); - comp = getEmptyComponent(); - - if (leftText != null) { - // comp.append(Component); - appendComponentMethod.invoke(comp, ((Component[]) this.resolveStringMethod.invoke(this.clazz_CraftChatMessage, leftText))[0]); - } - - appendComponentMethod.invoke(comp, mobNameComponent); - if (rightText != null) { - // comp.append(Component); - appendComponentMethod.invoke(comp, ((Component[]) this.resolveStringMethod.invoke(this.clazz_CraftChatMessage, rightText))[0]); - } - - } catch (InvocationTargetException | IllegalAccessException e) { - e.printStackTrace(); - } + final Object mobNameComponent = nametag.overriddenName == null ? + ComponentUtils.getTranslatableComponent(def.getTranslationKey(livingEntity)) : + ComponentUtils.getTextComponent(nametag.overriddenName); - return comp == null ? - Optional.empty() : Optional.of((Component) comp); - } + final Object comp = ComponentUtils.getEmptyComponent(); + // MutableComponent comp = Component.empty(); - private @Nullable Object getEmptyComponent() throws InvocationTargetException, IllegalAccessException { - if (versionInfo.getMinecraftVersion() >= 1.19) { - return this.emptyComponentMethod.invoke(null); + if (leftText != null) { + // comp.append(Component); + ComponentUtils.append(comp, ComponentUtils.getTextComponent(leftText)); } - else { - return this.nullToEmptyMethod.invoke(null, (Object) null); - } - } - private @Nullable Component getTranslatableComponent(final @NotNull String key){ - if (versionInfo.getMinecraftVersion() >= 1.19){ - return Component.translatable(key); - } - else { - Component result = null; - try { - result = (Component) this.clazz_TranslatableComponent.getConstructor(String.class).newInstance(key); - } catch (Exception e) { - e.printStackTrace(); - } + ComponentUtils.append(comp, mobNameComponent); - return result; + if (rightText != null) { + // comp.append(Component); + ComponentUtils.append(comp, ComponentUtils.getTextComponent(rightText)); } - } - @SuppressWarnings("deprecation") - private String getTranslationKey(final org.bukkit.entity.@NotNull EntityType type) { - return net.minecraft.world.entity.EntityType.byString(type.getName()).map(net.minecraft.world.entity.EntityType::getDescriptionId).orElse(null); + return Optional.of(comp); } private @Nullable String resolveText(final @Nullable String text){ @@ -242,23 +248,6 @@ private String getTranslationKey(final org.bukkit.entity.@NotNull EntityType typ return result; } - @NotNull private static SynchedEntityData cloneEntityData(@NotNull final SynchedEntityData other, - final Entity nmsEntity) { - final SynchedEntityData entityData = new SynchedEntityData(nmsEntity); - if (other.getAll() == null) { - return entityData; - } - - //noinspection rawtypes - for (SynchedEntityData.DataItem dataItem : other.getAll()) - { - //noinspection unchecked - entityData.define(dataItem.getAccessor(), dataItem.getValue()); - } - - return entityData; - } - public String toString() { return "Nametags_NMS"; } diff --git a/src/main/java/me/lokka30/levelledmobs/rules/FineTuningAttributes.java b/src/main/java/me/lokka30/levelledmobs/rules/FineTuningAttributes.java index d9991a05b..46328ccb5 100644 --- a/src/main/java/me/lokka30/levelledmobs/rules/FineTuningAttributes.java +++ b/src/main/java/me/lokka30/levelledmobs/rules/FineTuningAttributes.java @@ -4,10 +4,12 @@ package me.lokka30.levelledmobs.rules; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.LinkedList; -import java.util.List; +import java.util.LinkedHashMap; +import java.util.Map; + +import me.lokka30.levelledmobs.misc.Addition; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** @@ -17,36 +19,16 @@ * @since 3.0.0 */ public class FineTuningAttributes implements Cloneable { + public FineTuningAttributes(){ + this.multipliers = new LinkedHashMap<>(); + } - public Double attackDamage; - public Double creeperExplosionRadius; - public Double maxHealth; - public Double movementSpeed; - public Double rangedAttackDamage; - public Double itemDrop; - public Double armorBonus; - public Double armorToughness; - public Double attackKnockback; - public Double flyingSpeed; - public Double knockbackResistance; - public Double horseJumpStrength; - public Double zombieReinforcements; - public Double followRange; - public Double xpDrop; + private final Map multipliers; public boolean doNotMerge; + public Boolean useStacked; public boolean isEmpty(){ - try { - for (Field f : this.getClass().getDeclaredFields()){ - if (f.get(this) instanceof Double && f.get(this) != null){ - return false; - } - } - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - - return true; + return this.multipliers.isEmpty(); } void mergeAttributes(final @Nullable FineTuningAttributes attributes) { @@ -54,82 +36,71 @@ void mergeAttributes(final @Nullable FineTuningAttributes attributes) { return; } - try { - for (final Field f : attributes.getClass().getDeclaredFields()) { - if (!Modifier.isPublic(f.getModifiers())) { - continue; - } - if (f.get(attributes) == null) { - continue; - } - final Object presetValue = f.get(attributes); - - this.getClass().getDeclaredField(f.getName()).set(this, presetValue); - } - } catch (final IllegalAccessException | NoSuchFieldException e) { - e.printStackTrace(); - } + this.multipliers.putAll(attributes.multipliers); } - public String toString() { - final StringBuilder sb = new StringBuilder(); - final List list = new LinkedList<>(); - if (maxHealth != null) { - list.add("maxHlth: " + maxHealth); - } - if (attackDamage != null) { - list.add("attkDmg: " + attackDamage); - } - if (itemDrop != null) { - list.add("itemDrp: " + itemDrop); - } - if (xpDrop != null) { - list.add("xpDrp: " + xpDrop); - } - if (movementSpeed != null) { - list.add("moveSpd: " + movementSpeed); - } - if (rangedAttackDamage != null) { - list.add("rangdAtkDmg: " + rangedAttackDamage); - } - if (creeperExplosionRadius != null) { - list.add("creeperDmg: " + creeperExplosionRadius); - } - if (armorBonus != null) { - list.add("armrBns: " + armorBonus); - } - if (armorToughness != null) { - list.add("armrTuf: " + armorToughness); - } - if (attackKnockback != null) { - list.add("attkKnbk: " + attackKnockback); - } - if (flyingSpeed != null) { - list.add("flySpd: " + flyingSpeed); - } - if (knockbackResistance != null) { - list.add("knbkRst: " + knockbackResistance); - } - if (horseJumpStrength != null) { - list.add("horseJump: " + horseJumpStrength); - } - if (zombieReinforcements != null) { - list.add("zmbRnfrce: " + zombieReinforcements); - } - if (followRange != null) { - list.add("flwRng: " + followRange); + public void addItem(final Addition addition, final Multiplier multiplier){ + this.multipliers.put(addition, multiplier); + } + + public @Nullable Multiplier getItem(final @NotNull Addition addition){ + return this.multipliers.get(addition); + } + + static @NotNull String getShortName(final @NotNull Addition addition){ + switch (addition){ + case ATTRIBUTE_ATTACK_DAMAGE -> { return "attkDmg"; } + case CREEPER_BLAST_DAMAGE -> { return "creeperDmg"; } + case ATTRIBUTE_MAX_HEALTH -> { return "maxHlth"; } + case ATTRIBUTE_MOVEMENT_SPEED -> { return "moveSpd"; } + case CUSTOM_RANGED_ATTACK_DAMAGE -> { return "rangdAtkDmg"; } + case CUSTOM_ITEM_DROP -> { return "itemDrp"; } + case ATTRIBUTE_ARMOR_BONUS -> { return "armrBns"; } + case ATTRIBUTE_ARMOR_TOUGHNESS -> { return "armrTuf"; } + case ATTRIBUTE_ATTACK_KNOCKBACK -> { return "attkKnbk"; } + case ATTRIBUTE_FLYING_SPEED -> { return "flySpd"; } + case ATTRIBUTE_KNOCKBACK_RESISTANCE -> { return "knbkRst"; } + case ATTRIBUTE_HORSE_JUMP_STRENGTH -> { return "horseJump"; } + case ATTRIBUTE_ZOMBIE_SPAWN_REINFORCEMENTS -> { return "zmbRnfrce"; } + case ATTRIBUTE_FOLLOW_RANGE -> { return "flwRng"; } + case CUSTOM_XP_DROP -> { return "xpDrp"; } } - for (final String item : list) { - if (sb.length() > 0) { - sb.append(", "); - } - sb.append(item); + throw new UnsupportedOperationException("No short name was added for " + addition); + } + + public @NotNull Addition getAdditionFromLMMultiplier(final @NotNull LMMultiplier lmMultiplier){ + switch (lmMultiplier){ + case ATTACK_DAMAGE -> { return Addition.ATTRIBUTE_ATTACK_DAMAGE; } + case CREEPER_BLAST_DAMAGE -> { return Addition.CREEPER_BLAST_DAMAGE; } + case MAX_HEALTH -> { return Addition.ATTRIBUTE_MAX_HEALTH; } + case MOVEMENT_SPEED -> { return Addition.ATTRIBUTE_MOVEMENT_SPEED; } + case RANGED_ATTACK_DAMAGE -> { return Addition.CUSTOM_RANGED_ATTACK_DAMAGE; } + case ITEM_DROP -> { return Addition.CUSTOM_ITEM_DROP; } + case ARMOR_BONUS -> { return Addition.ATTRIBUTE_ARMOR_BONUS; } + case ARMOR_TOUGHNESS -> { return Addition.ATTRIBUTE_ARMOR_TOUGHNESS; } + case ATTACK_KNOCKBACK -> { return Addition.ATTRIBUTE_ATTACK_KNOCKBACK; } + case FLYING_SPEED -> { return Addition.ATTRIBUTE_FLYING_SPEED; } + case KNOCKBACK_RESISTANCE -> { return Addition.ATTRIBUTE_KNOCKBACK_RESISTANCE; } + case HORSE_JUMP_STRENGTH -> { return Addition.ATTRIBUTE_HORSE_JUMP_STRENGTH; } + case ZOMBIE_SPAWN_REINFORCEMENTS -> { return Addition.ATTRIBUTE_ZOMBIE_SPAWN_REINFORCEMENTS; } + case FOLLOW_RANGE -> { return Addition.ATTRIBUTE_FOLLOW_RANGE; } + case XP_DROP -> { return Addition.CUSTOM_XP_DROP; } } - if (sb.length() == 0) { - return "No items"; - } else { + throw new UnsupportedOperationException("No addition mapping for: " + lmMultiplier); + } + + public record Multiplier(Addition addition, boolean useStacked, float value){ + + @Contract(pure = true) + public @NotNull String toString(){ + final StringBuilder sb = new StringBuilder(); + sb.append(FineTuningAttributes.getShortName(addition())); + if (useStacked()) sb.append(" (stkd): "); + else sb.append(": "); + sb.append(value()); + return sb.toString(); } } @@ -144,4 +115,29 @@ public FineTuningAttributes cloneItem() { return copy; } + + public String toString() { + if (this.isEmpty()) + return "No items"; + + final StringBuilder sb = new StringBuilder(); + + if (this.useStacked != null && this.useStacked) + sb.append("(all stk)"); + + for (final Multiplier item : this.multipliers.values()) { + if (sb.length() > 0) { + sb.append(", "); + } + sb.append(getShortName(item.addition())); + sb.append(": "); + sb.append(item.value); + if (item.useStacked()){ + sb.append(" ("); + sb.append("stk)"); + } + } + + return sb.toString(); + } } diff --git a/src/main/java/me/lokka30/levelledmobs/rules/LMMultiplier.java b/src/main/java/me/lokka30/levelledmobs/rules/LMMultiplier.java new file mode 100644 index 000000000..4755e6c43 --- /dev/null +++ b/src/main/java/me/lokka30/levelledmobs/rules/LMMultiplier.java @@ -0,0 +1,19 @@ +package me.lokka30.levelledmobs.rules; + +public enum LMMultiplier { + ATTACK_DAMAGE, + CREEPER_BLAST_DAMAGE, + MAX_HEALTH, + MOVEMENT_SPEED, + RANGED_ATTACK_DAMAGE, + ITEM_DROP, + ARMOR_BONUS, + ARMOR_TOUGHNESS, + ATTACK_KNOCKBACK, + FLYING_SPEED, + KNOCKBACK_RESISTANCE, + HORSE_JUMP_STRENGTH, + ZOMBIE_SPAWN_REINFORCEMENTS, + FOLLOW_RANGE, + XP_DROP +} diff --git a/src/main/java/me/lokka30/levelledmobs/rules/RulesParsingManager.java b/src/main/java/me/lokka30/levelledmobs/rules/RulesParsingManager.java index e7c26de78..1f861caaa 100644 --- a/src/main/java/me/lokka30/levelledmobs/rules/RulesParsingManager.java +++ b/src/main/java/me/lokka30/levelledmobs/rules/RulesParsingManager.java @@ -20,6 +20,7 @@ import java.util.regex.Pattern; import me.lokka30.levelledmobs.LevelledMobs; import me.lokka30.levelledmobs.managers.ExternalCompatibilityManager; +import me.lokka30.levelledmobs.misc.Addition; import me.lokka30.levelledmobs.misc.CachedModalList; import me.lokka30.levelledmobs.misc.CustomUniversalGroups; import me.lokka30.levelledmobs.misc.YmlParsingHelper; @@ -1382,29 +1383,32 @@ private void parseFineTuning(final @Nullable ConfigurationSection cs) { final FineTuningAttributes attribs = parsingInfo.allMobMultipliers != null && doMerge ? parsingInfo.allMobMultipliers : new FineTuningAttributes(); - attribs.maxHealth = ymlHelper.getDouble2(cs, "max-health", attribs.maxHealth); - attribs.movementSpeed = ymlHelper.getDouble2(cs, "movement-speed", attribs.movementSpeed); - attribs.attackDamage = ymlHelper.getDouble2(cs, "attack-damage", attribs.attackDamage); - attribs.rangedAttackDamage = ymlHelper.getDouble2(cs, "ranged-attack-damage", - attribs.rangedAttackDamage); - attribs.itemDrop = ymlHelper.getDouble2(cs, "item-drop", attribs.itemDrop); - attribs.xpDrop = ymlHelper.getDouble2(cs, "xp-drop", attribs.xpDrop); - attribs.creeperExplosionRadius = ymlHelper.getDouble2(cs, "creeper-blast-damage", - attribs.creeperExplosionRadius); - attribs.armorBonus = ymlHelper.getDouble2(cs, "armor-bonus", attribs.armorBonus); - attribs.armorToughness = ymlHelper.getDouble2(cs, "armor-toughness", - attribs.armorToughness); - attribs.attackKnockback = ymlHelper.getDouble2(cs, "attack-knockback", - attribs.attackKnockback); - attribs.flyingSpeed = ymlHelper.getDouble2(cs, "flying-speed", attribs.flyingSpeed); - attribs.knockbackResistance = ymlHelper.getDouble2(cs, "knockback-resistance", - attribs.knockbackResistance); - attribs.horseJumpStrength = ymlHelper.getDouble2(cs, "horse-jump-strength", - attribs.horseJumpStrength); - attribs.zombieReinforcements = ymlHelper.getDouble2(cs, "zombie-spawn-reinforcements", - attribs.zombieReinforcements); - attribs.followRange = ymlHelper.getDouble2(cs, "follow-range", attribs.followRange); - attribs.doNotMerge = ymlHelper.getBoolean(cs, "do-not-merge", false); + for (final String item : cs.getKeys(false)){ + switch (item.toLowerCase()) { + case "use-stacked" -> + attribs.useStacked = ymlHelper.getBoolean2(cs, item, attribs.useStacked); + case "do-not-merge" -> + attribs.doNotMerge = ymlHelper.getBoolean(cs, item, false); + case "vanilla-bonus", "custom-mob-level" -> { } + default -> { + LMMultiplier lmMultiplier; + try{ + lmMultiplier = LMMultiplier.valueOf(item.replace("-", "_").toUpperCase()); + } + catch (Exception ignored){ + Utils.logger.warning("Invalid multiplier: " + item); + continue; + } + + final Addition addition = attribs.getAdditionFromLMMultiplier(lmMultiplier); + FineTuningAttributes.Multiplier multiplier = parseFineTuningValues2( + cs, addition, item); + if (multiplier != null) { + attribs.addItem(addition, multiplier); + } + } + } + } if (attribs.isEmpty()) { return defaults; @@ -1413,6 +1417,54 @@ private void parseFineTuning(final @Nullable ConfigurationSection cs) { return attribs; } + private @Nullable FineTuningAttributes.Multiplier parseFineTuningValues2( + final @NotNull ConfigurationSection cs, + final @NotNull Addition addition, + final @NotNull String item){ + + final List values = cs.getList(item); + if (values == null){ + final Float value = ymlHelper.getFloat2(cs, item, null); + return value != null ? + new FineTuningAttributes.Multiplier(addition, false, value) : + null; + } + + float value = Float.MIN_VALUE; + boolean useStacked = false; + int count = 0; + for (final Object obj : values){ + if (count > 2) break; + + if (obj instanceof Float flt){ + value = flt; + } + else if (obj instanceof Double dbl){ + value = dbl.floatValue(); + } + else if (obj instanceof Integer integer){ + value = integer; + } + else if (obj instanceof String str){ + if ("stacked".equalsIgnoreCase(str)){ + useStacked = true; + } + else if (Utils.isDouble(str)){ + value = Float.parseFloat(str); + } + } + + count++; + } + + if (value > Float.MIN_VALUE){ + return new FineTuningAttributes.Multiplier( + addition, useStacked, value); + } + + return null; + } + private void autoGenerateWeightedRandom(){ RandomLevellingStrategy rls = null; int minLevel = 1; diff --git a/src/main/resources/rules.yml b/src/main/resources/rules.yml index e514c2c9c..c5f2bbf75 100644 --- a/src/main/resources/rules.yml +++ b/src/main/resources/rules.yml @@ -188,6 +188,14 @@ presets: # Special Multipliers (0.0 Min - 1.0 Max) armor-bonus: 0.2 armor-toughness: 0.15 + ## optional: use the stacker multiplier instead + # max-health: [ '4.25', 'STACKED' ] + # movement-speed: [ '0.002', 'STACKED' ] + # attack-damage: [ '0.25', 'STACKED' ] + # ranged-attack-damage: [ '0.25', 'STACKED' ] + # creeper-blast-damage: [ '0.03', 'STACKED' ] + # item-drop: [ '0.25', 'STACKED' ] + # xp-drop: [ '17.5', 'STACKED' ] tiered-coloring: 1-5: 'E76B' #Green