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);
+ }
+ }
+
}