From 6d5f42b2a5e5291437444ad9be7d012200f48be7 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Sat, 21 Sep 2019 16:14:58 +0200 Subject: [PATCH] Added ReflectionStorage (mostly version-independent) (Started on 2019.08.29.) This means it supports 1.14.4 It automatically falls back to the previous storage if the newer version is not found --- pom.xml | 4 +- .../limitedcreative/ModInventories.java | 276 +++++++++--------- .../inventories/store/ReflectionStorage.java | 217 ++++++++++++++ 3 files changed, 355 insertions(+), 142 deletions(-) create mode 100644 src/main/java/de/jaschastarke/minecraft/limitedcreative/inventories/store/ReflectionStorage.java diff --git a/pom.xml b/pom.xml index 0384d7a..48dc169 100644 --- a/pom.xml +++ b/pom.xml @@ -159,8 +159,8 @@ maven-compiler-plugin 2.3.2 - 1.6 - 1.6 + 1.8 + 1.8 de.jaschastarke.maven.AnnotationProcessor diff --git a/src/main/java/de/jaschastarke/minecraft/limitedcreative/ModInventories.java b/src/main/java/de/jaschastarke/minecraft/limitedcreative/ModInventories.java index ae3869f..80680d8 100644 --- a/src/main/java/de/jaschastarke/minecraft/limitedcreative/ModInventories.java +++ b/src/main/java/de/jaschastarke/minecraft/limitedcreative/ModInventories.java @@ -1,140 +1,136 @@ -package de.jaschastarke.minecraft.limitedcreative; - -import java.io.File; -import java.util.Map; -import java.util.WeakHashMap; - -import org.bukkit.GameMode; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; - -import de.jaschastarke.bukkit.lib.CoreModule; -import de.jaschastarke.minecraft.limitedcreative.inventories.ArmoryConfig; -import de.jaschastarke.minecraft.limitedcreative.inventories.Inventory; -import de.jaschastarke.minecraft.limitedcreative.inventories.InventoryConfig; -import de.jaschastarke.minecraft.limitedcreative.inventories.InventoryPermissions; -import de.jaschastarke.minecraft.limitedcreative.inventories.PlayerListener; -import de.jaschastarke.minecraft.limitedcreative.inventories.store.InvYamlStorage; -import de.jaschastarke.minecraft.limitedcreative.inventories.store.PlayerInventoryStorage; -import de.jaschastarke.modularize.IModule; -import de.jaschastarke.modularize.ModuleEntry; - -public class ModInventories extends CoreModule { - protected PlayerInventoryStorage storage; - protected Map inventories; - protected InventoryConfig config; - protected ArmoryConfig armor_config; - - public ModInventories(LimitedCreative plugin) { - super(plugin); - } - @Override - public String getName() { - return "Inventory"; - } - - @SuppressWarnings("deprecation") - @Override - public void initialize(ModuleEntry entry) { - super.initialize(entry); - listeners.addListener(new PlayerListener(this)); - config = plugin.getPluginConfig().registerSection(new InventoryConfig(this, entry)); - armor_config = config.registerSection(new ArmoryConfig(this)); - - if (Hooks.isAuthMePresent()) { - addModule(new de.jaschastarke.minecraft.limitedcreative.inventories.AuthMeInventories(plugin, this)); - } - String incomp = Hooks.InventoryIncompatible.test(); - if (config.getEnabled() && incomp != null) { - getLog().warn(plugin.getLocale().trans("inventory.warning.conflict", incomp, this.getName())); - entry.deactivateUsage(); - } - } - @Override - public void onEnable() { - String incomp = Hooks.InventoryIncompatible.test(); - if (incomp != null) { - throw new IllegalAccessError(plugin.getLocale().trans("inventory.warning.conflict", incomp, this.getName())); - } - super.onEnable(); - storage = new InvYamlStorage(this, new File(plugin.getDataFolder(), config.getFolder())); - inventories = new WeakHashMap(); - getLog().info(plugin.getLocale().trans("basic.loaded.module")); - } - public InventoryConfig getConfig() { - return config; - } - public ArmoryConfig getArmorConfig() { - return armor_config; - } - - public PlayerInventoryStorage getStorage() { - return storage; - } - - public Inventory getInventory(Player player) { - if (inventories.containsKey(player)) { - return inventories.get(player); - } else { - Inventory inv = new Inventory(storage, player); - inventories.put(player, inv); - return inv; - } - } - - public void onSetGameMode(Player player, GameMode gm) { - if (plugin.getPermManager().hasPermission(player, InventoryPermissions.KEEP_INVENTORY)) - return; - player.closeInventory(); - - GameMode cgm = player.getGameMode(); - if (gm == GameMode.ADVENTURE && !config.getSeparateAdventure()) - gm = GameMode.SURVIVAL; - else if (gm == GameMode.SPECTATOR) - gm = GameMode.CREATIVE; - if (cgm == GameMode.ADVENTURE && !config.getSeparateAdventure()) - cgm = GameMode.SURVIVAL; - else if (cgm == GameMode.SPECTATOR) - cgm = GameMode.CREATIVE; - - if (gm != cgm) { - if (gm != GameMode.CREATIVE || config.getStoreCreative()) { - getInventory(player).save(cgm); - } - if (gm == GameMode.CREATIVE) { - if (config.getStoreCreative() && getInventory(player).isStored(GameMode.CREATIVE)) { - getInventory(player).load(GameMode.CREATIVE); - } else { - getInventory(player).clear(); - } - setCreativeArmor(player); - } else if (gm == GameMode.SURVIVAL) { - if (getInventory(player).isStored(GameMode.SURVIVAL)) - getInventory(player).load(GameMode.SURVIVAL); - } else if (gm == GameMode.ADVENTURE) { - if (getInventory(player).isStored(GameMode.ADVENTURE)) - getInventory(player).load(GameMode.ADVENTURE); - else - getInventory(player).clear(); - } - } - } - - public void setCreativeArmor(Player player) { - if (!getPlugin().getPermManager().hasPermission(player, InventoryPermissions.BYPASS_CREATIVE_ARMOR)) { - Map armor = armor_config.getCreativeArmor(); - if (armor != null) { - ItemStack[] is = new ItemStack[4]; - if (armor.containsKey("feet")) - is[0] = armor.get("feet"); - if (armor.containsKey("legs")) - is[1] = armor.get("legs"); - if (armor.containsKey("chest")) - is[2] = armor.get("chest"); - if (armor.containsKey("head")) - is[3] = armor.get("head"); - player.getInventory().setArmorContents(is); - } - } - } -} +package de.jaschastarke.minecraft.limitedcreative; + +import de.jaschastarke.bukkit.lib.CoreModule; +import de.jaschastarke.minecraft.limitedcreative.inventories.*; +import de.jaschastarke.minecraft.limitedcreative.inventories.store.PlayerInventoryStorage; +import de.jaschastarke.minecraft.limitedcreative.inventories.store.ReflectionStorage; +import de.jaschastarke.modularize.IModule; +import de.jaschastarke.modularize.ModuleEntry; +import org.bukkit.GameMode; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.io.File; +import java.util.Map; +import java.util.WeakHashMap; + +public class ModInventories extends CoreModule { + protected PlayerInventoryStorage storage; + protected Map inventories; + protected InventoryConfig config; + protected ArmoryConfig armor_config; + + public ModInventories(LimitedCreative plugin) { + super(plugin); + } + @Override + public String getName() { + return "Inventory"; + } + + @SuppressWarnings("deprecation") + @Override + public void initialize(ModuleEntry entry) { + super.initialize(entry); + listeners.addListener(new PlayerListener(this)); + config = plugin.getPluginConfig().registerSection(new InventoryConfig(this, entry)); + armor_config = config.registerSection(new ArmoryConfig(this)); + + if (Hooks.isAuthMePresent()) { + addModule(new de.jaschastarke.minecraft.limitedcreative.inventories.AuthMeInventories(plugin, this)); + } + String incomp = Hooks.InventoryIncompatible.test(); + if (config.getEnabled() && incomp != null) { + getLog().warn(plugin.getLocale().trans("inventory.warning.conflict", incomp, this.getName())); + entry.deactivateUsage(); + } + } + @Override + public void onEnable() { + String incomp = Hooks.InventoryIncompatible.test(); + if (incomp != null) { + throw new IllegalAccessError(plugin.getLocale().trans("inventory.warning.conflict", incomp, this.getName())); + } + super.onEnable(); + //storage = new InvYamlStorage(this, new File(plugin.getDataFolder(), config.getFolder())); + storage = new ReflectionStorage(this, new File(plugin.getDataFolder(), config.getFolder())); + inventories = new WeakHashMap(); + getLog().info(plugin.getLocale().trans("basic.loaded.module")); + } + public InventoryConfig getConfig() { + return config; + } + public ArmoryConfig getArmorConfig() { + return armor_config; + } + + public PlayerInventoryStorage getStorage() { + return storage; + } + + public Inventory getInventory(Player player) { + if (inventories.containsKey(player)) { + return inventories.get(player); + } else { + Inventory inv = new Inventory(storage, player); + inventories.put(player, inv); + return inv; + } + } + + public void onSetGameMode(Player player, GameMode gm) { + if (plugin.getPermManager().hasPermission(player, InventoryPermissions.KEEP_INVENTORY)) + return; + player.closeInventory(); + + GameMode cgm = player.getGameMode(); + if (gm == GameMode.ADVENTURE && !config.getSeparateAdventure()) + gm = GameMode.SURVIVAL; + else if (gm == GameMode.SPECTATOR) + gm = GameMode.CREATIVE; + if (cgm == GameMode.ADVENTURE && !config.getSeparateAdventure()) + cgm = GameMode.SURVIVAL; + else if (cgm == GameMode.SPECTATOR) + cgm = GameMode.CREATIVE; + + if (gm != cgm) { + if (gm != GameMode.CREATIVE || config.getStoreCreative()) { + getInventory(player).save(cgm); + } + if (gm == GameMode.CREATIVE) { + if (config.getStoreCreative() && getInventory(player).isStored(GameMode.CREATIVE)) { + getInventory(player).load(GameMode.CREATIVE); + } else { + getInventory(player).clear(); + } + setCreativeArmor(player); + } else if (gm == GameMode.SURVIVAL) { + if (getInventory(player).isStored(GameMode.SURVIVAL)) + getInventory(player).load(GameMode.SURVIVAL); + } else if (gm == GameMode.ADVENTURE) { + if (getInventory(player).isStored(GameMode.ADVENTURE)) + getInventory(player).load(GameMode.ADVENTURE); + else + getInventory(player).clear(); + } + } + } + + public void setCreativeArmor(Player player) { + if (!getPlugin().getPermManager().hasPermission(player, InventoryPermissions.BYPASS_CREATIVE_ARMOR)) { + Map armor = armor_config.getCreativeArmor(); + if (armor != null) { + ItemStack[] is = new ItemStack[4]; + if (armor.containsKey("feet")) + is[0] = armor.get("feet"); + if (armor.containsKey("legs")) + is[1] = armor.get("legs"); + if (armor.containsKey("chest")) + is[2] = armor.get("chest"); + if (armor.containsKey("head")) + is[3] = armor.get("head"); + player.getInventory().setArmorContents(is); + } + } + } +} diff --git a/src/main/java/de/jaschastarke/minecraft/limitedcreative/inventories/store/ReflectionStorage.java b/src/main/java/de/jaschastarke/minecraft/limitedcreative/inventories/store/ReflectionStorage.java new file mode 100644 index 0000000..2d434a5 --- /dev/null +++ b/src/main/java/de/jaschastarke/minecraft/limitedcreative/inventories/store/ReflectionStorage.java @@ -0,0 +1,217 @@ +package de.jaschastarke.minecraft.limitedcreative.inventories.store; + +import de.jaschastarke.bukkit.lib.CoreModule; +import de.jaschastarke.bukkit.lib.ModuleLogger; +import de.jaschastarke.minecraft.limitedcreative.inventories.Inventory; +import org.bukkit.configuration.Configuration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; + +import java.io.*; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Base64; +import java.util.Objects; +import java.util.UUID; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public class ReflectionStorage extends PlayerInventoryStorage { + private CoreModule mod; + private File dir; + private String nms; + private InvYamlStorage yamlStorage; + + public ReflectionStorage(CoreModule mod, File file) { + this.mod = mod; + dir = file; + yamlStorage = new InvYamlStorage(mod, file); + } + + @Override + public ModuleLogger getLog() { + return mod.getLog(); + } + + private File getFile(UUID uuid) { + return new File(dir, uuid.toString() + "_ref.yml"); + } + + private Object getInventory(Player player) throws Exception { + org.bukkit.inventory.Inventory inv = player.getInventory(); + if (getInventory == null) + getInventory = inv.getClass().getMethod("getInventory"); + Object handle = getInventory.invoke(inv); + if (nms == null) + nms = handle.getClass().getPackage().getName(); + return handle; + } + + @Override + public void store(Inventory pinv, Inventory.Target target) { + try { + File f = getFile(pinv.getPlayer().getUniqueId()); + YamlConfiguration config = YamlConfiguration.loadConfiguration(f); + config.set(target.name(), serialize(getInventory(pinv.getPlayer()))); + config.save(f); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private Method getInventory; + + @Override + public void load(Inventory pinv, Inventory.Target target) { + Player player = pinv.getPlayer(); + try { + File f = getFile(player.getUniqueId()); + if (!f.exists()) { //If not found use the older file(s) + yamlStorage.load(pinv, target); + return; + } + //String content = new String(Files.readAllBytes(f.toPath())); + Configuration config = YamlConfiguration.loadConfiguration(f); + String content = config.getString(target.name()); + if (content == null) { + yamlStorage.load(pinv, target); + return; + } + setFromSerialized(getInventory(player), content); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public void remove(Inventory pinv, Inventory.Target target) { + File f = getFile(pinv.getPlayer().getUniqueId()); + if (!f.exists()) return; + Configuration config = YamlConfiguration.loadConfiguration(f); + config.set(target.name(), null); + } + + @Override + public boolean contains(Inventory pinv, Inventory.Target target) { + File f = getFile(pinv.getPlayer().getUniqueId()); + if (!f.exists()) return yamlStorage.contains(pinv, target); + Configuration config = YamlConfiguration.loadConfiguration(f); + return config.contains(target.name()) || yamlStorage.contains(pinv, target); + } + + //Based on iie's per-world inventory + //https://github.com/TBMCPlugins/iiePerWorldInventory/blob/master/src/buttondevteam/perworld/serializers/inventory.java + + private Method save; + private Class nbtcl; + private Method nbtcsta; + private Class nbtcstcl; + + //SERIALIZE ITEMSTACK + private String serializeItemStack(Object itemStack) throws Exception { + if (nbtcl == null) + nbtcl = Class.forName(nms + ".NBTTagCompound"); + if (save == null) + save = itemStack.getClass().getMethod("save", nbtcl); + if (nbtcstcl == null) + nbtcstcl = Class.forName(nms + ".NBTCompressedStreamTools"); + if (nbtcsta == null) + nbtcsta = nbtcstcl.getMethod("a", nbtcl, OutputStream.class); + Object tag = save.invoke(itemStack, nbtcl.newInstance()); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + + nbtcsta.invoke(null, tag, outputStream); + + return Base64.getEncoder().encodeToString(outputStream.toByteArray()); + } + + + private Method nbtcstaa; + private Function createStack; + + //DESERIALIZE ITEMSTACK + private Object deserializeItemStack(String itemStackString) throws Exception { + if (nbtcstcl == null) + nbtcstcl = Class.forName(nms + ".NBTCompressedStreamTools"); + if (nbtcstaa == null) + nbtcstaa = nbtcstcl.getMethod("a", InputStream.class); + if (nbtcl == null) + nbtcl = Class.forName(nms + ".NBTTagCompound"); + try { + if (createStack == null) { + final Method a = iscl.getMethod("a", nbtcl); + createStack = nbt -> { + try { + return a.invoke(null, nbt); + } catch (Exception e) { + throw new RuntimeException(e); + } + }; + } + } catch (NoSuchMethodException ex) { //It can only get here inside the if + final Constructor constructor = iscl.getConstructor(nbtcl); + createStack = nbt -> { + try { + return constructor.newInstance(nbt); + } catch (Exception e) { + throw new RuntimeException(e); + } + }; + } + ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64.getDecoder().decode(itemStackString)); + + Object nbtTagCompound = nbtcstaa.invoke(null, inputStream); + return createStack.apply(nbtTagCompound); + } + + private Method getSize; + private Method getItem; + + //SERIALIZE INVENTORY + private String serialize(Object invInventory) throws Exception { + if (getSize == null) + getSize = invInventory.getClass().getMethod("getSize"); + if (getItem == null) + getItem = invInventory.getClass().getMethod("getItem", int.class); + return IntStream.range(0, (int) getSize.invoke(invInventory)) + .mapToObj(s -> { + try { + //nms ItemStack + Object i = getItem.invoke(invInventory, s); + return Objects.isNull(i) ? null : s + "#" + serializeItemStack(i); + } catch (Exception e) { + throw new RuntimeException(e); + } + }) + .filter(Objects::nonNull) + .collect(Collectors.joining(";")); + } + + private Method clear; + private Method setItem; + private Class iscl; + + //SET INVENTORY FROM SERIALIZED + private void setFromSerialized(Object invInventory, String invString) throws Exception { + if (clear == null) + clear = invInventory.getClass().getMethod("clear"); + if (iscl == null) + iscl = Class.forName(nms + ".ItemStack"); + if (setItem == null) + setItem = invInventory.getClass().getMethod("setItem", int.class, iscl); + clear.invoke(invInventory); //clear inventory + if (invString != null && !invString.isEmpty()) + Arrays.asList(invString.split(";")) + .parallelStream() + .forEach(s -> { + String[] e = s.split("#"); + try { + setItem.invoke(invInventory, Integer.parseInt(e[0]), deserializeItemStack(e[1])); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + }); + } +}