From 301c5b8da120a2098f4f9ffd5f96826fde3ddb9c Mon Sep 17 00:00:00 2001 From: Lemon Date: Sat, 21 Jul 2018 21:34:34 +0500 Subject: [PATCH] add patch for negative infinity and infinity attributes causing disconnect --- .../mojangson/MojangsonFinder.java | 21 ++++ .../momothereal/mojangson/MojangsonToken.java | 47 +++++++ .../mojangson/ex/MojangsonParseException.java | 35 ++++++ .../mojangson/value/MojangsonCompound.java | 118 +++++++++++++++++ .../mojangson/value/MojangsonString.java | 46 +++++++ .../mojangson/value/MojangsonValue.java | 36 ++++++ .../totalfreedommod/MovementValidator.java | 119 ++++++++++++++++++ 7 files changed, 422 insertions(+) create mode 100644 src/main/java/ca/momothereal/mojangson/MojangsonFinder.java create mode 100644 src/main/java/ca/momothereal/mojangson/MojangsonToken.java create mode 100644 src/main/java/ca/momothereal/mojangson/ex/MojangsonParseException.java create mode 100644 src/main/java/ca/momothereal/mojangson/value/MojangsonCompound.java create mode 100644 src/main/java/ca/momothereal/mojangson/value/MojangsonString.java create mode 100644 src/main/java/ca/momothereal/mojangson/value/MojangsonValue.java diff --git a/src/main/java/ca/momothereal/mojangson/MojangsonFinder.java b/src/main/java/ca/momothereal/mojangson/MojangsonFinder.java new file mode 100644 index 00000000..f2dd79a7 --- /dev/null +++ b/src/main/java/ca/momothereal/mojangson/MojangsonFinder.java @@ -0,0 +1,21 @@ +package ca.momothereal.mojangson; + +import ca.momothereal.mojangson.ex.MojangsonParseException; +import ca.momothereal.mojangson.value.*; + +import static ca.momothereal.mojangson.MojangsonToken.*; + +public class MojangsonFinder { + + /** + * Automatically detects the appropriate MojangsonValue from the given value. + * @param value The value to parse + * @return The resulting MojangsonValue. If the type couldn't be found, it falls back to MojangsonString + * @throws MojangsonParseException if the given value could not be parsed + */ + public static MojangsonValue readFromValue(String value) throws MojangsonParseException { + MojangsonValue val = new MojangsonString(); + val.read(value); + return val; + } +} diff --git a/src/main/java/ca/momothereal/mojangson/MojangsonToken.java b/src/main/java/ca/momothereal/mojangson/MojangsonToken.java new file mode 100644 index 00000000..cbb707b5 --- /dev/null +++ b/src/main/java/ca/momothereal/mojangson/MojangsonToken.java @@ -0,0 +1,47 @@ +package ca.momothereal.mojangson; + +public enum MojangsonToken { + + COMPOUND_START(0, "Compound_Start", '{'), + COMPOUND_END(1, "Compound_End", '}'), + ELEMENT_SEPERATOR(2, "Element_Seperator", ','), + ARRAY_START(3, "Array_Start", '['), + ARRAY_END(4, "Array_End", ']'), + ELEMENT_PAIR_SEPERATOR(5, "Pair_Seperator", ':'), + + STRING_QUOTES(6, "String_Quotes", '\"'), + DOUBLE_SUFFIX(8, "Double_Suffix", 'd'), + BYTE_SUFFIX(9, "Byte_Suffix", 'b'), + FLOAT_SUFFIX(10, "Float_Suffix", 'f'), + SHORT_SUFFIX(11, "Short_Suffix", 's'), + LONG_SUFFIX(12, "Long_Suffix", 'l'), + + WHITE_SPACE(13, "WhiteSpace", ' '); + + private int id; + private String name; + private char symbol; + + MojangsonToken(int id, String name, char symbol) { + this.id = id; + this.name = name; + this.symbol = symbol; + } + + public int getId() { + return id; + } + + public String getName() { + return name; + } + + public char getSymbol() { + return symbol; + } + + @Override + public String toString() { + return String.valueOf(symbol); + } +} diff --git a/src/main/java/ca/momothereal/mojangson/ex/MojangsonParseException.java b/src/main/java/ca/momothereal/mojangson/ex/MojangsonParseException.java new file mode 100644 index 00000000..61a847d7 --- /dev/null +++ b/src/main/java/ca/momothereal/mojangson/ex/MojangsonParseException.java @@ -0,0 +1,35 @@ +package ca.momothereal.mojangson.ex; + +public class MojangsonParseException extends Exception { + + private ParseExceptionReason reason; + + public MojangsonParseException(String message, ParseExceptionReason reason) { + super(message); + this.reason = reason; + } + + public ParseExceptionReason getReason() { + return reason; + } + + @Override + public String getMessage() { + return reason.getMessage() + ": " + super.getMessage(); + } + + public enum ParseExceptionReason { + INVALID_FORMAT_NUM("Given value is not numerical"), + UNEXPECTED_SYMBOL("Unexpected symbol in Mojangson string"); + + private String message; + + ParseExceptionReason(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + } +} diff --git a/src/main/java/ca/momothereal/mojangson/value/MojangsonCompound.java b/src/main/java/ca/momothereal/mojangson/value/MojangsonCompound.java new file mode 100644 index 00000000..53c59f84 --- /dev/null +++ b/src/main/java/ca/momothereal/mojangson/value/MojangsonCompound.java @@ -0,0 +1,118 @@ +package ca.momothereal.mojangson.value; + +import ca.momothereal.mojangson.MojangsonFinder; +import ca.momothereal.mojangson.ex.MojangsonParseException; + +import java.util.*; + +import static ca.momothereal.mojangson.MojangsonToken.*; + +public class MojangsonCompound extends HashMap> implements MojangsonValue> { + + private final int C_COMPOUND_START = 0; // Parsing context + private final int C_COMPOUND_PAIR_KEY = 1; // Parsing context + private final int C_COMPOUND_PAIR_VALUE = 2; // Parsing context + + public MojangsonCompound() { + + } + + public MojangsonCompound(Map map) { + super(map); + } + + @Override + public void write(StringBuilder builder) { + builder.append(COMPOUND_START); + boolean start = true; + + for (String key : keySet()) { + if (start) { + start = false; + } else { + builder.append(ELEMENT_SEPERATOR); + } + + builder.append(key).append(ELEMENT_PAIR_SEPERATOR); + List value = get(key); + for(MojangsonValue val : value) + { + val.write(builder); + } + } + builder.append(COMPOUND_END); + } + + @Override + public void read(String string) throws MojangsonParseException { + int context = C_COMPOUND_START; + String tmp_key = "", tmp_val = ""; + int scope = 0; + boolean inString = false; + + for (int index = 0; index < string.length(); index++) { + Character character = string.charAt(index); + + if (character == STRING_QUOTES.getSymbol()) { + inString = !inString; + } + if (character == WHITE_SPACE.getSymbol()) { + if (!inString) + continue; + } + if ((character == COMPOUND_START.getSymbol() || character == ARRAY_START.getSymbol()) && !inString) { + scope++; + } + if ((character == COMPOUND_END.getSymbol() || character == ARRAY_END.getSymbol()) && !inString) { + scope--; + } + if (context == C_COMPOUND_START) { + if (character != COMPOUND_START.getSymbol()) { + parseException(index, character); + return; + } + context++; + continue; + } + if (context == C_COMPOUND_PAIR_KEY) { + if (character == ELEMENT_PAIR_SEPERATOR.getSymbol() && scope <= 1) { + context++; + continue; + } + tmp_key += character; + continue; + } + if (context == C_COMPOUND_PAIR_VALUE) { + if ((character == ELEMENT_SEPERATOR.getSymbol() || character == COMPOUND_END.getSymbol()) && scope <= 1 && !inString) { + context = C_COMPOUND_PAIR_KEY; + computeIfAbsent(tmp_key, k -> new ArrayList<>()).add(MojangsonFinder.readFromValue(tmp_val)); + tmp_key = tmp_val = ""; + continue; + } + tmp_val += character; + } + } + } + + @Override + public Map getValue() { + HashMap hack = new HashMap<>(); + for(String string : keySet()) + { + for(MojangsonValue value : get(string)) + { + hack.put(string, value); + } + } + return hack; + } + + @Override + public Class getValueClass() { + return Map.class; + } + + private void parseException(int index, char symbol) throws MojangsonParseException { + throw new MojangsonParseException("Index: " + index + ", symbol: \'" + symbol + "\'", MojangsonParseException.ParseExceptionReason.UNEXPECTED_SYMBOL); + } +} diff --git a/src/main/java/ca/momothereal/mojangson/value/MojangsonString.java b/src/main/java/ca/momothereal/mojangson/value/MojangsonString.java new file mode 100644 index 00000000..e7e636e2 --- /dev/null +++ b/src/main/java/ca/momothereal/mojangson/value/MojangsonString.java @@ -0,0 +1,46 @@ +package ca.momothereal.mojangson.value; + +import ca.momothereal.mojangson.MojangsonToken; +import ca.momothereal.mojangson.ex.MojangsonParseException; + +public class MojangsonString implements MojangsonValue { + private String value; + + public MojangsonString() { + + } + + public MojangsonString(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public void write(StringBuilder builder) { + builder.append(MojangsonToken.STRING_QUOTES).append(value).append(MojangsonToken.STRING_QUOTES); + } + + @Override + public Class getValueClass() { + return String.class; + } + + @Override + public void read(String string) throws MojangsonParseException { + Character lastChar = string.charAt(string.length() - 1); + Character firstChar = string.charAt(0); + + if (firstChar == MojangsonToken.STRING_QUOTES.getSymbol() && lastChar == MojangsonToken.STRING_QUOTES.getSymbol()) { + value = string.substring(1, string.length() - 1); + } else { + value = string; + } + } +} diff --git a/src/main/java/ca/momothereal/mojangson/value/MojangsonValue.java b/src/main/java/ca/momothereal/mojangson/value/MojangsonValue.java new file mode 100644 index 00000000..db76e9cf --- /dev/null +++ b/src/main/java/ca/momothereal/mojangson/value/MojangsonValue.java @@ -0,0 +1,36 @@ +package ca.momothereal.mojangson.value; + +import ca.momothereal.mojangson.ex.MojangsonParseException; + +/** + * Represents a value inside a compound or array. + * @param The type of value this MojangsonValue holds + */ +public interface MojangsonValue { + + /** + * Writes the value to a StringBuilder buffer. + * @param builder The buffer to write to + */ + void write(StringBuilder builder); + + /** + * Parses and updates the current value to the given string representation + * @param string The string representation of the value + * @throws MojangsonParseException if the given value cannot be parsed + */ + void read(String string) throws MojangsonParseException; + + /** + * Gets the current literal value + * @return The current literal value of the MojangsonValue + */ + T getValue(); + + /** + * Gets the literal value's class + * @return The literal value's class + */ + Class getValueClass(); + +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/MovementValidator.java b/src/main/java/me/totalfreedom/totalfreedommod/MovementValidator.java index 5b9854b5..5b84bdca 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/MovementValidator.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/MovementValidator.java @@ -1,11 +1,24 @@ package me.totalfreedom.totalfreedommod; +import ca.momothereal.mojangson.ex.MojangsonParseException; +import ca.momothereal.mojangson.value.MojangsonCompound; +import ca.momothereal.mojangson.value.MojangsonValue; +import net.minecraft.server.v1_12_R1.NBTTagCompound; +import net.minecraft.server.v1_12_R1.NBTTagList; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.craftbukkit.v1_12_R1.inventory.CraftItemStack; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerItemHeldEvent; import org.bukkit.event.player.PlayerLoginEvent; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerTeleportEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.List; +import java.util.Objects; public class MovementValidator extends FreedomService { @@ -48,6 +61,43 @@ public class MovementValidator extends FreedomService event.setCancelled(true); player.teleport(player.getWorld().getSpawnLocation()); } + + if (exploitItem(event.getPlayer().getInventory().getHelmet())) + { + event.getPlayer().getInventory().setHelmet(new ItemStack(Material.AIR)); + event.getPlayer().sendMessage(ChatColor.RED + "An item with both negative infinity and positive infinity attributes was cleared from your helmet slot."); + event.setCancelled(true); + } + if (exploitItem(event.getPlayer().getInventory().getBoots())) + { + event.getPlayer().getInventory().setBoots(new ItemStack(Material.AIR)); + event.getPlayer().sendMessage(ChatColor.RED + "An item with both negative infinity and positive infinity attributes was cleared from your boots slot."); + event.setCancelled(true); + } + if (exploitItem(event.getPlayer().getInventory().getLeggings())) + { + event.getPlayer().getInventory().setLeggings(new ItemStack(Material.AIR)); + event.getPlayer().sendMessage(ChatColor.RED + "An item with both negative infinity and positive infinity attributes was cleared from your leggings slot."); + event.setCancelled(true); + } + if (exploitItem(event.getPlayer().getInventory().getChestplate())) + { + event.getPlayer().getInventory().setChestplate(new ItemStack(Material.AIR)); + event.getPlayer().sendMessage(ChatColor.RED + "An item with both negative infinity and positive infinity attributes was cleared from your chestplate slot."); + event.setCancelled(true); + } + if (exploitItem(event.getPlayer().getInventory().getItemInMainHand())) + { + event.getPlayer().getInventory().setItemInMainHand(new ItemStack(Material.AIR)); + event.getPlayer().sendMessage(ChatColor.RED + "An item with both negative infinity and positive infinity attributes was cleared from your hand."); + event.setCancelled(true); + } + if (exploitItem(event.getPlayer().getInventory().getItemInOffHand())) + { + event.getPlayer().getInventory().setItemInOffHand(new ItemStack(Material.AIR)); + event.getPlayer().sendMessage(ChatColor.RED + "An item with both negative infinity and positive infinity attributes was cleared from your offhand."); + event.setCancelled(true); + } } @EventHandler(priority = EventPriority.HIGH) @@ -62,4 +112,73 @@ public class MovementValidator extends FreedomService } } + @EventHandler + public void onPlayerHoldItem(PlayerItemHeldEvent event) + { + if (exploitItem(event.getPlayer().getInventory().getItemInMainHand())) + { + event.getPlayer().getInventory().setItemInMainHand(new ItemStack(Material.AIR)); + event.getPlayer().sendMessage(ChatColor.RED + "An item with both negative infinity and positive infinity attributes was cleared from your hand."); + } + if (exploitItem(event.getPlayer().getInventory().getItemInOffHand())) + { + event.getPlayer().getInventory().setItemInOffHand(new ItemStack(Material.AIR)); + event.getPlayer().sendMessage(ChatColor.RED + "An item with both negative infinity and positive infinity attributes was cleared from your offhand."); + } + } + + private Boolean exploitItem(ItemStack item) + { + net.minecraft.server.v1_12_R1.ItemStack nmsStack = CraftItemStack.asNMSCopy(item); + NBTTagList modifiers = getAttributeList(nmsStack); + MojangsonCompound compound = new MojangsonCompound(); + boolean foundNegative = false; + boolean foundPositive = false; + try + { + String mod = modifiers.toString(); + String fancy = ("{" + (mod.substring(1, mod.length() - 1).replace("{", "").replace("}", "")) + "}"); + compound.read(fancy); + for (String key : compound.keySet()) + { + if (Objects.equals(key, "Amount")) //null-safe .equals() + { + List values = compound.get(key); + for (MojangsonValue val : values) + { + if (val.getValue().toString().equals("Infinityd")) + { + foundPositive = true; + } + if (val.getValue().toString().equals("-Infinityd")) + { + foundNegative = true; + } + } + } + } + } + catch (MojangsonParseException e) + { + e.printStackTrace(); + } + return foundNegative && foundPositive; + } + + + private NBTTagList getAttributeList(net.minecraft.server.v1_12_R1.ItemStack stack) + { + if (stack.getTag() == null) + { + stack.setTag(new NBTTagCompound()); + } + NBTTagList attr = stack.getTag().getList("AttributeModifiers", 10); + if (attr == null) + { + stack.getTag().set("AttributeModifiers", new NBTTagList()); + } + return stack.getTag().getList("AttributeModifiers", 10); + } } + +