diff --git a/.classpath b/.classpath index cfda90a..f48ebbe 100644 --- a/.classpath +++ b/.classpath @@ -3,13 +3,7 @@ - - - - - - diff --git a/plugin.yml b/plugin.yml index d33fd55..e9e2062 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,6 +1,6 @@ name: SkStuff author: TheBukor description: A Skript addon which adds extra functionalities such as NBT and extended WorldEdit support. -version: 1.4.2.1 +version: 1.5 main: me.TheBukor.SkStuff.SkStuff softdepend: [Skript, WorldEdit] \ No newline at end of file diff --git a/src/me/TheBukor/SkStuff/SkStuff.java b/src/me/TheBukor/SkStuff/SkStuff.java index a245baf..b05d7f1 100644 --- a/src/me/TheBukor/SkStuff/SkStuff.java +++ b/src/me/TheBukor/SkStuff/SkStuff.java @@ -26,9 +26,11 @@ import ch.njol.skript.registrations.EventValues; import ch.njol.skript.util.Getter; import ch.njol.util.coll.CollectionUtils; import me.TheBukor.SkStuff.conditions.CondSelectionContains; +import me.TheBukor.SkStuff.effects.EffClearPathGoals; import me.TheBukor.SkStuff.effects.EffDrainLiquid; import me.TheBukor.SkStuff.effects.EffDrawLineWE; import me.TheBukor.SkStuff.effects.EffMakeCylinder; +import me.TheBukor.SkStuff.effects.EffMakeJump; import me.TheBukor.SkStuff.effects.EffMakePyramid; import me.TheBukor.SkStuff.effects.EffMakeSphere; import me.TheBukor.SkStuff.effects.EffMakeWalls; @@ -36,6 +38,7 @@ import me.TheBukor.SkStuff.effects.EffNaturalize; import me.TheBukor.SkStuff.effects.EffRememberChanges; import me.TheBukor.SkStuff.effects.EffReplaceBlocksWE; import me.TheBukor.SkStuff.effects.EffSetBlocksWE; +import me.TheBukor.SkStuff.effects.EffSetPathGoals; import me.TheBukor.SkStuff.effects.EffSimulateSnow; import me.TheBukor.SkStuff.effects.EffToggleVanish; import me.TheBukor.SkStuff.effects.EffUndoRedoSession; @@ -69,8 +72,10 @@ public class SkStuff extends JavaPlugin { private int exprAmount = 0; private int typeAmount = 0; - private Class nbtClass = ReflectionUtils.getNMSClass("NBTTagCompound"); - private Class nbtParserClass = ReflectionUtils.getNMSClass("MojangsonParser"); + private Class nbtClass = ReflectionUtils.getNMSClass("NBTTagCompound", false); + private Class nbtListClass = ReflectionUtils.getNMSClass("NBTTagList", false); + private Class nbtArrayClass = ReflectionUtils.getNMSClass("NBTTagCompound", true); + private Class nbtParserClass = ReflectionUtils.getNMSClass("MojangsonParser", false); @SuppressWarnings("unchecked") public void onEnable() { @@ -86,6 +91,10 @@ public class SkStuff extends JavaPlugin { exprAmount += 4; getLogger().info("Trying to register version specific stuff..."); + Skript.registerEffect(EffClearPathGoals.class, "(clear|delete) [all] pathfind[er] goals (of|from) %livingentity%"); +// Skript.registerEffect(EffSetPathGoals.class, "add pathfind[er] goal leap at target to %livingentity% with [leap] height [of] %number%"); + Skript.registerEffect(EffSetPathGoals.class, "add pathfind[er] goal [priority %-integer%] (0¦(avoid|run away from) %entitydata%,[ ]radius %number%,[ ][at] speed %number%,[ ][at] speed if close %number%|1¦break door[s]|2¦breed,[ ][move to[wards] lover at] speed %number%|3¦eat grass|4¦(flee from the sun|seek shad(e|ow)),[ ][at] speed %number%|5¦float (in[side]|on) water|6¦follow (owner|tamer),[ ][at] speed %number%|7¦follow (adult[s]|parent[s]),[ ][at] speed %number%|8¦(fight back|react to|target) (damager|attacker) [[of] type %entitydata%]|9¦o(c|z)elot jump on blocks,[ ][at] speed %number%|10¦leap at target height %number%|11¦look at players,[ ]radius %number%|12¦melee attack %entitydata%,[ ][at] speed %number%|13¦(move|go) indoors|14¦move thr(u|ough) village,[ ][at] speed %number%|15¦move to[wards] target,[ ][at] speed %number%,[ ]radius %number%|16¦attack nearest entity [of] type %entitydata%|17¦o(c|z)elot attack [chicken]|18¦open door[s]|19¦(panic|flee),[ ][at] speed %number%|20¦look around randomly|21¦(walk around randomly|wander),[ ][at] speed %number%,[ ][with] %integer% ticks between mov(e[ment]|ing)|22¦sit|23¦[creeper] swell)"); + Skript.registerEffect(EffMakeJump.class, "make %livingentity% jump"); Skript.registerExpression(ExprNBTOf.class, Object.class, ExpressionType.PROPERTY, "nbt[[ ]tag[s]] of %entity/block/itemstack%", "%entity/block/itemstack%'s nbt[[ ]tag[s]]"); Skript.registerExpression(ExprItemNBT.class, ItemStack.class, ExpressionType.SIMPLE, "%itemstack% with [custom] nbt[[ ]tag[s]] %string%"); Skript.registerExpression(ExprTagOf.class, Object.class, ExpressionType.PROPERTY, "[nbt[ ]]tag %string% of [[nbt] compound] %compound%"); @@ -98,8 +107,8 @@ public class SkStuff extends JavaPlugin { @Override @Nullable public Class[] acceptChange(ChangeMode mode) { - if (mode == ChangeMode.ADD || mode == ChangeMode.REMOVE) { - return CollectionUtils.array(String[].class); + if (mode == ChangeMode.ADD || mode == ChangeMode.REMOVE || mode == ChangeMode.SET) { + return CollectionUtils.array(String[].class, nbtArrayClass); } return null; } @@ -107,22 +116,31 @@ public class SkStuff extends JavaPlugin { @Override public void change(Object[] NBT, @Nullable Object[] delta, ChangeMode mode) { if (NBT[0].getClass().getName().contains("NBTTagCompound")) { - if (!(delta[0] instanceof String)) + if (!(delta[0] instanceof String) || !(delta[0].getClass().isInstance(nbtClass))) return; String newTags = (String) delta[0]; - if (mode == ChangeMode.ADD) { - Object NBT1 = null; - try { - NBT1 = nbtParserClass.getMethod("parse", String.class).invoke(NBT1, newTags); - } catch (Exception ex) { - if (ex instanceof InvocationTargetException && ex.getCause().getClass().getName().contains("MojangsonParseException")) { - getLogger().warning(Ansi.ansi().fgBright(Ansi.Color.RED) + "Error when parsing NBT - " + ex.getCause().getMessage() + Ansi.ansi().fgBright(Ansi.Color.DEFAULT)); - return; + if (mode == ChangeMode.SET) { + if (!(delta[0] instanceof String)) + NBT[0] = delta[0]; + } else if (mode == ChangeMode.ADD) { + if (delta[0] instanceof String) { + Object NBT1 = null; + try { + NBT1 = nbtParserClass.getMethod("parse", String.class).invoke(NBT1, newTags); + } catch (Exception ex) { + if (ex instanceof InvocationTargetException && ex.getCause().getClass().getName().contains("MojangsonParseException")) { + getLogger().warning(Ansi.ansi().fgBright(Ansi.Color.RED) + "Error when parsing NBT - " + ex.getCause().getMessage() + Ansi.ansi().fgBright(Ansi.Color.DEFAULT)); + return; + } + ex.printStackTrace(); } - ex.printStackTrace(); + NBTUtil.addCompound(NBT[0], NBT1); + } else { + NBTUtil.addCompound(NBT[0], delta[0]); } - NBTUtil.addCompound(NBT[0], NBT1); } else if (mode == ChangeMode.REMOVE) { + if (!(delta[0] instanceof String)) + return; for (Object s : delta) { try { nbtClass.getMethod("remove", String.class).invoke(NBT[0], (String) s); @@ -137,13 +155,13 @@ public class SkStuff extends JavaPlugin { @Override public String getVariableNamePattern() { - return "nbt:{.+:.+}"; + return ".+"; } @Override @Nullable public Object parse(String rawNBT, ParseContext context) { - if (rawNBT.startsWith("{") && rawNBT.contains(":") && rawNBT.endsWith("}")) { + if (rawNBT.startsWith("nbt:{") && rawNBT.endsWith("}")) { Object NBT = null; try { NBT = nbtParserClass.getMethod("parse", String.class).invoke(NBT, rawNBT); @@ -168,10 +186,93 @@ public class SkStuff extends JavaPlugin { @Override public String toVariableNameString(Object compound) { - return "nbt:" + compound.toString(); + return compound.toString(); } })); - typeAmount += 1; + + Classes.registerClass(new ClassInfo((Class) nbtListClass, "nbtlist").user("nbt ?list ?(tag)?").name("NBT List").changer(new Changer() { + + @Override + @Nullable + public Class[] acceptChange(ChangeMode mode) { + if (mode == ChangeMode.ADD || mode == ChangeMode.SET || mode == ChangeMode.DELETE || mode == ChangeMode.RESET) { + return CollectionUtils.array(Float[].class, Double[].class, String[].class, nbtArrayClass, Integer[].class); + } + return null; + } + + @Override + public void change(Object[] list, @Nullable Object[] delta, ChangeMode mode) { + if (list[0].getClass().getName().contains("NBTTagList")) { + int typeId = 0; + if (delta instanceof Float[]) { + typeId = 5; + } else if (delta instanceof Double[]) { + typeId = 6; + } else if (delta instanceof String[]) { + typeId = 8; + } else if (delta.getClass() == nbtArrayClass) { + typeId = 10; + } else if (delta instanceof Integer[]) { + typeId = 11; + } else { + return; + } + if (mode == ChangeMode.SET) { + if (NBTUtil.getContentsId(list) == typeId) + list[0] = delta[0]; + } else if (mode == ChangeMode.ADD) { + if (NBTUtil.getContentsId(list) == typeId) { + if (typeId == 10) { + String newTags = (String) delta[0]; + Object NBT1 = null; + try { + NBT1 = nbtParserClass.getMethod("parse", String.class).invoke(NBT1, newTags); + } catch (Exception ex) { + if (ex instanceof InvocationTargetException && ex.getCause().getClass().getName().contains("MojangsonParseException")) { + Skript.warning(Ansi.ansi().fgBright(Ansi.Color.RED) + "Error when parsing NBT - " + ex.getCause().getMessage() + Ansi.ansi().fgBright(Ansi.Color.DEFAULT)); + return; + } + ex.printStackTrace(); + } + NBTUtil.addToList(list[0], new Object[] { NBT1 }); + } else { + NBTUtil.addToList(list[0], delta); + } + } + } else if (mode == ChangeMode.DELETE || mode == ChangeMode.RESET) { + try { + list[0] = nbtListClass.newInstance(); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + } + } + }).parser(new Parser() { + + @Override + public String getVariableNamePattern() { + return ".+"; + } + + @Override + @Nullable + public Object parse(String rawNBTList, ParseContext context) { + return null; + } + + @Override + public String toString(Object list, int arg1) { + return list.toString(); + } + + @Override + public String toVariableNameString(Object list) { + return list.toString(); + } + })); + typeAmount += 2; exprAmount += 6; if (Bukkit.getPluginManager().getPlugin("WorldEdit") != null) { getLogger().info("WorldEdit found! Registering WorldEdit stuff..."); diff --git a/src/me/TheBukor/SkStuff/effects/EffClearPathGoals.java b/src/me/TheBukor/SkStuff/effects/EffClearPathGoals.java new file mode 100644 index 0000000..1f95481 --- /dev/null +++ b/src/me/TheBukor/SkStuff/effects/EffClearPathGoals.java @@ -0,0 +1,54 @@ +package me.TheBukor.SkStuff.effects; + +import java.util.List; + +import javax.annotation.Nullable; + +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.Event; + +import ch.njol.skript.lang.Effect; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.util.Kleenean; +import me.TheBukor.SkStuff.util.ReflectionUtils; + +public class EffClearPathGoals extends Effect { + private Expression entity; + + private Class goalSelectorClass = ReflectionUtils.getNMSClass("PathfinderGoalSelector", false); + private Class insentientEnt = ReflectionUtils.getNMSClass("EntityInsentient", false); + private Class craftLivEnt = ReflectionUtils.getOBCClass("entity.CraftLivingEntity"); + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression[] expr, int matchedPattern, Kleenean arg2, ParseResult result) { + entity = (Expression) expr[0]; + return true; + } + + @Override + public String toString(@Nullable Event e, boolean arg1) { + return "clear all pathfind goals of " + entity.toString(e, false); + } + + @Override + protected void execute(Event e) { + LivingEntity ent = entity.getSingle(e); + if (ent instanceof Player || ent == null) + return; + Object obcEnt = craftLivEnt.cast(ent); + try { + Object nmsEnt = insentientEnt.cast(obcEnt.getClass().getMethod("getHandle").invoke(obcEnt)); + Object goalSelector = ReflectionUtils.getField("goalSelector", insentientEnt, nmsEnt); + Object targetSelector = ReflectionUtils.getField("targetSelector", insentientEnt, nmsEnt); + ((List) ReflectionUtils.getField("b", goalSelectorClass, goalSelector)).clear(); + ((List) ReflectionUtils.getField("c", goalSelectorClass, goalSelector)).clear(); + ((List) ReflectionUtils.getField("b", goalSelectorClass, targetSelector)).clear(); + ((List) ReflectionUtils.getField("c", goalSelectorClass, targetSelector)).clear(); + } catch (Exception ex) { + ex.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/me/TheBukor/SkStuff/effects/EffMakeJump.java b/src/me/TheBukor/SkStuff/effects/EffMakeJump.java new file mode 100644 index 0000000..2b68720 --- /dev/null +++ b/src/me/TheBukor/SkStuff/effects/EffMakeJump.java @@ -0,0 +1,47 @@ +package me.TheBukor.SkStuff.effects; + +import javax.annotation.Nullable; + +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.Event; + +import ch.njol.skript.lang.Effect; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.util.Kleenean; +import me.TheBukor.SkStuff.util.ReflectionUtils; + +public class EffMakeJump extends Effect { + private Expression entity; + + private Class entInsent = ReflectionUtils.getNMSClass("EntityInsentient", false); + private Class craftLivEnt = ReflectionUtils.getOBCClass("entity.CraftLivingEntity"); + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression[] expr, int matchedPattern, Kleenean arg2, ParseResult result) { + entity = (Expression) expr[0]; + return true; + } + + @Override + public String toString(@Nullable Event e, boolean arg1) { + return "make ent jump"; + } + + @Override + protected void execute(Event e) { + LivingEntity ent = entity.getSingle(e); + if (ent instanceof Player || ent == null) + return; + Object obcEnt = craftLivEnt.cast(ent); + try { + Object nmsEnt = entInsent.cast(obcEnt.getClass().getMethod("getHandle").invoke(obcEnt)); + Object controllerJump = nmsEnt.getClass().getMethod("getControllerJump").invoke(nmsEnt); + controllerJump.getClass().getMethod("a").invoke(controllerJump); + } catch (Exception ex) { + ex.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/me/TheBukor/SkStuff/effects/EffSetPathGoals.java b/src/me/TheBukor/SkStuff/effects/EffSetPathGoals.java new file mode 100644 index 0000000..3dd443f --- /dev/null +++ b/src/me/TheBukor/SkStuff/effects/EffSetPathGoals.java @@ -0,0 +1,188 @@ +package me.TheBukor.SkStuff.effects; + +import javax.annotation.Nullable; + +import org.bukkit.Bukkit; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.Event; + +import ch.njol.skript.entity.EntityData; +import ch.njol.skript.lang.Effect; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.util.Kleenean; +import me.TheBukor.SkStuff.util.ReflectionUtils; + +public class EffSetPathGoals extends Effect { + private Expression goalPriority; + private Expression> entityToAvoid; + private Expression avoidRadius; + private Expression avoidSpeed1; + private Expression avoidSpeed2; + private Expression breedSpeed; + private Expression fleeSunSpeed; + @SuppressWarnings("unused") + private Expression followOwnerSpeed; + @SuppressWarnings("unused") + private Expression followAdultsSpeed; + @SuppressWarnings("unused") + private Expression> entitiesToFightBack; + @SuppressWarnings("unused") + private Expression jumpOnBlockSpeed; + @SuppressWarnings("unused") + private Expression leapHeight; + @SuppressWarnings("unused") + private Expression lookRadius; + @SuppressWarnings("unused") + private Expression> meleeTarget; + @SuppressWarnings("unused") + private Expression meleeSpeed; + @SuppressWarnings("unused") + private Expression moveVillageSpeed; + @SuppressWarnings("unused") + private Expression moveTargetSpeed; + @SuppressWarnings("unused") + private Expression moveTargetRadius; + @SuppressWarnings("unused") + private Expression> nearTarget; + @SuppressWarnings("unused") + private Expression panicSpeed; + @SuppressWarnings("unused") + private Expression randomWalkSpeed; + @SuppressWarnings("unused") + private Expression randomWalkInterval; + private Expression entity; + + private int mark; + + private Class goal = ReflectionUtils.getNMSClass("PathfinderGoal", false); + private Class goalSelector = ReflectionUtils.getNMSClass("PathfinderGoalSelector", false); + private Class goalAvoid = ReflectionUtils.getNMSClass("PathfinderGoalAvoidTarget", false); + private Class goalBreed = ReflectionUtils.getNMSClass("PathfinderGoalBreed", false); + private Class goalBreakDoor = ReflectionUtils.getNMSClass("PathfinderGoalBreakDoor", false); + private Class goalEatGrass = ReflectionUtils.getNMSClass("PathfinderGoalEatTile", false); + private Class goalFleeSun = ReflectionUtils.getNMSClass("PathfinderGoalFleeSun", false); + @SuppressWarnings("unused") + private Class goalLeapTarget = ReflectionUtils.getNMSClass("PathfinderGoalLeapAtTarget", false); + + private Class entAnimal = ReflectionUtils.getNMSClass("EntityAnimal", false); + private Class entCreature = ReflectionUtils.getNMSClass("EntityCreature", false); + private Class entInsent = ReflectionUtils.getNMSClass("EntityInsentient", false); + private Class craftLivEnt = ReflectionUtils.getOBCClass("entity.CraftLivingEntity"); + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression[] expr, int matchedPattern, Kleenean arg2, ParseResult result) { + goalPriority = (Expression) expr[0]; + mark = result.mark; + if (mark == 0) { + entityToAvoid = (Expression>) expr[1]; + avoidRadius = (Expression) expr[2]; + avoidSpeed1 = (Expression) expr[3]; + avoidSpeed2 = (Expression) expr[4]; + } else if (mark == 2) { + breedSpeed = (Expression) expr[5]; + } else if (mark == 4) { + fleeSunSpeed = (Expression) expr[6]; + } else if (mark == 6) { + followOwnerSpeed = (Expression) expr[7]; + } else if (mark == 7) { + followAdultsSpeed = (Expression) expr[8]; + } else if (mark == 8) { + entitiesToFightBack = (Expression>) expr[9]; + } else if (mark == 9) { + jumpOnBlockSpeed = (Expression) expr[10]; + } else if (mark == 10) { + leapHeight = (Expression) expr[11]; + } else if (mark == 11) { + lookRadius = (Expression) expr[12]; + } else if (mark == 12) { + meleeTarget = (Expression>) expr[13]; + meleeSpeed = (Expression) expr[14]; + } else if (mark == 13) { + moveVillageSpeed = (Expression) expr[15]; + } else if (mark == 15) { + moveTargetSpeed = (Expression) expr[16]; + moveTargetRadius = (Expression) expr[17]; + } else if (mark == 16) { + nearTarget = (Expression>) expr[18]; + } else if (mark == 19) { + panicSpeed = (Expression) expr[19]; + } else if (mark == 21) { + randomWalkSpeed = (Expression) expr[20]; + randomWalkInterval = (Expression) expr[21]; + } + entity = (Expression) expr[22]; + return true; + } + + @Override + public String toString(@Nullable Event e, boolean arg1) { + return "add pathfinder goal to ent"; + } + + @Override + protected void execute(Event e) { + int priority = 0; + if (goalPriority != null) { + priority = goalPriority.getSingle(e).intValue(); + } else { + priority = 1; + } + if (priority < 0) { + priority = 1; + } else if (priority > 9) { + priority = 9; + } + LivingEntity ent = entity.getSingle(e); + if (ent instanceof Player || ent == null) + return; + Object obcEnt = craftLivEnt.cast(ent); + try { + Object newGoal = null; + Object nmsEnt = entInsent.cast(obcEnt.getClass().getMethod("getHandle").invoke(obcEnt)); + Object goals = ReflectionUtils.getField("goalSelector", entInsent, nmsEnt); + if (mark == 0) { + float radius = avoidRadius.getSingle(e).floatValue(); + double spd1 = avoidSpeed1.getSingle(e).doubleValue(); + double spd2 = avoidSpeed2.getSingle(e).doubleValue(); + EntityData entityData; + String exprInput = entityToAvoid.toString(e, false); + if (exprInput.startsWith("the ")) { + exprInput = exprInput.substring(4); + } + entityData = EntityData.parseWithoutIndefiniteArticle(exprInput); + String className = entityData.getType().getSimpleName(); + if (className.equals("HumanEntity")) + className = "Human"; + className = "Entity" + className; + Class nmsClass = ReflectionUtils.getNMSClass(className, false); + if (nmsClass == null) + return; + newGoal = goalAvoid.getConstructor(entCreature, Class.class, float.class, double.class, double.class).newInstance(nmsEnt, nmsClass, radius, spd1, spd2); + newGoal = goalSelector.getMethod("a", int.class, goal).invoke(goals, priority, newGoal); + } else if (mark == 1) { + newGoal = goalBreakDoor.getConstructor(entInsent).newInstance(nmsEnt); + newGoal = goalSelector.getMethod("a", int.class, goal).invoke(goals, priority, newGoal); + } else if (mark == 2) { + double spd = breedSpeed.getSingle(e).doubleValue(); + if (!(nmsEnt.getClass().isAssignableFrom(entAnimal))) + return; + newGoal = goalBreed.getConstructor(entAnimal, double.class).newInstance(nmsEnt, spd); + newGoal = goalSelector.getMethod("a", int.class, goal).invoke(goals, priority, newGoal); + } else if (mark == 3) { + newGoal = goalEatGrass.getConstructor(entInsent).newInstance(nmsEnt); + newGoal = goalSelector.getMethod("a", int.class, goal).invoke(goals, priority, newGoal); + } else if (mark == 4) { + double spd = fleeSunSpeed.getSingle(e).doubleValue(); + newGoal = goalFleeSun.getConstructor(entCreature, double.class).newInstance(nmsEnt, spd); + newGoal = goalSelector.getMethod("a", int.class, goal).invoke(goals, priority, newGoal); + } else { + Bukkit.broadcastMessage("Not an Avoid, BreakDoor, Breed or FleeSun goal"); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/me/TheBukor/SkStuff/expressions/ExprFileNBT.java b/src/me/TheBukor/SkStuff/expressions/ExprFileNBT.java index 3705667..acee8f4 100644 --- a/src/me/TheBukor/SkStuff/expressions/ExprFileNBT.java +++ b/src/me/TheBukor/SkStuff/expressions/ExprFileNBT.java @@ -28,9 +28,9 @@ import me.TheBukor.SkStuff.util.ReflectionUtils; public class ExprFileNBT extends SimpleExpression { private Expression input; - private Class nbtClass = ReflectionUtils.getNMSClass("NBTTagCompound"); - private Class nbtParserClass = ReflectionUtils.getNMSClass("MojangsonParser"); - private Class nbtCompressedClass = ReflectionUtils.getNMSClass("NBTCompressedStreamTools"); + private Class nbtClass = ReflectionUtils.getNMSClass("NBTTagCompound", false); + private Class nbtParserClass = ReflectionUtils.getNMSClass("MojangsonParser", false); + private Class nbtCompressedClass = ReflectionUtils.getNMSClass("NBTCompressedStreamTools", false); @Override public Class getReturnType() { diff --git a/src/me/TheBukor/SkStuff/expressions/ExprFireProof.java b/src/me/TheBukor/SkStuff/expressions/ExprFireProof.java index b460a7d..700cff7 100644 --- a/src/me/TheBukor/SkStuff/expressions/ExprFireProof.java +++ b/src/me/TheBukor/SkStuff/expressions/ExprFireProof.java @@ -1,7 +1,5 @@ package me.TheBukor.SkStuff.expressions; -import java.lang.reflect.Field; - import javax.annotation.Nullable; import org.bukkit.entity.Entity; @@ -71,14 +69,7 @@ public class ExprFireProof extends SimpleExpression { } if (mode == ChangeMode.SET) { Boolean newValue = (Boolean) delta[0]; - try { - Field field = nmsEnt.getClass().getDeclaredField("fireProof"); - field.setAccessible(true); - field.setBoolean(nmsEnt, newValue); - field.setAccessible(false); - } catch (Exception ex) { - ex.printStackTrace(); - } + ReflectionUtils.setField("fireProof", nmsEnt.getClass(), nmsEnt, newValue); } } diff --git a/src/me/TheBukor/SkStuff/expressions/ExprItemNBT.java b/src/me/TheBukor/SkStuff/expressions/ExprItemNBT.java index b5f2f8c..2f32109 100644 --- a/src/me/TheBukor/SkStuff/expressions/ExprItemNBT.java +++ b/src/me/TheBukor/SkStuff/expressions/ExprItemNBT.java @@ -20,9 +20,9 @@ public class ExprItemNBT extends SimpleExpression { private Expression itemStack; private Expression string; - private Class nbtClass = ReflectionUtils.getNMSClass("NBTTagCompound"); - private Class nbtParseClass = ReflectionUtils.getNMSClass("MojangsonParser"); - private Class nmsItemClass = ReflectionUtils.getNMSClass("ItemStack"); + private Class nbtClass = ReflectionUtils.getNMSClass("NBTTagCompound", false); + private Class nbtParseClass = ReflectionUtils.getNMSClass("MojangsonParser", false); + private Class nmsItemClass = ReflectionUtils.getNMSClass("ItemStack", false); private Class craftItemClass = ReflectionUtils.getOBCClass("inventory.CraftItemStack"); diff --git a/src/me/TheBukor/SkStuff/expressions/ExprNBTOf.java b/src/me/TheBukor/SkStuff/expressions/ExprNBTOf.java index 785babc..c757204 100644 --- a/src/me/TheBukor/SkStuff/expressions/ExprNBTOf.java +++ b/src/me/TheBukor/SkStuff/expressions/ExprNBTOf.java @@ -25,10 +25,10 @@ import me.TheBukor.SkStuff.util.ReflectionUtils; public class ExprNBTOf extends SimpleExpression { private Expression target; - private Class nbtClass = ReflectionUtils.getNMSClass("NBTTagCompound"); - private Class nbtParserClass = ReflectionUtils.getNMSClass("MojangsonParser"); - private Class nmsPosClass = ReflectionUtils.getNMSClass("BlockPosition"); - private Class nmsItemClass = ReflectionUtils.getNMSClass("ItemStack"); + private Class nbtClass = ReflectionUtils.getNMSClass("NBTTagCompound", false); + private Class nbtParserClass = ReflectionUtils.getNMSClass("MojangsonParser", false); + private Class nmsPosClass = ReflectionUtils.getNMSClass("BlockPosition", false); + private Class nmsItemClass = ReflectionUtils.getNMSClass("ItemStack", false); private Class craftEntClass = ReflectionUtils.getOBCClass("entity.CraftEntity"); private Class craftItemClass = ReflectionUtils.getOBCClass("inventory.CraftItemStack"); diff --git a/src/me/TheBukor/SkStuff/expressions/ExprTagOf.java b/src/me/TheBukor/SkStuff/expressions/ExprTagOf.java index 7719ed0..dc0f98a 100644 --- a/src/me/TheBukor/SkStuff/expressions/ExprTagOf.java +++ b/src/me/TheBukor/SkStuff/expressions/ExprTagOf.java @@ -16,8 +16,8 @@ public class ExprTagOf extends SimpleExpression { private Expression string; private Expression compound; - private Class nbtClass = ReflectionUtils.getNMSClass("NBTTagCompound"); - private Class nbtBaseClass = ReflectionUtils.getNMSClass("NBTBase"); + private Class nbtClass = ReflectionUtils.getNMSClass("NBTTagCompound", false); + private Class nbtBaseClass = ReflectionUtils.getNMSClass("NBTBase", false); @Override public Class getReturnType() { @@ -47,7 +47,7 @@ public class ExprTagOf extends SimpleExpression { String stringTag = string.getSingle(e); Object tag = null; try { - tag = NBT.getClass().getMethod("get", String.class).invoke(NBT, stringTag); + tag = nbtClass.getMethod("get", String.class).invoke(NBT, stringTag); } catch (Exception ex) { ex.printStackTrace(); } @@ -62,34 +62,45 @@ public class ExprTagOf extends SimpleExpression { try { switch (id) { case 1: - return new Byte[] { Byte.valueOf(NBT.getClass().getMethod("getByte", String.class).invoke(NBT, stringTag).toString()) }; + return new Byte[] { Byte.valueOf(nbtClass.getMethod("getByte", String.class).invoke(NBT, stringTag).toString()) }; case 2: - return new Short[] { Short.valueOf(NBT.getClass().getMethod("getShort", String.class).invoke(NBT, stringTag).toString()) }; + return new Short[] { Short.valueOf(nbtClass.getMethod("getShort", String.class).invoke(NBT, stringTag).toString()) }; case 3: - return new Integer[] { Integer.valueOf(NBT.getClass().getMethod("getInt", String.class).invoke(NBT, stringTag).toString()) }; + return new Integer[] { Integer.valueOf(nbtClass.getMethod("getInt", String.class).invoke(NBT, stringTag).toString()) }; case 4: - return new Long[] { Long.valueOf(NBT.getClass().getMethod("getLong", String.class).invoke(NBT, stringTag).toString()) }; + return new Long[] { Long.valueOf(nbtClass.getMethod("getLong", String.class).invoke(NBT, stringTag).toString()) }; case 5: - return new Float[] { Float.valueOf(NBT.getClass().getMethod("getFloat", String.class).invoke(NBT, stringTag).toString()) }; + return new Float[] { Float.valueOf(nbtClass.getMethod("getFloat", String.class).invoke(NBT, stringTag).toString()) }; case 6: - return new Double[] { Double.valueOf(NBT.getClass().getMethod("getDouble", String.class).invoke(NBT, stringTag).toString()) }; + return new Double[] { Double.valueOf(nbtClass.getMethod("getDouble", String.class).invoke(NBT, stringTag).toString()) }; case 7: //Byte array, only used in chunk files. Also doesn't have support for the MojangsonParser. break; case 8: - return new String[] { NBT.getClass().getMethod("getString", String.class).invoke(NBT, stringTag).toString() }; - //Lists will be probably an ASS to implement when I get to them + return new String[] { nbtClass.getMethod("getString", String.class).invoke(NBT, stringTag).toString() }; case 9: + int i; + Object[] list = new Object[] { new Object() }; + for (i = 1; i <= 11; i++) { //To get a list I need to know the type of the tags it contains inside, + //since I can't predict what type the list will have, I just loop all of the IDs until I find a non-empty list. + list[0] = nbtClass.getMethod("getList", String.class, int.class).invoke(NBT, stringTag, i); //Try to get the list with the ID "loop-number". + if (!list[0].toString().equals("[]")) { //If list is not empty. + break; //Stop loop. + } + } + return list; + /* + REMOVED TEMPORARILY, HOPEFULLY THE NEW IMPLEMENTATION SHOULD WORK BETTER int i; Object list = null; for (i = 1; i <= 11; i++) { //To get a list I need to know the type of the tags it contains inside, //since I can't predict what type the list will have, I just loop all of the IDs until I find a non-empty list. - list = NBT.getClass().getMethod("getList", String.class, int.class).invoke(NBT, stringTag, i); //Try to get the list with the ID "loop-number". + list = nbtClass.getMethod("getList", String.class, int.class).invoke(NBT, stringTag, i); //Try to get the list with the ID "loop-number". if (!list.toString().equals("[]")) { //If list is not empty. break; //Stop loop. } } String methodName = null; - switch (((int) list.getClass().getMethod("f").invoke(list))) { //list.f() gets the type of the tags in the list. + switch (NBTUtil.getContentsId(list)) { case 5: //Float methodName = "e"; //list.e(int) = get float from the specified index. break; @@ -115,10 +126,11 @@ public class ExprTagOf extends SimpleExpression { tags[i] = gottenTag; } return tags; + */ case 10: - return new Object[] { NBT.getClass().getMethod("getCompound", String.class).invoke(NBT, stringTag) }; + return new Object[] { nbtClass.getMethod("getCompound", String.class).invoke(NBT, stringTag) }; case 11: //Integer array, this one is only used on the chunk files (and maybe schematic files?). - return new Object[] { NBT.getClass().getMethod("getIntArray", String.class).invoke(NBT, stringTag).toString() }; + return new Object[] { nbtClass.getMethod("getIntArray", String.class).invoke(NBT, stringTag).toString() }; default: //This shouldn't happen, but it's better to have this just in case it spills errors everywhere. break; } @@ -142,19 +154,19 @@ public class ExprTagOf extends SimpleExpression { Object newValue = delta[0]; try { if (newValue instanceof Byte) { - NBT.getClass().getMethod("setByte", String.class, byte.class).invoke(NBT, stringTag, (byte) newValue); + nbtClass.getMethod("setByte", String.class, byte.class).invoke(NBT, stringTag, ((Byte) newValue).byteValue()); } else if (newValue instanceof Short) { - NBT.getClass().getMethod("setShort", String.class, short.class).invoke(NBT, stringTag, (short) newValue); + nbtClass.getMethod("setShort", String.class, short.class).invoke(NBT, stringTag, ((Short) newValue).shortValue()); } else if (newValue instanceof Integer) { - NBT.getClass().getMethod("setInt", String.class, int.class).invoke(NBT, stringTag, (int) newValue); + nbtClass.getMethod("setInt", String.class, int.class).invoke(NBT, stringTag, ((Integer) newValue).intValue()); } else if (newValue instanceof Long) { - NBT.getClass().getMethod("setLong", String.class, long.class).invoke(NBT, stringTag, (long) newValue); + nbtClass.getMethod("setLong", String.class, long.class).invoke(NBT, stringTag, ((Long) newValue).longValue()); } else if (newValue instanceof Float) { - NBT.getClass().getMethod("setFloat", String.class, float.class).invoke(NBT, stringTag, (float) newValue); + nbtClass.getMethod("setFloat", String.class, float.class).invoke(NBT, stringTag, ((Float) newValue).floatValue()); } else if (newValue instanceof Double) { - NBT.getClass().getMethod("setDouble", String.class, double.class).invoke(NBT, stringTag, (double) newValue); + nbtClass.getMethod("setDouble", String.class, double.class).invoke(NBT, stringTag, ((Double) newValue).doubleValue()); } else if (newValue instanceof String) { - NBT.getClass().getMethod("setString", String.class, String.class).invoke(NBT, stringTag, (String) newValue); + nbtClass.getMethod("setString", String.class, String.class).invoke(NBT, stringTag, String.valueOf(newValue)); } else { return; //Something else like a list or entire compound. } @@ -163,7 +175,7 @@ public class ExprTagOf extends SimpleExpression { } } else if (mode == ChangeMode.RESET || mode == ChangeMode.DELETE) { try { - NBT.getClass().getMethod("set", String.class, nbtBaseClass).invoke(NBT, stringTag, nbtBaseClass.newInstance()); + nbtClass.getMethod("set", String.class, nbtBaseClass).invoke(NBT, stringTag, nbtBaseClass.newInstance()); } catch (Exception ex) { ex.printStackTrace(); } diff --git a/src/me/TheBukor/SkStuff/util/NBTUtil.java b/src/me/TheBukor/SkStuff/util/NBTUtil.java index 83466e3..ab36d42 100644 --- a/src/me/TheBukor/SkStuff/util/NBTUtil.java +++ b/src/me/TheBukor/SkStuff/util/NBTUtil.java @@ -3,11 +3,17 @@ package me.TheBukor.SkStuff.util; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Set; +import org.fusesource.jansi.Ansi; + +import ch.njol.skript.Skript; + public class NBTUtil { - private static Class nbtBaseClass = ReflectionUtils.getNMSClass("NBTBase"); - private static Class nbtClass = ReflectionUtils.getNMSClass("NBTTagCompound"); + private static Class nbtBaseClass = ReflectionUtils.getNMSClass("NBTBase", false); + private static Class nbtClass = ReflectionUtils.getNMSClass("NBTTagCompound", false); + private static Class nbtListClass = ReflectionUtils.getNMSClass("NBTTagList", false); /** * This is actually a copy of the "a(NBTTagCompound)" method in the NBTTagCompound class. @@ -19,22 +25,20 @@ public class NBTUtil { */ @SuppressWarnings("unchecked") public static void addCompound(Object NBT, Object toAdd) { - if (NBT.getClass().getName().contains("NBTTagCompound") && toAdd.getClass().getName().contains("NBTTagCompound")) { - Field map = null; + if (NBT.getClass() == nbtClass && toAdd.getClass() == nbtClass) { try { - map = nbtClass.getDeclaredField("map"); - map.setAccessible(true); + HashMap map = (HashMap) ReflectionUtils.getField("map", nbtClass, toAdd); Set keySet = (Set) nbtClass.getMethod("c").invoke(toAdd); Iterator iterator = keySet.iterator(); while(iterator.hasNext()) { String string = (String) iterator.next(); - Object base = nbtBaseClass.cast((((HashMap) map.get(toAdd)).get(string))); + Object base = nbtBaseClass.cast(map.get(string)); if((byte) nbtBaseClass.getMethod("getTypeId").invoke(base) == 10) { if((boolean) nbtClass.getMethod("hasKeyOfType", String.class, int.class).invoke(NBT, string, 10)) { Object localNBT = null; localNBT = nbtClass.getMethod("getCompound", String.class).invoke(localNBT, string); - NBTUtil.addCompound(localNBT, base.getClass().cast(nbtClass)); + NBTUtil.addCompound(localNBT, nbtBaseClass.cast(nbtClass)); } else { nbtClass.getMethod("set", String.class, nbtBaseClass).invoke(NBT, string, base.getClass().getMethod("clone").invoke(base)); } @@ -42,11 +46,139 @@ public class NBTUtil { nbtClass.getMethod("set", String.class, nbtBaseClass).invoke(NBT, string, base.getClass().getMethod("clone").invoke(base)); } } - map.setAccessible(false); } catch (Exception ex) { - map.setAccessible(false); ex.printStackTrace(); } } } + + /** + * Gets the ID of the contents inside a NBT List. + * I needed to add this because the 1.7 and before versions of the NBTTagList + * class had a different name for the method that got this value. + * 1.8 used "f()", while 1.7 used "d()". + */ + public static int getContentsId(Object list) { + if (list.getClass() == nbtListClass) { + Field type = null; + int result = 0; + try { + type = nbtListClass.getDeclaredField("type"); + type.setAccessible(true); + result = type.getInt(list); + type.setAccessible(false); + return result; + } catch (Exception ex) { + type.setAccessible(false); + ex.printStackTrace(); + } + return result; + } + return 0; + } + + /** + * Used for the "addToList()" and "setIndex()" methods if the typeId of the contents is still not defined. + */ + public static void setContentsId(Object list, int newId) { + if (list.getClass() == nbtListClass) { + Field type = null; + try { + type = nbtListClass.getDeclaredField("type"); + type.setAccessible(true); + type.set(list, newId); + type.setAccessible(false); + } catch (Exception ex) { + type.setAccessible(false); + ex.printStackTrace(); + } + } + } + + @SuppressWarnings("unchecked") + public static List getContents(Object list) { + if (list.getClass() == nbtListClass) { + List result = null; + try { + result = (List) ReflectionUtils.getField("list", nbtListClass, list); + return result; + } catch (Exception ex) { + ex.printStackTrace(); + } + return result; + } + return null; + } + + /** + * Kind of a copy of the "add()" method from the NBTTagList class. + */ + public static void addToList(Object list, Object[] toAdd) { + if (list.getClass() == nbtListClass && toAdd[0].getClass() == nbtBaseClass) { + int listTypeId = NBTUtil.getContentsId(list); + int toAddId = 0; + try { + toAddId = (int) toAdd.getClass().getMethod("getTypeId").invoke(toAdd); + } catch (Exception ex) { + ex.printStackTrace(); + } + if (listTypeId == 0) { + try { + NBTUtil.setContentsId(list, toAddId); + } catch (Exception ex) { + ex.printStackTrace(); + } + } else if (listTypeId != toAddId) { + Skript.warning(Ansi.ansi().fgBright(Ansi.Color.RED) + "Adding mismatching tag types to NBT list" + Ansi.ansi().fgBright(Ansi.Color.DEFAULT)); + return; + } + + for (Object tag : toAdd) { + NBTUtil.getContents(list).add(tag); + } + } + } + + public static void removefromList(Object list, int index) { + if (list.getClass() == nbtListClass) { + if (index >= 0 && index < NBTUtil.getContents(list).size()) { + NBTUtil.getContents(list).remove(index); + } + } + } + + public static void setIndex(Object list, int index, Object toAdd) { + if (list.getClass() == nbtListClass && toAdd.getClass() == nbtBaseClass) { + if (index >= 0 && index < NBTUtil.getContents(list).size()) { + int listTypeId = NBTUtil.getContentsId(list); + int toAddId = 0; + try { + toAddId = (int) toAdd.getClass().getMethod("getTypeId").invoke(toAdd); + } catch (Exception ex) { + ex.printStackTrace(); + } + if (listTypeId == 0) { + try { + NBTUtil.setContentsId(list, toAddId); + } catch (Exception ex) { + ex.printStackTrace(); + } + } else if (listTypeId != toAddId) { + Skript.warning(Ansi.ansi().fgBright(Ansi.Color.RED) + "Adding mismatching tag types to NBT list" + Ansi.ansi().fgBright(Ansi.Color.DEFAULT)); + return; + } + + NBTUtil.getContents(list).set(index, toAdd); + } + } + } + + public static Object getIndex(Object list, int index) { + if (list.getClass() == nbtListClass) { + if (index >= 0 && index < NBTUtil.getContents(list).size()) { + NBTUtil.getContents(list).get(index); + } + } + return null; + } } \ No newline at end of file diff --git a/src/me/TheBukor/SkStuff/util/ReflectionUtils.java b/src/me/TheBukor/SkStuff/util/ReflectionUtils.java index fd371cb..52bdba5 100644 --- a/src/me/TheBukor/SkStuff/util/ReflectionUtils.java +++ b/src/me/TheBukor/SkStuff/util/ReflectionUtils.java @@ -1,13 +1,17 @@ package me.TheBukor.SkStuff.util; +import java.lang.reflect.Field; + import org.bukkit.Bukkit; import org.fusesource.jansi.Ansi; public class ReflectionUtils { - public static Class getNMSClass(String classString) { + public static Class getNMSClass(String classString, boolean isArray) { String version = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3] + "."; String name = "net.minecraft.server." + version + classString; + if (isArray) + name = "[L" + name; Class nmsClass = null; try { nmsClass = Class.forName(name); @@ -31,6 +35,36 @@ public class ReflectionUtils { return obcClass; } + public static Object getField(String field, Class clazz, Object object) { + Field f = null; + Object obj = null; + try { + f = clazz.getDeclaredField(field); + f.setAccessible(true); + obj = f.get(object); + f.setAccessible(false); + } catch (Exception ex) { + if (f != null) + f.setAccessible(false); + ex.printStackTrace(); + } + return obj; + } + + public static void setField(String field, Class clazz, Object object, Object toSet) { + Field f = null; + try { + f = clazz.getDeclaredField(field); + f.setAccessible(true); + f.set(object, toSet); + f.setAccessible(false); + } catch (Exception ex) { + if (f != null) + f.setAccessible(false); + ex.printStackTrace(); + } + } + public static String getVersion() { return Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3] + "."; }