diff --git a/pom.xml b/pom.xml index 2e5246f..1a66a85 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ spigot-repo - https://hub.spigotmc.org/nexus/content/groups/public/ + https://hub.spigotmc.org/nexus/content/repositories/snapshots/ vault-repo @@ -111,7 +111,7 @@ org.bukkit bukkit - 1.14-R0.1-SNAPSHOT + 1.14.4-R0.1-SNAPSHOT provided diff --git a/src/com/dre/brewery/BDistiller.java b/src/com/dre/brewery/BDistiller.java index 595b90b..6f344d7 100644 --- a/src/com/dre/brewery/BDistiller.java +++ b/src/com/dre/brewery/BDistiller.java @@ -60,7 +60,9 @@ public class BDistiller { final int fuel = standInv.getHolder().getFuelLevel(); // Now check if we should bother to track it. - trackedDistillers.put(standBlock, new BDistiller(standBlock, fuel)).start(); + distiller = new BDistiller(standBlock, fuel); + trackedDistillers.put(standBlock, distiller); + distiller.start(); } // Returns a Brew or null for every Slot in the BrewerInventory diff --git a/src/com/dre/brewery/Brew.java b/src/com/dre/brewery/Brew.java index ff76efe..32e9189 100644 --- a/src/com/dre/brewery/Brew.java +++ b/src/com/dre/brewery/Brew.java @@ -16,10 +16,7 @@ import org.bukkit.potion.PotionEffectType; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Nullable; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.File; -import java.io.IOException; +import java.io.*; import java.security.InvalidKeyException; import java.security.SecureRandom; import java.util.ArrayList; @@ -44,6 +41,7 @@ public class Brew { private boolean persistent; // Only for legacy private boolean immutable; // static/immutable potions should not be changed private int lastUpdate; // last update in hours after install time + private boolean needsSave; // There was a change that has not yet been saved public Brew(BIngredients ingredients) { this.ingredients = ingredients; @@ -78,7 +76,7 @@ public class Brew { // returns a Brew by ItemMeta @Nullable public static Brew get(ItemMeta meta) { - if (!meta.hasLore()) return null; + if (!P.useNBT && !meta.hasLore()) return null; Brew brew = load(meta); @@ -96,7 +94,7 @@ public class Brew { if (!item.hasItemMeta()) return null; ItemMeta meta = item.getItemMeta(); - if (!meta.hasLore()) return null; + if (!P.useNBT && !meta.hasLore()) return null; Brew brew = load(meta); @@ -104,7 +102,16 @@ public class Brew { // Load Legacy and convert brew = getFromPotionEffect(((PotionMeta) meta), true); if (brew == null) return null; - new BrewLore(brew, ((PotionMeta) meta)).removeLegacySpacing(); + new BrewLore(brew, (PotionMeta) meta).removeLegacySpacing(); + brew.save(meta); + item.setItemMeta(meta); + } else if (brew != null && brew.needsSave) { + // Brew needs saving from a previous format + P.p.debugLog("Brew needs saving from previous format"); + if (P.useNBT) { + new BrewLore(brew, (PotionMeta) meta).removeLoreData(); + P.p.debugLog("removed Data from Lore"); + } brew.save(meta); item.setItemMeta(meta); } @@ -434,6 +441,14 @@ public class Brew { return unlabeled; } + public boolean needsSave() { + return needsSave; + } + + public void setNeedsSave(boolean needsSave) { + this.needsSave = needsSave; + } + // Set the Static flag, so potion is unchangeable public void setStatic(boolean immutable, ItemStack potion) { this.immutable = immutable; @@ -660,14 +675,25 @@ public class Brew { } private static Brew load(ItemMeta meta) { - LoreLoadStream loreStream; - try { - loreStream = new LoreLoadStream(meta, 0); - } catch (IllegalArgumentException ignored) { - // No Brew data found in Meta - return null; + InputStream itemLoadStream = null; + if (P.useNBT) { + // Try loading the Item Data from PersistentDataContainer + NBTLoadStream nbtStream = new NBTLoadStream(meta); + if (nbtStream.hasData()) { + itemLoadStream = nbtStream; + } } - XORUnscrambleStream unscrambler = new XORUnscrambleStream(new Base91DecoderStream(loreStream), saveSeed, prevSaveSeeds); + if (itemLoadStream == null) { + // If either NBT is not supported or no data was found in NBT, try loading from Lore + try { + itemLoadStream = new Base91DecoderStream(new LoreLoadStream(meta, 0)); + } catch (IllegalArgumentException ignored) { + // No Brew data found in Meta + return null; + } + } + + XORUnscrambleStream unscrambler = new XORUnscrambleStream(itemLoadStream, saveSeed, prevSaveSeeds); try (DataInputStream in = new DataInputStream(unscrambler)) { boolean parityFailed = false; if (in.readByte() != 86) { @@ -678,8 +704,10 @@ public class Brew { byte ver = in.readByte(); switch (ver) { case 1: + unscrambler.start(); brew.loadFromStream(in); + break; default: if (parityFailed) { @@ -689,6 +717,18 @@ public class Brew { } return null; } + + XORUnscrambleStream.SuccessType successType = unscrambler.getSuccessType(); + if (successType == XORUnscrambleStream.SuccessType.PREV_SEED) { + brew.setNeedsSave(true); + } else if (BConfig.enableEncode != (successType == XORUnscrambleStream.SuccessType.MAIN_SEED)) { + // We have either enabled encode and the data was not encoded or the other way round + brew.setNeedsSave(true); + } else if (P.useNBT && itemLoadStream instanceof Base91DecoderStream) { + // We are on a version that supports nbt but the data is still in the lore of the item + // Just save it again so that it gets saved to nbt + brew.setNeedsSave(true); + } return brew; } catch (IOException e) { P.p.errorLog("IO Error while loading Brew"); @@ -723,9 +763,15 @@ public class Brew { ingredients = BIngredients.load(in); } - // Save brew data into meta/lore + // Save brew data into meta: lore/nbt public void save(ItemMeta meta) { - XORScrambleStream scrambler = new XORScrambleStream(new Base91EncoderStream(new LoreSaveStream(meta, 0)), saveSeed); + OutputStream itemSaveStream; + if (P.useNBT) { + itemSaveStream = new NBTSaveStream(meta); + } else { + itemSaveStream = new Base91EncoderStream(new LoreSaveStream(meta, 0)); + } + XORScrambleStream scrambler = new XORScrambleStream(itemSaveStream, saveSeed); try (DataOutputStream out = new DataOutputStream(scrambler)) { out.writeByte(86); // Parity/sanity out.writeByte(1); // Version diff --git a/src/com/dre/brewery/P.java b/src/com/dre/brewery/P.java index 2203109..57e5782 100644 --- a/src/com/dre/brewery/P.java +++ b/src/com/dre/brewery/P.java @@ -9,6 +9,7 @@ import com.dre.brewery.integration.IntegrationListener; import com.dre.brewery.integration.LogBlockBarrel; import com.dre.brewery.listeners.*; import com.dre.brewery.utility.BUtil; +import com.dre.brewery.utility.LegacyUtil; import org.apache.commons.lang.math.NumberUtils; import org.bukkit.Bukkit; import org.bukkit.ChatColor; @@ -22,6 +23,7 @@ public class P extends JavaPlugin { public static P p; public static boolean debug; public static boolean useUUID; + public static boolean useNBT; public static boolean use1_9; public static boolean use1_11; public static boolean use1_13; @@ -51,6 +53,13 @@ public class P extends JavaPlugin { use1_13 = !v.matches("(^|.*[^.\\d])1\\.1[0-2]([^\\d].*|$)") && !v.matches("(^|.*[^.\\d])1\\.[0-9]([^\\d].*|$)"); use1_14 = !v.matches("(^|.*[^.\\d])1\\.1[0-3]([^\\d].*|$)") && !v.matches("(^|.*[^.\\d])1\\.[0-9]([^\\d].*|$)"); + //MC 1.13 uses a different NBT API than the newer versions.. + // We decide here which to use, the new or the old or none at all + P.debug = true; + if (LegacyUtil.initNbt()) { + useNBT = true; + } + //P.p.log("§" + (use1_9 ? "a":"c") + "1.9 " + "§" + (use1_11 ? "a":"c") + "1.11 " + "§" + (use1_13 ? "a":"c") + "1.13 " + "§" + (use1_14 ? "a":"c") + "1.14"); /*long master = new SecureRandom().nextLong(); diff --git a/src/com/dre/brewery/lore/BrewLore.java b/src/com/dre/brewery/lore/BrewLore.java index 0213911..7891db2 100644 --- a/src/com/dre/brewery/lore/BrewLore.java +++ b/src/com/dre/brewery/lore/BrewLore.java @@ -189,12 +189,24 @@ public class BrewLore { } public void removeLegacySpacing() { + if (P.useNBT) { + // Using NBT we don't get the invisible line, so we keep our spacing + return; + } if (lore.size() > 0 && lore.get(0).equals("")) { lore.remove(0); write(); } } + public void removeLoreData() { + int index = BUtil.indexOfStart(lore, LoreSaveStream.IDENTIFIER); + if (index != -1) { + lore.set(index, ""); + write(); + } + } + // True if the PotionMeta has Lore in quality color public static boolean hasColorLore(PotionMeta meta) { if (!meta.hasLore()) return false; diff --git a/src/com/dre/brewery/lore/LoreLoadStream.java b/src/com/dre/brewery/lore/LoreLoadStream.java index 3d73889..4ad7ab2 100644 --- a/src/com/dre/brewery/lore/LoreLoadStream.java +++ b/src/com/dre/brewery/lore/LoreLoadStream.java @@ -8,6 +8,8 @@ import java.util.List; public class LoreLoadStream extends ByteArrayInputStream { + public static final String IDENTIFIER = "§%"; + public LoreLoadStream(ItemMeta meta) throws IllegalArgumentException { this(meta, -1); } @@ -21,12 +23,12 @@ public class LoreLoadStream extends ByteArrayInputStream { List lore = meta.getLore(); if (lineNum >= 0) { String line = lore.get(lineNum); - if (line.startsWith("§%")) { + if (line.startsWith(IDENTIFIER)) { return loreLineToBytes(line); } } for (String line : lore) { - if (line.startsWith("§%")) { + if (line.startsWith(IDENTIFIER)) { return loreLineToBytes(line); } } diff --git a/src/com/dre/brewery/lore/LoreSaveStream.java b/src/com/dre/brewery/lore/LoreSaveStream.java index a895691..de127d3 100644 --- a/src/com/dre/brewery/lore/LoreSaveStream.java +++ b/src/com/dre/brewery/lore/LoreSaveStream.java @@ -10,6 +10,8 @@ import java.util.List; public class LoreSaveStream extends ByteArrayOutputStream { + public static final String IDENTIFIER = "§%"; + private ItemMeta meta; private int line; private boolean flushed = false; @@ -38,7 +40,7 @@ public class LoreSaveStream extends ByteArrayOutputStream { String s = toString(); StringBuilder loreLineBuilder = new StringBuilder((s.length() * 2) + 6); - loreLineBuilder.append("§%"); + loreLineBuilder.append(IDENTIFIER); for (char c : s.toCharArray()) { loreLineBuilder.append('§').append(c); } @@ -50,7 +52,7 @@ public class LoreSaveStream extends ByteArrayOutputStream { } int prev = 0; for (Iterator iterator = lore.iterator(); iterator.hasNext(); ) { - if (iterator.next().startsWith("§%")) { + if (iterator.next().startsWith(IDENTIFIER)) { iterator.remove(); break; } diff --git a/src/com/dre/brewery/lore/NBTLoadStream.java b/src/com/dre/brewery/lore/NBTLoadStream.java new file mode 100644 index 0000000..36eea63 --- /dev/null +++ b/src/com/dre/brewery/lore/NBTLoadStream.java @@ -0,0 +1,28 @@ +package com.dre.brewery.lore; + +import com.dre.brewery.P; +import com.dre.brewery.utility.LegacyUtil; +import org.bukkit.NamespacedKey; +import org.bukkit.inventory.meta.ItemMeta; + +import java.io.ByteArrayInputStream; + +public class NBTLoadStream extends ByteArrayInputStream { + private static final String TAG = "brewdata"; + + public NBTLoadStream(ItemMeta meta) { + super(getNBTBytes(meta)); + } + + private static byte[] getNBTBytes(ItemMeta meta) { + byte[] bytes = LegacyUtil.readBytesItem(meta, new NamespacedKey(P.p, TAG)); + if (bytes == null) { + return new byte[0]; + } + return bytes; + } + + public boolean hasData() { + return count > 0; + } +} diff --git a/src/com/dre/brewery/lore/NBTSaveStream.java b/src/com/dre/brewery/lore/NBTSaveStream.java new file mode 100644 index 0000000..4cfd5a5 --- /dev/null +++ b/src/com/dre/brewery/lore/NBTSaveStream.java @@ -0,0 +1,26 @@ +package com.dre.brewery.lore; + +import com.dre.brewery.P; +import com.dre.brewery.utility.LegacyUtil; +import org.bukkit.NamespacedKey; +import org.bukkit.inventory.meta.ItemMeta; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +public class NBTSaveStream extends ByteArrayOutputStream { + private static final String TAG = "brewdata"; + private final ItemMeta meta; + + public NBTSaveStream(ItemMeta meta) { + super(128); + this.meta = meta; + } + + @Override + public void flush() throws IOException { + super.flush(); + if (size() <= 0) return; + LegacyUtil.writeBytesItem(toByteArray(), meta, new NamespacedKey(P.p, TAG)); + } +} diff --git a/src/com/dre/brewery/lore/XORUnscrambleStream.java b/src/com/dre/brewery/lore/XORUnscrambleStream.java index f409cf3..e418eb1 100644 --- a/src/com/dre/brewery/lore/XORUnscrambleStream.java +++ b/src/com/dre/brewery/lore/XORUnscrambleStream.java @@ -23,6 +23,8 @@ public class XORUnscrambleStream extends FilterInputStream { private boolean markRunning; private boolean markxor; + private SuccessType successType = SuccessType.NONE; + /** * Create a new instance of an XORUnscrambler, unscrambling the given inputstream * @@ -62,19 +64,24 @@ public class XORUnscrambleStream extends FilterInputStream { short id = (short) (in.read() << 8 | in.read()); if (id == 0) { running = false; + successType = SuccessType.UNSCRAMBLED; P.p.debugLog("Unscrambled data"); return; } int parity = in.read(); xorStream = new SeedInputStream(seed ^ id); boolean success = checkParity(parity); - if (success) P.p.debugLog("Using main Seed to unscramble"); + if (success) { + successType = SuccessType.MAIN_SEED; + P.p.debugLog("Using main Seed to unscramble"); + } if (!success && prevSeeds != null) { for (int i = prevSeeds.size() - 1; i >= 0; i--) { seed = prevSeeds.get(i); xorStream = new SeedInputStream(seed ^ id); if (success = checkParity(parity)) { + successType = SuccessType.PREV_SEED; P.p.debugLog("Had to use prevSeed to unscramble"); break; } @@ -118,6 +125,15 @@ public class XORUnscrambleStream extends FilterInputStream { return len; } + /** + * What was used to unscramble the stream, it was already unscrambled, Main Seed, Prev Seed + * + * @return The Type of Seed used to unscramble this, if any + */ + public SuccessType getSuccessType() { + return successType; + } + @SuppressWarnings("ResultOfMethodCallIgnored") @Override public long skip(long n) throws IOException { @@ -163,4 +179,11 @@ public class XORUnscrambleStream extends FilterInputStream { } markRunning = running; } + + public static enum SuccessType { + UNSCRAMBLED, + MAIN_SEED, + PREV_SEED, + NONE; + } } diff --git a/src/com/dre/brewery/utility/LegacyUtil.java b/src/com/dre/brewery/utility/LegacyUtil.java index 37e173b..d981883 100644 --- a/src/com/dre/brewery/utility/LegacyUtil.java +++ b/src/com/dre/brewery/utility/LegacyUtil.java @@ -1,14 +1,11 @@ package com.dre.brewery.utility; import com.dre.brewery.P; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.TreeSpecies; -import org.bukkit.World; +import org.bukkit.*; import org.bukkit.block.Block; import org.bukkit.block.data.BlockData; import org.bukkit.block.data.Levelled; +import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.material.Cauldron; import org.bukkit.material.MaterialData; import org.bukkit.material.Tree; @@ -30,6 +27,8 @@ public class LegacyUtil { private static Method GET_BLOCK_TYPE_ID_AT; private static Method SET_DATA; + public static boolean NewNbtVer; + static { // <= 1.12.2 methods // These will be rarely used @@ -248,4 +247,48 @@ public class LegacyUtil { } } + /** + * MC 1.13 uses a different NBT API than the newer versions.. + * We decide here which to use, the new or the old + * + * @return true if we can use nbt at all + */ + public static boolean initNbt() { + try { + Class.forName("org.bukkit.persistence.PersistentDataContainer"); + NewNbtVer = true; + P.p.debugLog("Using the NEW nbt api"); + return true; + } catch (ClassNotFoundException e) { + try { + Class.forName("org.bukkit.inventory.meta.tags.CustomItemTagContainer"); + NewNbtVer = false; + P.p.debugLog("Using the OLD nbt api"); + return true; + } catch (ClassNotFoundException ex) { + NewNbtVer = false; + P.p.debugLog("No nbt api found, using Lore Save System"); + return false; + } + } + } + + @SuppressWarnings("deprecation") + public static void writeBytesItem(byte[] bytes, ItemMeta meta, NamespacedKey key) { + if (NewNbtVer) { + meta.getPersistentDataContainer().set(key, org.bukkit.persistence.PersistentDataType.BYTE_ARRAY, bytes); + } else { + meta.getCustomTagContainer().setCustomTag(key, org.bukkit.inventory.meta.tags.ItemTagType.BYTE_ARRAY, bytes); + } + } + + @SuppressWarnings("deprecation") + public static byte[] readBytesItem(ItemMeta meta, NamespacedKey key) { + if (NewNbtVer) { + return meta.getPersistentDataContainer().get(key, org.bukkit.persistence.PersistentDataType.BYTE_ARRAY); + } else { + return meta.getCustomTagContainer().getCustomTag(key, org.bukkit.inventory.meta.tags.ItemTagType.BYTE_ARRAY); + } + } + }