diff --git a/pom.xml b/pom.xml
index 09a7613..2e5246f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -232,6 +232,12 @@
1.5
compile
+
+ org.jetbrains
+ annotations
+ 16.0.2
+ compile
+
diff --git a/resources/config/v12/de/config.yml b/resources/config/v12/de/config.yml
index 8be0305..ac799da 100644
--- a/resources/config/v12/de/config.yml
+++ b/resources/config/v12/de/config.yml
@@ -3,7 +3,6 @@
# -- Verschiedene Einstellungen --
# Standardeinstellungen sind in [] angegeben
-# Löschen einzelner Einstellungen deaktiviert sie
# Sprachedatei die genutzt werden sollte (befindet sich in plugins/Brewery/languages)
language: de
@@ -57,8 +56,12 @@ openLargeBarrelEverywhere: true
# Wie viele Brewery Getränke in die Minecraft Fässer getan werden können [6]
maxBrewsInMCBarrels: 6
-# In den Serverlog loggen was der Spieler tatsächlich geschrieben hat, bevor seine Worte verändert wurden [false]
-logRealChat: false
+# Benutzte Zutaten und andere Brau-Daten werden in allen Brewery Tränken gespeichert. Um zu verhindern,
+# dass gehackte clients diese Daten auslesen um Rezepte herauszufinden, können diese encodiert werden.
+# Einziger Nachteil: Tränke können nur auf Servern mit dem gleichen encodeKey benutzt werden.
+# Dies kann also aktiviert werden um Rezept-cheating schwerer zu machen, aber keine Tränke per World Download, Schematic, o.ä. geteilt werden. [false]
+enableEncode: false
+encodeKey: 0
# Aktiviert das Suchen nach Updates für Brewery mit der curseforge api [true]
# Wenn ein Update gefunden wurde, wird dies bei Serverstart im log angezeigt, sowie OPs benachrichtigt
@@ -286,6 +289,9 @@ useLogBlock: true
# Unten kann noch eingestellt werden wie und was verändert wird
enableChatDistortion: true
+# In den Serverlog loggen was der Spieler tatsächlich geschrieben hat, bevor seine Worte verändert wurden [false]
+logRealChat: false
+
# Text nach den angegebenen Kommandos wird bei Trunkenheit ebenfalls Verändert (Liste) [- /gl]
distortCommands:
- /gl
diff --git a/resources/config/v12/en/config.yml b/resources/config/v12/en/config.yml
index 38eadb4..1845a69 100644
--- a/resources/config/v12/en/config.yml
+++ b/resources/config/v12/en/config.yml
@@ -3,7 +3,6 @@
# -- Settings --
# Defaults are written in []
-# Deleting of single settings disables them
# Languagefile to be used (found in plugins/Brewery/languages)
language: en
@@ -57,6 +56,14 @@ openLargeBarrelEverywhere: true
# How many Brewery drinks can be put into the Minecraft barrels [6]
maxBrewsInMCBarrels: 6
+# The used Ingredients and other brewing-data is saved to all Brewery Items. To prevent
+# hacked clients from reading what exactly was used to brew an item, the data can be encoded/scrambled.
+# This is a fast process to stop players from hacking out recipes, once they get hold of a brew.
+# Only drawback: brew items can only be used on another server with the same encodeKey.
+# So enable this if you want to make recipe cheating harder, but don't share any brews by world download, schematics, or other means. [false]
+enableEncode: false
+encodeKey: 0
+
# Enable checking for Updates, Checks the curseforge api for updates to Brewery [true]
# If an Update is found a Message is logged on Server-start and displayed to OPs joining the game
updateCheck: true
diff --git a/resources/config/v12/fr/config.yml b/resources/config/v12/fr/config.yml
index e7673d6..82cd49e 100644
--- a/resources/config/v12/fr/config.yml
+++ b/resources/config/v12/fr/config.yml
@@ -3,7 +3,6 @@
# -- Paramètres --
# Les paramètres par défaut sont entre []
-# Supprimer un paramètre le désactive
# Fichier de langage utilisé (trouvable dans plugins/Brewery/languages)
language: fr
@@ -57,6 +56,14 @@ openLargeBarrelEverywhere: true
# How many Brewery drinks can be put into the Minecraft barrels [6]
maxBrewsInMCBarrels: 6
+# The used Ingredients and other brewing-data is saved to all Brewery Items. To prevent
+# hacked clients from reading what exactly was used to brew an item, the data can be encoded/scrambled.
+# This is a fast process to stop players from hacking out recipes, once they get hold of a brew.
+# Only drawback: brew items can only be used on another server with the same encodeKey.
+# So enable this if you want to make recipe cheating harder, but don't share any brews by world download, schematics, or other means. [false]
+enableEncode: false
+encodeKey: 0
+
# Enable checking for Updates, Checks the curseforge api for updates to Brewery [true]
# If an Update is found a Message is logged on Server-start and displayed to OPs joining the game
updateCheck: true
diff --git a/resources/config/v12/it/config.yml b/resources/config/v12/it/config.yml
index 70af3f2..335cf03 100644
--- a/resources/config/v12/it/config.yml
+++ b/resources/config/v12/it/config.yml
@@ -3,7 +3,6 @@
# -- Opzioni --
# I valori di default sono scritti fra []
-# Cancellare una voce la disabilita
# Lingua da usare (fra quelle in plugins/Brewery/languages)
language: it
@@ -57,6 +56,14 @@ openLargeBarrelEverywhere: true
# How many Brewery drinks can be put into the Minecraft barrels [6]
maxBrewsInMCBarrels: 6
+# The used Ingredients and other brewing-data is saved to all Brewery Items. To prevent
+# hacked clients from reading what exactly was used to brew an item, the data can be encoded/scrambled.
+# This is a fast process to stop players from hacking out recipes, once they get hold of a brew.
+# Only drawback: brew items can only be used on another server with the same encodeKey.
+# So enable this if you want to make recipe cheating harder, but don't share any brews by world download, schematics, or other means. [false]
+enableEncode: false
+encodeKey: 0
+
# Abilita il controllo degli aggiornamenti, controlla l'API di CurseForge per eventuali aggiornamenti di Brewery [true]
# Se quando un aggiornamento viene trovato un messaggio è loggato e mostrato agli OPs quando entrano in gioco.
updateCheck: true
diff --git a/resources/config/v13/de/config.yml b/resources/config/v13/de/config.yml
index f46df44..681e8fb 100644
--- a/resources/config/v13/de/config.yml
+++ b/resources/config/v13/de/config.yml
@@ -3,7 +3,6 @@
# -- Verschiedene Einstellungen --
# Standardeinstellungen sind in [] angegeben
-# Löschen einzelner Einstellungen deaktiviert sie
# Sprachedatei die genutzt werden sollte (befindet sich in plugins/Brewery/languages)
language: de
@@ -57,8 +56,12 @@ openLargeBarrelEverywhere: true
# Wie viele Brewery Getränke in die Minecraft Fässer getan werden können [6]
maxBrewsInMCBarrels: 6
-# In den Serverlog loggen was der Spieler tatsächlich geschrieben hat, bevor seine Worte verändert wurden [false]
-logRealChat: false
+# Benutzte Zutaten und andere Brau-Daten werden in allen Brewery Tränken gespeichert. Um zu verhindern,
+# dass gehackte clients diese Daten auslesen um Rezepte herauszufinden, können diese encodiert werden.
+# Einziger Nachteil: Tränke können nur auf Servern mit dem gleichen encodeKey benutzt werden.
+# Dies kann also aktiviert werden um Rezept-cheating schwerer zu machen, aber keine Tränke per World Download, Schematic, o.ä. geteilt werden. [false]
+enableEncode: false
+encodeKey: 0
# Aktiviert das Suchen nach Updates für Brewery mit der curseforge api [true]
# Wenn ein Update gefunden wurde, wird dies bei Serverstart im log angezeigt, sowie OPs benachrichtigt
@@ -277,6 +280,9 @@ useLogBlock: true
# Unten kann noch eingestellt werden wie und was verändert wird
enableChatDistortion: true
+# In den Serverlog loggen was der Spieler tatsächlich geschrieben hat, bevor seine Worte verändert wurden [false]
+logRealChat: false
+
# Text nach den angegebenen Kommandos wird bei Trunkenheit ebenfalls Verändert (Liste) [- /gl]
distortCommands:
- /gl
diff --git a/resources/config/v13/en/config.yml b/resources/config/v13/en/config.yml
index cc68467..376d920 100644
--- a/resources/config/v13/en/config.yml
+++ b/resources/config/v13/en/config.yml
@@ -3,7 +3,6 @@
# -- Settings --
# Defaults are written in []
-# Deleting of single settings disables them
# Languagefile to be used (found in plugins/Brewery/languages)
language: en
@@ -57,6 +56,14 @@ openLargeBarrelEverywhere: true
# How many Brewery drinks can be put into the Minecraft barrels [6]
maxBrewsInMCBarrels: 6
+# The used Ingredients and other brewing-data is saved to all Brewery Items. To prevent
+# hacked clients from reading what exactly was used to brew an item, the data can be encoded/scrambled.
+# This is a fast process to stop players from hacking out recipes, once they get hold of a brew.
+# Only drawback: brew items can only be used on another server with the same encodeKey.
+# So enable this if you want to make recipe cheating harder, but don't share any brews by world download, schematics, or other means. [false]
+enableEncode: false
+encodeKey: 0
+
# Enable checking for Updates, Checks the curseforge api for updates to Brewery [true]
# If an Update is found a Message is logged on Server-start and displayed to OPs joining the game
updateCheck: true
diff --git a/resources/config/v13/fr/config.yml b/resources/config/v13/fr/config.yml
index cc000f0..98f15cf 100644
--- a/resources/config/v13/fr/config.yml
+++ b/resources/config/v13/fr/config.yml
@@ -3,7 +3,6 @@
# -- Paramètres --
# Les paramètres par défaut sont entre []
-# Supprimer un paramètre le désactive
# Fichier de langage utilisé (trouvable dans plugins/Brewery/languages)
language: fr
@@ -57,6 +56,14 @@ openLargeBarrelEverywhere: true
# How many Brewery drinks can be put into the Minecraft barrels [6]
maxBrewsInMCBarrels: 6
+# The used Ingredients and other brewing-data is saved to all Brewery Items. To prevent
+# hacked clients from reading what exactly was used to brew an item, the data can be encoded/scrambled.
+# This is a fast process to stop players from hacking out recipes, once they get hold of a brew.
+# Only drawback: brew items can only be used on another server with the same encodeKey.
+# So enable this if you want to make recipe cheating harder, but don't share any brews by world download, schematics, or other means. [false]
+enableEncode: false
+encodeKey: 0
+
# Enable checking for Updates, Checks the curseforge api for updates to Brewery [true]
# If an Update is found a Message is logged on Server-start and displayed to OPs joining the game
updateCheck: true
diff --git a/resources/config/v13/it/config.yml b/resources/config/v13/it/config.yml
index 55649f8..64895cd 100644
--- a/resources/config/v13/it/config.yml
+++ b/resources/config/v13/it/config.yml
@@ -3,7 +3,6 @@
# -- Opzioni --
# I valori di default sono scritti fra []
-# Cancellare una voce la disabilita
# Lingua da usare (fra quelle in plugins/Brewery/languages)
language: it
@@ -57,6 +56,14 @@ openLargeBarrelEverywhere: true
# How many Brewery drinks can be put into the Minecraft barrels [6]
maxBrewsInMCBarrels: 6
+# The used Ingredients and other brewing-data is saved to all Brewery Items. To prevent
+# hacked clients from reading what exactly was used to brew an item, the data can be encoded/scrambled.
+# This is a fast process to stop players from hacking out recipes, once they get hold of a brew.
+# Only drawback: brew items can only be used on another server with the same encodeKey.
+# So enable this if you want to make recipe cheating harder, but don't share any brews by world download, schematics, or other means. [false]
+enableEncode: false
+encodeKey: 0
+
# Abilita il controllo degli aggiornamenti, controlla l'API di CurseForge per eventuali aggiornamenti di Brewery [true]
# Se quando un aggiornamento viene trovato un messaggio è loggato e mostrato agli OPs quando entrano in gioco.
updateCheck: true
diff --git a/resources/config/v13/zh/config.yml b/resources/config/v13/zh/config.yml
index ad157f0..b1dfda5 100644
--- a/resources/config/v13/zh/config.yml
+++ b/resources/config/v13/zh/config.yml
@@ -58,6 +58,14 @@ openLargeBarrelEverywhere: false
# MC自带的桶内可以存放多少饮品 [6]
maxBrewsInMCBarrels: 6
+# The used Ingredients and other brewing-data is saved to all Brewery Items. To prevent
+# hacked clients from reading what exactly was used to brew an item, the data can be encoded/scrambled.
+# This is a fast process to stop players from hacking out recipes, once they get hold of a brew.
+# Only drawback: brew items can only be used on another server with the same encodeKey.
+# So enable this if you want to make recipe cheating harder, but don't share any brews by world download, schematics, or other means. [false]
+enableEncode: false
+encodeKey: 0
+
# 是否检查更新.[true]
# 若有更新, 服务端后台与上线时的管理员会收到通知.
updateCheck: true
diff --git a/src/com/dre/brewery/BPlayer.java b/src/com/dre/brewery/BPlayer.java
index 857e57c..7db9de3 100644
--- a/src/com/dre/brewery/BPlayer.java
+++ b/src/com/dre/brewery/BPlayer.java
@@ -21,6 +21,7 @@ import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.util.Vector;
+import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
@@ -53,6 +54,7 @@ public class BPlayer {
players.put(name, this);
}
+ @Nullable
public static BPlayer get(Player player) {
if (!players.isEmpty()) {
return players.get(BUtil.playerString(player));
@@ -61,6 +63,7 @@ public class BPlayer {
}
// This method may be slow and should not be used if not needed
+ @Nullable
public static BPlayer getByName(String playerName) {
if (P.useUUID) {
for (Map.Entry entry : players.entrySet()) {
diff --git a/src/com/dre/brewery/Barrel.java b/src/com/dre/brewery/Barrel.java
index e5cc718..f42bb90 100644
--- a/src/com/dre/brewery/Barrel.java
+++ b/src/com/dre/brewery/Barrel.java
@@ -23,6 +23,7 @@ import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.PotionMeta;
import org.bukkit.scheduler.BukkitRunnable;
+import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
@@ -42,6 +43,11 @@ public class Barrel implements InventoryHolder {
public Barrel(Block spigot, byte signoffset) {
this.spigot = spigot;
+ if (isLarge()) {
+ inventory = P.p.getServer().createInventory(this, 27, P.p.languageReader.get("Etc_Barrel"));
+ } else {
+ inventory = P.p.getServer().createInventory(this, 9, P.p.languageReader.get("Etc_Barrel"));
+ }
body = new BarrelBody(this, signoffset);
}
@@ -163,6 +169,7 @@ public class Barrel implements InventoryHolder {
public void playOpeningSound() {
float randPitch = (float) (Math.random() * 0.1);
Location location = getSpigot().getLocation();
+ if (location.getWorld() == null) return;
if (isLarge()) {
location.getWorld().playSound(location, Sound.BLOCK_CHEST_OPEN, SoundCategory.BLOCKS, 0.4f, 0.55f + randPitch);
//getSpigot().getWorld().playSound(getSpigot().getLocation(), Sound.ITEM_BUCKET_EMPTY, SoundCategory.BLOCKS, 0.5f, 0.6f + randPitch);
@@ -175,6 +182,7 @@ public class Barrel implements InventoryHolder {
public void playClosingSound() {
float randPitch = (float) (Math.random() * 0.1);
Location location = getSpigot().getLocation();
+ if (location.getWorld() == null) return;
if (isLarge()) {
location.getWorld().playSound(location, Sound.BLOCK_BARREL_CLOSE, SoundCategory.BLOCKS, 0.5f, 0.5f + randPitch);
location.getWorld().playSound(location, Sound.ITEM_BUCKET_EMPTY, SoundCategory.BLOCKS, 0.2f, 0.6f + randPitch);
@@ -184,14 +192,17 @@ public class Barrel implements InventoryHolder {
}
@Override
+ @NotNull
public Inventory getInventory() {
return inventory;
}
+ @NotNull
public Block getSpigot() {
return spigot;
}
+ @NotNull
public BarrelBody getBody() {
return body;
}
@@ -239,7 +250,7 @@ public class Barrel implements InventoryHolder {
for (Barrel barrel : barrels) {
if (barrel.body.isSignOfBarrel(signoffset)) {
- if (barrel.body.equals(spigot)) {
+ if (barrel.spigot.equals(spigot)) {
if (barrel.body.getSignoffset() == 0 && signoffset != 0) {
// Barrel has no signOffset even though we clicked a sign, may be old
barrel.body.setSignoffset(signoffset);
@@ -316,7 +327,7 @@ public class Barrel implements InventoryHolder {
P.p.getServer().getPluginManager().callEvent(event);
if (inventory != null) {
- List viewers = new ArrayList(inventory.getViewers());
+ List viewers = new ArrayList<>(inventory.getViewers());
// Copy List to fix ConcModExc
for (HumanEntity viewer : viewers) {
viewer.closeInventory();
@@ -368,7 +379,7 @@ public class Barrel implements InventoryHolder {
// is this a Small barrel?
public boolean isSmall() {
- return !LegacyUtil.isSign(spigot.getType());
+ return LegacyUtil.isSign(spigot.getType());
}
// returns the Sign of a large barrel, the spigot if there is none
diff --git a/src/com/dre/brewery/Brew.java b/src/com/dre/brewery/Brew.java
index df4ee1e..ff76efe 100644
--- a/src/com/dre/brewery/Brew.java
+++ b/src/com/dre/brewery/Brew.java
@@ -2,6 +2,7 @@ package com.dre.brewery;
import com.dre.brewery.api.events.brew.BrewModifyEvent;
import com.dre.brewery.filedata.BConfig;
+import com.dre.brewery.filedata.ConfigUpdater;
import com.dre.brewery.lore.*;
import com.dre.brewery.utility.PotionColor;
import org.bukkit.Material;
@@ -12,20 +13,24 @@ import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.PotionMeta;
import org.bukkit.potion.PotionEffect;
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.security.InvalidKeyException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
public class Brew {
-
// represents the liquid in the brewed Potions
private static long saveSeed;
+ private static List prevSaveSeeds = new ArrayList<>(); // Save Seeds that have been used in the past, stored to decode brews made at that time
public static Map legacyPotions = new HashMap<>();
public static long installTime = System.currentTimeMillis(); // plugin install time in millis after epoch
@@ -71,22 +76,21 @@ public class Brew {
}
// returns a Brew by ItemMeta
+ @Nullable
public static Brew get(ItemMeta meta) {
if (!meta.hasLore()) return null;
- if (meta instanceof PotionMeta && ((PotionMeta) meta).hasCustomEffect(PotionEffectType.REGENERATION)) {
- Brew brew = load(meta);
- if (brew != null) {
- // Load Legacy
- brew = getFromPotionEffect(((PotionMeta) meta), false);
- }
- return brew;
- } else {
- return load(meta);
+ Brew brew = load(meta);
+
+ if (brew == null && meta instanceof PotionMeta && ((PotionMeta) meta).hasCustomEffect(PotionEffectType.REGENERATION)) {
+ // Load Legacy
+ return getFromPotionEffect(((PotionMeta) meta), false);
}
+ return brew;
}
// returns a Brew by ItemStack
+ @Nullable
public static Brew get(ItemStack item) {
if (item.getType() != Material.POTION) return null;
if (!item.hasItemMeta()) return null;
@@ -94,21 +98,17 @@ public class Brew {
ItemMeta meta = item.getItemMeta();
if (!meta.hasLore()) return null;
- if (meta instanceof PotionMeta && ((PotionMeta) meta).hasCustomEffect(PotionEffectType.REGENERATION)) {
- Brew brew = load(meta);
- if (brew != null) {
- ((PotionMeta) meta).removeCustomEffect(PotionEffectType.REGENERATION);
- } else {
- // Load Legacy and convert
- brew = getFromPotionEffect(((PotionMeta) meta), true);
- if (brew == null) return null;
- brew.save(meta);
- }
+ Brew brew = load(meta);
+
+ if (brew == null && meta instanceof PotionMeta && ((PotionMeta) meta).hasCustomEffect(PotionEffectType.REGENERATION)) {
+ // Load Legacy and convert
+ brew = getFromPotionEffect(((PotionMeta) meta), true);
+ if (brew == null) return null;
+ new BrewLore(brew, ((PotionMeta) meta)).removeLegacySpacing();
+ brew.save(meta);
item.setItemMeta(meta);
- return brew;
- } else {
- return load(meta);
}
+ return brew;
}
// Legacy Brew Loading
@@ -117,7 +117,16 @@ public class Brew {
if (effect.getType().equals(PotionEffectType.REGENERATION)) {
if (effect.getDuration() < -1) {
if (remove) {
- return legacyPotions.remove(effect.getDuration());
+ Brew b = legacyPotions.get(effect.getDuration());
+ if (b != null) {
+ potionMeta.removeCustomEffect(PotionEffectType.REGENERATION);
+ if (b.persistent) {
+ return b;
+ } else {
+ return legacyPotions.remove(effect.getDuration());
+ }
+ }
+ return null;
} else {
return legacyPotions.get(effect.getDuration());
}
@@ -612,6 +621,7 @@ public class Brew {
* @param event Set event to true if a BrewModifyEvent type CREATE should be called and may be cancelled. Only then may this method return null
* @return The created Item, null if the Event is cancelled
*/
+ @Contract("_, false -> !null")
public ItemStack createItem(BRecipe recipe, boolean event) {
if (recipe == null) {
recipe = getCurrentRecipe();
@@ -654,9 +664,10 @@ public class Brew {
try {
loreStream = new LoreLoadStream(meta, 0);
} catch (IllegalArgumentException ignored) {
+ // No Brew data found in Meta
return null;
}
- XORUnscrambleStream unscrambler = new XORUnscrambleStream(new Base91DecoderStream(loreStream), saveSeed);
+ XORUnscrambleStream unscrambler = new XORUnscrambleStream(new Base91DecoderStream(loreStream), saveSeed, prevSaveSeeds);
try (DataInputStream in = new DataInputStream(unscrambler)) {
boolean parityFailed = false;
if (in.readByte() != 86) {
@@ -683,7 +694,7 @@ public class Brew {
P.p.errorLog("IO Error while loading Brew");
e.printStackTrace();
} catch (InvalidKeyException e) {
- P.p.errorLog("Failed to load Brew, has the data key 'BrewDataSeed' in the data.yml been changed?");
+ P.p.errorLog("Failed to load Brew, has the data key 'encodeKey' in the config.yml been changed?");
e.printStackTrace();
}
return null;
@@ -718,7 +729,11 @@ public class Brew {
try (DataOutputStream out = new DataOutputStream(scrambler)) {
out.writeByte(86); // Parity/sanity
out.writeByte(1); // Version
- scrambler.start();
+ if (BConfig.enableEncode) {
+ scrambler.start();
+ } else {
+ scrambler.startUnscrambled();
+ }
saveToStream(out);
} catch (IOException e) {
P.p.errorLog("IO Error while saving Brew");
@@ -768,17 +783,33 @@ public class Brew {
ingredients.save(out);
}
- public static void writeSeed(ConfigurationSection section) {
- section.set("BrewDataSeed", saveSeed);
+ public static void loadPrevSeeds(ConfigurationSection section) {
+ if (section.contains("prevSaveSeeds")) {
+ prevSaveSeeds = section.getLongList("prevSaveSeeds");
+ if (!prevSaveSeeds.contains(saveSeed)) {
+ prevSaveSeeds.add(saveSeed);
+ }
+ }
}
- public static void loadSeed(ConfigurationSection section) {
- if (section.contains("BrewDataSeed")) {
- saveSeed = section.getLong("BrewDataSeed");
- } else {
+ public static void writePrevSeeds(ConfigurationSection section) {
+ if (!prevSaveSeeds.isEmpty()) {
+ section.set("prevSaveSeeds", prevSaveSeeds);
+ }
+ }
+
+ public static void loadSeed(ConfigurationSection config, File file) {
+ saveSeed = config.getLong("encodeKey", 0);
+ if (saveSeed == 0) {
while (saveSeed == 0) {
saveSeed = new SecureRandom().nextLong();
}
+ ConfigUpdater updater = new ConfigUpdater(file);
+ updater.setEncodeKey(saveSeed);
+ updater.saveConfig();
+ }
+ if (!prevSaveSeeds.contains(saveSeed)) {
+ prevSaveSeeds.add(saveSeed);
}
}
@@ -793,31 +824,26 @@ public class Brew {
legacyPotions.put(uid, brew);
}
- // remove legacy potiondata from item
+ // remove legacy potiondata for an item
public static void removeLegacy(ItemStack item) {
if (legacyPotions.isEmpty()) return;
if (!item.hasItemMeta()) return;
ItemMeta meta = item.getItemMeta();
if (!(meta instanceof PotionMeta)) return;
- for (PotionEffect effect : ((PotionMeta) meta).getCustomEffects()) {
- if (effect.getType().equals(PotionEffectType.REGENERATION)) {
- if (effect.getDuration() < -1) {
- legacyPotions.remove(effect.getDuration());
- return;
- }
- }
- }
+ getFromPotionEffect(((PotionMeta) meta), true);
}
- public void convertLegacy(ItemStack item) {
+ public void convertPre19(ItemStack item) {
removeLegacy(item);
PotionMeta potionMeta = ((PotionMeta) item.getItemMeta());
if (hasRecipe()) {
BrewLore lore = new BrewLore(this, potionMeta);
lore.removeEffects();
PotionColor.fromString(currentRecipe.getColor()).colorBrew(potionMeta, item, canDistill());
+ lore.removeLegacySpacing();
} else {
PotionColor.GREY.colorBrew(potionMeta, item, canDistill());
+ new BrewLore(this, potionMeta).removeLegacySpacing();
}
save(potionMeta);
item.setItemMeta(potionMeta);
diff --git a/src/com/dre/brewery/api/BreweryApi.java b/src/com/dre/brewery/api/BreweryApi.java
index ca75e72..0a4d536 100644
--- a/src/com/dre/brewery/api/BreweryApi.java
+++ b/src/com/dre/brewery/api/BreweryApi.java
@@ -10,6 +10,7 @@ import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
+import org.jetbrains.annotations.Nullable;
public class BreweryApi {
@@ -46,6 +47,7 @@ public class BreweryApi {
* Checks if item is actually a Brew
* Returns null if item is not a Brew
*/
+ @Nullable
public static Brew getBrew(ItemStack item) {
return Brew.get(item);
}
@@ -56,6 +58,7 @@ public class BreweryApi {
* Checks if meta has a Brew saved
* Returns null if meta is not a Brew
*/
+ @Nullable
public static Brew getBrew(ItemMeta meta) {
return Brew.get(meta);
}
@@ -68,6 +71,7 @@ public class BreweryApi {
* May be any Wood, Fence, Sign that is part of a Barrel
* Returns null if block is not part of a Barrel
*/
+ @Nullable
public static Barrel getBarrel(Block block) {
return Barrel.get(block);
}
@@ -77,6 +81,7 @@ public class BreweryApi {
* May be any Wood, Fence or Sign that is part of a Barrel
* Returns null if block is not part of a Barrel
*/
+ @Nullable
public static Inventory getBarrelInventory(Block block) {
Barrel barrel = Barrel.get(block);
if (barrel != null) {
@@ -115,6 +120,7 @@ public class BreweryApi {
* Get a BCauldron from a Block
* Returns null if block is not a BCauldron
*/
+ @Nullable
public static BCauldron getCauldron(Block block) {
return BCauldron.get(block);
}
@@ -136,6 +142,7 @@ public class BreweryApi {
* The name is the middle one of the three if three are set in the config
* Returns null if recipe with that name does not exist
*/
+ @Nullable
public static BRecipe getRecipe(String name) {
return BRecipe.get(name);
}
diff --git a/src/com/dre/brewery/api/events/brew/BrewEvent.java b/src/com/dre/brewery/api/events/brew/BrewEvent.java
index e3ccd53..c6fee36 100644
--- a/src/com/dre/brewery/api/events/brew/BrewEvent.java
+++ b/src/com/dre/brewery/api/events/brew/BrewEvent.java
@@ -3,20 +3,23 @@ package com.dre.brewery.api.events.brew;
import com.dre.brewery.Brew;
import org.bukkit.event.Event;
import org.bukkit.inventory.meta.ItemMeta;
+import org.jetbrains.annotations.NotNull;
public abstract class BrewEvent extends Event {
protected final Brew brew;
protected final ItemMeta meta;
- public BrewEvent(Brew brew, ItemMeta meta) {
+ public BrewEvent(@NotNull Brew brew, @NotNull ItemMeta meta) {
this.brew = brew;
this.meta = meta;
}
+ @NotNull
public Brew getBrew() {
return brew;
}
+ @NotNull
public ItemMeta getItemMeta() {
return meta;
}
diff --git a/src/com/dre/brewery/api/events/brew/BrewModifyEvent.java b/src/com/dre/brewery/api/events/brew/BrewModifyEvent.java
index def9f4b..12c3c07 100644
--- a/src/com/dre/brewery/api/events/brew/BrewModifyEvent.java
+++ b/src/com/dre/brewery/api/events/brew/BrewModifyEvent.java
@@ -6,6 +6,7 @@ import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.PotionMeta;
+import org.jetbrains.annotations.NotNull;
/*
* A Brew has been created or modified
@@ -19,15 +20,17 @@ public class BrewModifyEvent extends BrewEvent implements Cancellable {
private boolean cancelled;
- public BrewModifyEvent(Brew brew, ItemMeta meta, Type type) {
+ public BrewModifyEvent(@NotNull Brew brew, @NotNull ItemMeta meta, @NotNull Type type) {
super(brew, meta);
this.type = type;
}
+ @NotNull
public Type getType() {
return type;
}
+ @NotNull
public BrewLore getLore() {
return new BrewLore(getBrew(), (PotionMeta) getItemMeta());
}
@@ -46,6 +49,7 @@ public class BrewModifyEvent extends BrewEvent implements Cancellable {
this.cancelled = cancelled;
}
+ @NotNull
@Override
public HandlerList getHandlers() {
return handlers;
diff --git a/src/com/dre/brewery/filedata/BConfig.java b/src/com/dre/brewery/filedata/BConfig.java
index 63a40c2..5e6dd2d 100644
--- a/src/com/dre/brewery/filedata/BConfig.java
+++ b/src/com/dre/brewery/filedata/BConfig.java
@@ -54,6 +54,7 @@ public class BConfig {
//Brew
public static boolean colorInBarrels; // color the Lore while in Barrels
public static boolean colorInBrewer; // color the Lore while in Brewer
+ public static boolean enableEncode;
public static P p = P.p;
@@ -173,9 +174,12 @@ public class BConfig {
homeType = config.getString("homeType", null);
colorInBarrels = config.getBoolean("colorInBarrels", false);
colorInBrewer = config.getBoolean("colorInBrewer", false);
+ enableEncode = config.getBoolean("enableEncode", false);
openEverywhere = config.getBoolean("openLargeBarrelEverywhere", false);
MCBarrel.maxBrews = config.getInt("maxBrewsInMCBarrels", 6);
+ Brew.loadSeed(config, file);
+
// loading recipes
ConfigurationSection configSection = config.getConfigurationSection("recipes");
if (configSection != null) {
diff --git a/src/com/dre/brewery/filedata/BData.java b/src/com/dre/brewery/filedata/BData.java
index 0d4bed5..6d4a29a 100644
--- a/src/com/dre/brewery/filedata/BData.java
+++ b/src/com/dre/brewery/filedata/BData.java
@@ -31,7 +31,7 @@ public class BData {
Brew.installTime = data.getLong("installTime", System.currentTimeMillis());
MCBarrel.mcBarrelTime = data.getLong("MCBarrelTime", 0);
- Brew.loadSeed(data);
+ Brew.loadPrevSeeds(data);
// Check if data is the newest version
String version = data.getString("Version", null);
diff --git a/src/com/dre/brewery/filedata/ConfigUpdater.java b/src/com/dre/brewery/filedata/ConfigUpdater.java
index 60b1f71..b03e232 100644
--- a/src/com/dre/brewery/filedata/ConfigUpdater.java
+++ b/src/com/dre/brewery/filedata/ConfigUpdater.java
@@ -82,6 +82,31 @@ public class ConfigUpdater {
+ // ---- Updating Scramble Seed ----
+
+ public void setEncodeKey(long key) {
+ int index = indexOfStart("encodeKey:");
+ if (index != -1) {
+ setLine(index, "encodeKey: " + key);
+ return;
+ }
+
+ // Old key not present
+ index = indexOfStart("enableEncode:");
+ if (index == -1) {
+ index = indexOfStart("# So enable this if you want to make recipe cheating harder");
+ }
+ if (index == -1) {
+ index = indexOfStart("version:");
+ }
+ if (index != -1) {
+ addLines(index + 1, "encodeKey: " + key);
+ } else {
+ addLines(1, "encodeKey: " + key);
+ }
+
+ }
+
// ---- Updating to newer Versions ----
// Update from a specified Config version and language to the newest version
diff --git a/src/com/dre/brewery/filedata/DataSave.java b/src/com/dre/brewery/filedata/DataSave.java
index 0cd86ed..579f93e 100644
--- a/src/com/dre/brewery/filedata/DataSave.java
+++ b/src/com/dre/brewery/filedata/DataSave.java
@@ -64,7 +64,7 @@ public class DataSave extends BukkitRunnable {
configFile.set("installTime", Brew.installTime);
configFile.set("MCBarrelTime", MCBarrel.mcBarrelTime);
- Brew.writeSeed(configFile);
+ Brew.writePrevSeeds(configFile);
if (!Brew.legacyPotions.isEmpty()) {
Brew.save(configFile.createSection("Brew"));
diff --git a/src/com/dre/brewery/listeners/InventoryListener.java b/src/com/dre/brewery/listeners/InventoryListener.java
index b180a48..bcc53b0 100644
--- a/src/com/dre/brewery/listeners/InventoryListener.java
+++ b/src/com/dre/brewery/listeners/InventoryListener.java
@@ -111,14 +111,14 @@ public class InventoryListener implements Listener {
if (P.use1_9 && !potion.hasItemFlag(ItemFlag.HIDE_POTION_EFFECTS)) {
Brew brew = Brew.get(potion);
if (brew != null) {
- brew.convertLegacy(item);
+ brew.convertPre19(item);
}
}
Brew brew = Brew.get(item);
if (brew != null) {
P.p.log(brew.toString());
P.p.log(potion.getLore().get(0).replaceAll("§", ""));
- P.p.log("similar to beispiel? " + BRecipe.get("Beispiel").createBrew(10).isSimilar(brew));
+ //P.p.log("similar to beispiel? " + BRecipe.get("Beispiel").createBrew(10).isSimilar(brew));
brew.touch();
diff --git a/src/com/dre/brewery/lore/BrewLore.java b/src/com/dre/brewery/lore/BrewLore.java
index 07cd4b5..0213911 100644
--- a/src/com/dre/brewery/lore/BrewLore.java
+++ b/src/com/dre/brewery/lore/BrewLore.java
@@ -179,7 +179,7 @@ public class BrewLore {
// Removes all effects
public void removeEffects() {
if (meta.hasCustomEffects()) {
- for (PotionEffect effect : meta.getCustomEffects()) {
+ for (PotionEffect effect : new ArrayList<>(meta.getCustomEffects())) {
PotionEffectType type = effect.getType();
//if (!type.equals(PotionEffectType.REGENERATION)) {
meta.removeCustomEffect(type);
@@ -188,6 +188,13 @@ public class BrewLore {
}
}
+ public void removeLegacySpacing() {
+ if (lore.size() > 0 && lore.get(0).equals("")) {
+ lore.remove(0);
+ 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/SeedInputStream.java b/src/com/dre/brewery/lore/SeedInputStream.java
index c293297..2d892c1 100644
--- a/src/com/dre/brewery/lore/SeedInputStream.java
+++ b/src/com/dre/brewery/lore/SeedInputStream.java
@@ -1,5 +1,7 @@
package com.dre.brewery.lore;
+import org.jetbrains.annotations.NotNull;
+
import java.io.InputStream;
import java.util.Arrays;
@@ -34,7 +36,7 @@ public class SeedInputStream extends InputStream {
}
@Override
- public int read(byte[] b, int off, int len) {
+ public int read(@NotNull byte[] b, int off, int len) {
for (int i = off; i < len; i++) {
if (reader >= 4) {
genNext();
diff --git a/src/com/dre/brewery/lore/XORScrambleStream.java b/src/com/dre/brewery/lore/XORScrambleStream.java
index f37dd43..d860088 100644
--- a/src/com/dre/brewery/lore/XORScrambleStream.java
+++ b/src/com/dre/brewery/lore/XORScrambleStream.java
@@ -5,17 +5,35 @@ import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;
+/**
+ * A Scramble Stream that uses XOR operations to scramble an outputstream.
+ * a byte generator feeded with the seed is used as xor source
+ * The resulting data can be unscrambled by the XORUnscrambleStream
+ */
public class XORScrambleStream extends FilterOutputStream {
private final long seed;
private SeedInputStream xorStream;
private boolean running;
+ /**
+ * Create a new instance of an XORScrambler, scrambling the given outputstream
+ *
+ * @param out The Outputstream to be scrambled
+ * @param seed The seed used for scrambling
+ */
public XORScrambleStream(OutputStream out, long seed) {
super(out);
this.seed = seed;
}
+ /**
+ * To start the scrambling process this has to be called before writing any data to this stream.
+ * Before starting the scrambler, any data will just be passed through unscrambled to the underlying stream.
+ * The Scrambling can be started and stopped arbitrarily at any point, allowing for parts of unscrambled data in the stream.
+ *
+ * @throws IOException IOException
+ */
public void start() throws IOException {
running = true;
if (xorStream == null) {
@@ -30,10 +48,28 @@ public class XORScrambleStream extends FilterOutputStream {
}
}
+ /**
+ * Stop the scrambling, any following data will be passed through unscrambled.
+ * The scrambling can be started again at any point after calling this
+ */
public void stop() {
running = false;
}
+ /**
+ * Mark the stream as unscrambled, any effort of unscrambing the data later will automatically read the already unscrambled data
+ * Useful if a stream may be scrambled or unscrambled, the unscrambler will automatically identify either way.
+ *
+ * @throws IOException IOException
+ * @throws IllegalStateException If the Scrambler was started in normal scrambling mode before
+ */
+ public void startUnscrambled() throws IOException, IllegalStateException {
+ if (xorStream != null) throw new IllegalStateException("The Scrambler was started in scrambling mode before");
+ short id = 0;
+ out.write((byte) (id >> 8));
+ out.write((byte) id);
+ }
+
@Override
public void write(int b) throws IOException {
if (!running) {
diff --git a/src/com/dre/brewery/lore/XORUnscrambleStream.java b/src/com/dre/brewery/lore/XORUnscrambleStream.java
index 25e45f5..f409cf3 100644
--- a/src/com/dre/brewery/lore/XORUnscrambleStream.java
+++ b/src/com/dre/brewery/lore/XORUnscrambleStream.java
@@ -1,38 +1,99 @@
package com.dre.brewery.lore;
+import com.dre.brewery.P;
+import org.jetbrains.annotations.NotNull;
+
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
+import java.util.List;
+/**
+ * A Scramble Stream that uses XOR operations to unscramble an inputstream.
+ * a byte generator feeded with the seed is used as xor source
+ * Used to unscramble data generated by the XORScrambleStream
+ */
public class XORUnscrambleStream extends FilterInputStream {
- private final long seed;
+ private long seed;
+ private final List prevSeeds;
private SeedInputStream xorStream;
private boolean running;
private boolean markRunning;
private boolean markxor;
+ /**
+ * Create a new instance of an XORUnscrambler, unscrambling the given inputstream
+ *
+ * @param in The Inputstream to be unscrambled
+ * @param seed The seed used for unscrambling
+ */
public XORUnscrambleStream(InputStream in, long seed) {
super(in);
this.seed = seed;
+ prevSeeds = null;
}
+ /**
+ * Create a new instance of an XORUnscrambler, unscrambling the given inputstream
+ * If given a List of previous Seeds, the unscrambler will try all of them for unscrambling the stream in case the seed fails.
+ *
+ * @param in The Inputstream to be unscrambled
+ * @param seed The seed used for unscrambling
+ */
+ public XORUnscrambleStream(InputStream in, long seed, List prevSeeds) {
+ super(in);
+ this.seed = seed;
+ this.prevSeeds = prevSeeds;
+ }
+
+ /**
+ * Before unscrambling, this has to be called to tell the unscrambler that scrambled data will follow.
+ * Before starting the unscrambler, any data will just be passed through unmodified to the underlying stream.
+ * The Unscrambling can be started and stopped arbitrarily at any point, allowing for parts of already unscrambled data in the stream.
+ *
+ * @throws IOException IOException
+ * @throws InvalidKeyException If the scrambled data could not be read, very likely caused by a wrong seed. Thrown after checking all previous seeds.
+ */
public void start() throws IOException, InvalidKeyException {
running = true;
if (xorStream == null) {
short id = (short) (in.read() << 8 | in.read());
if (id == 0) {
running = false;
+ P.p.debugLog("Unscrambled data");
return;
}
+ int parity = in.read();
xorStream = new SeedInputStream(seed ^ id);
- if (read() != ((int) (seed >> 48) & 0xFF)) { // Parity/Sanity
+ boolean success = checkParity(parity);
+ if (success) 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)) {
+ P.p.debugLog("Had to use prevSeed to unscramble");
+ break;
+ }
+ }
+ }
+ if (!success) {
throw new InvalidKeyException("Could not read scrambled data, is the seed wrong?");
}
}
}
+ private boolean checkParity(int parity) {
+ return ((parity ^ xorStream.read()) & 0xFF) == ((int) (seed >> 48) & 0xFF); // Parity/Sanity
+ }
+
+ /**
+ * Stop the unscrambling, any following data will be passed through unmodified.
+ * The unscrambling can be started again at any point after calling this
+ */
public void stop() {
running = false;
}
@@ -46,7 +107,7 @@ public class XORUnscrambleStream extends FilterInputStream {
}
@Override
- public int read(byte[] b, int off, int len) throws IOException {
+ public int read(@NotNull byte[] b, int off, int len) throws IOException {
if (!running) {
return in.read(b, off, len);
}
@@ -57,6 +118,7 @@ public class XORUnscrambleStream extends FilterInputStream {
return len;
}
+ @SuppressWarnings("ResultOfMethodCallIgnored")
@Override
public long skip(long n) throws IOException {
long skipped = in.skip(n);