Load Scramble Seed key from Config

Remember all used Scramble seeds to unscramble potions
This commit is contained in:
Sn0wStorm 2019-10-18 16:30:03 +02:00
parent 437f521a9b
commit f431c13100
25 changed files with 327 additions and 69 deletions

View File

@ -232,6 +232,12 @@
<version>1.5</version> <version>1.5</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>16.0.2</version>
<scope>compile</scope>
</dependency>
</dependencies> </dependencies>
<distributionManagement> <distributionManagement>

View File

@ -3,7 +3,6 @@
# -- Verschiedene Einstellungen -- # -- Verschiedene Einstellungen --
# Standardeinstellungen sind in [] angegeben # Standardeinstellungen sind in [] angegeben
# Löschen einzelner Einstellungen deaktiviert sie
# Sprachedatei die genutzt werden sollte (befindet sich in plugins/Brewery/languages) # Sprachedatei die genutzt werden sollte (befindet sich in plugins/Brewery/languages)
language: de language: de
@ -57,8 +56,12 @@ openLargeBarrelEverywhere: true
# Wie viele Brewery Getränke in die Minecraft Fässer getan werden können [6] # Wie viele Brewery Getränke in die Minecraft Fässer getan werden können [6]
maxBrewsInMCBarrels: 6 maxBrewsInMCBarrels: 6
# In den Serverlog loggen was der Spieler tatsächlich geschrieben hat, bevor seine Worte verändert wurden [false] # Benutzte Zutaten und andere Brau-Daten werden in allen Brewery Tränken gespeichert. Um zu verhindern,
logRealChat: false # 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] # 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 # 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 # Unten kann noch eingestellt werden wie und was verändert wird
enableChatDistortion: true 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] # Text nach den angegebenen Kommandos wird bei Trunkenheit ebenfalls Verändert (Liste) [- /gl]
distortCommands: distortCommands:
- /gl - /gl

View File

@ -3,7 +3,6 @@
# -- Settings -- # -- Settings --
# Defaults are written in [] # Defaults are written in []
# Deleting of single settings disables them
# Languagefile to be used (found in plugins/Brewery/languages) # Languagefile to be used (found in plugins/Brewery/languages)
language: en language: en
@ -57,6 +56,14 @@ openLargeBarrelEverywhere: true
# How many Brewery drinks can be put into the Minecraft barrels [6] # How many Brewery drinks can be put into the Minecraft barrels [6]
maxBrewsInMCBarrels: 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] # 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 # If an Update is found a Message is logged on Server-start and displayed to OPs joining the game
updateCheck: true updateCheck: true

View File

@ -3,7 +3,6 @@
# -- Paramètres -- # -- Paramètres --
# Les paramètres par défaut sont entre [] # Les paramètres par défaut sont entre []
# Supprimer un paramètre le désactive
# Fichier de langage utilisé (trouvable dans plugins/Brewery/languages) # Fichier de langage utilisé (trouvable dans plugins/Brewery/languages)
language: fr language: fr
@ -57,6 +56,14 @@ openLargeBarrelEverywhere: true
# How many Brewery drinks can be put into the Minecraft barrels [6] # How many Brewery drinks can be put into the Minecraft barrels [6]
maxBrewsInMCBarrels: 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] # 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 # If an Update is found a Message is logged on Server-start and displayed to OPs joining the game
updateCheck: true updateCheck: true

View File

@ -3,7 +3,6 @@
# -- Opzioni -- # -- Opzioni --
# I valori di default sono scritti fra [] # I valori di default sono scritti fra []
# Cancellare una voce la disabilita
# Lingua da usare (fra quelle in plugins/Brewery/languages) # Lingua da usare (fra quelle in plugins/Brewery/languages)
language: it language: it
@ -57,6 +56,14 @@ openLargeBarrelEverywhere: true
# How many Brewery drinks can be put into the Minecraft barrels [6] # How many Brewery drinks can be put into the Minecraft barrels [6]
maxBrewsInMCBarrels: 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] # 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. # Se quando un aggiornamento viene trovato un messaggio è loggato e mostrato agli OPs quando entrano in gioco.
updateCheck: true updateCheck: true

View File

@ -3,7 +3,6 @@
# -- Verschiedene Einstellungen -- # -- Verschiedene Einstellungen --
# Standardeinstellungen sind in [] angegeben # Standardeinstellungen sind in [] angegeben
# Löschen einzelner Einstellungen deaktiviert sie
# Sprachedatei die genutzt werden sollte (befindet sich in plugins/Brewery/languages) # Sprachedatei die genutzt werden sollte (befindet sich in plugins/Brewery/languages)
language: de language: de
@ -57,8 +56,12 @@ openLargeBarrelEverywhere: true
# Wie viele Brewery Getränke in die Minecraft Fässer getan werden können [6] # Wie viele Brewery Getränke in die Minecraft Fässer getan werden können [6]
maxBrewsInMCBarrels: 6 maxBrewsInMCBarrels: 6
# In den Serverlog loggen was der Spieler tatsächlich geschrieben hat, bevor seine Worte verändert wurden [false] # Benutzte Zutaten und andere Brau-Daten werden in allen Brewery Tränken gespeichert. Um zu verhindern,
logRealChat: false # 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] # 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 # 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 # Unten kann noch eingestellt werden wie und was verändert wird
enableChatDistortion: true 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] # Text nach den angegebenen Kommandos wird bei Trunkenheit ebenfalls Verändert (Liste) [- /gl]
distortCommands: distortCommands:
- /gl - /gl

View File

@ -3,7 +3,6 @@
# -- Settings -- # -- Settings --
# Defaults are written in [] # Defaults are written in []
# Deleting of single settings disables them
# Languagefile to be used (found in plugins/Brewery/languages) # Languagefile to be used (found in plugins/Brewery/languages)
language: en language: en
@ -57,6 +56,14 @@ openLargeBarrelEverywhere: true
# How many Brewery drinks can be put into the Minecraft barrels [6] # How many Brewery drinks can be put into the Minecraft barrels [6]
maxBrewsInMCBarrels: 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] # 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 # If an Update is found a Message is logged on Server-start and displayed to OPs joining the game
updateCheck: true updateCheck: true

View File

@ -3,7 +3,6 @@
# -- Paramètres -- # -- Paramètres --
# Les paramètres par défaut sont entre [] # Les paramètres par défaut sont entre []
# Supprimer un paramètre le désactive
# Fichier de langage utilisé (trouvable dans plugins/Brewery/languages) # Fichier de langage utilisé (trouvable dans plugins/Brewery/languages)
language: fr language: fr
@ -57,6 +56,14 @@ openLargeBarrelEverywhere: true
# How many Brewery drinks can be put into the Minecraft barrels [6] # How many Brewery drinks can be put into the Minecraft barrels [6]
maxBrewsInMCBarrels: 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] # 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 # If an Update is found a Message is logged on Server-start and displayed to OPs joining the game
updateCheck: true updateCheck: true

View File

@ -3,7 +3,6 @@
# -- Opzioni -- # -- Opzioni --
# I valori di default sono scritti fra [] # I valori di default sono scritti fra []
# Cancellare una voce la disabilita
# Lingua da usare (fra quelle in plugins/Brewery/languages) # Lingua da usare (fra quelle in plugins/Brewery/languages)
language: it language: it
@ -57,6 +56,14 @@ openLargeBarrelEverywhere: true
# How many Brewery drinks can be put into the Minecraft barrels [6] # How many Brewery drinks can be put into the Minecraft barrels [6]
maxBrewsInMCBarrels: 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] # 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. # Se quando un aggiornamento viene trovato un messaggio è loggato e mostrato agli OPs quando entrano in gioco.
updateCheck: true updateCheck: true

View File

@ -58,6 +58,14 @@ openLargeBarrelEverywhere: false
# MC自带的桶内可以存放多少饮品 [6] # MC自带的桶内可以存放多少饮品 [6]
maxBrewsInMCBarrels: 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] # 是否检查更新.[true]
# 若有更新, 服务端后台与上线时的管理员会收到通知. # 若有更新, 服务端后台与上线时的管理员会收到通知.
updateCheck: true updateCheck: true

View File

@ -21,6 +21,7 @@ import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType; import org.bukkit.potion.PotionEffectType;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
@ -53,6 +54,7 @@ public class BPlayer {
players.put(name, this); players.put(name, this);
} }
@Nullable
public static BPlayer get(Player player) { public static BPlayer get(Player player) {
if (!players.isEmpty()) { if (!players.isEmpty()) {
return players.get(BUtil.playerString(player)); 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 // This method may be slow and should not be used if not needed
@Nullable
public static BPlayer getByName(String playerName) { public static BPlayer getByName(String playerName) {
if (P.useUUID) { if (P.useUUID) {
for (Map.Entry<String, BPlayer> entry : players.entrySet()) { for (Map.Entry<String, BPlayer> entry : players.entrySet()) {

View File

@ -23,6 +23,7 @@ import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.PotionMeta; import org.bukkit.inventory.meta.PotionMeta;
import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitRunnable;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -42,6 +43,11 @@ public class Barrel implements InventoryHolder {
public Barrel(Block spigot, byte signoffset) { public Barrel(Block spigot, byte signoffset) {
this.spigot = spigot; 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); body = new BarrelBody(this, signoffset);
} }
@ -163,6 +169,7 @@ public class Barrel implements InventoryHolder {
public void playOpeningSound() { public void playOpeningSound() {
float randPitch = (float) (Math.random() * 0.1); float randPitch = (float) (Math.random() * 0.1);
Location location = getSpigot().getLocation(); Location location = getSpigot().getLocation();
if (location.getWorld() == null) return;
if (isLarge()) { if (isLarge()) {
location.getWorld().playSound(location, Sound.BLOCK_CHEST_OPEN, SoundCategory.BLOCKS, 0.4f, 0.55f + randPitch); 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); //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() { public void playClosingSound() {
float randPitch = (float) (Math.random() * 0.1); float randPitch = (float) (Math.random() * 0.1);
Location location = getSpigot().getLocation(); Location location = getSpigot().getLocation();
if (location.getWorld() == null) return;
if (isLarge()) { if (isLarge()) {
location.getWorld().playSound(location, Sound.BLOCK_BARREL_CLOSE, SoundCategory.BLOCKS, 0.5f, 0.5f + randPitch); 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); 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 @Override
@NotNull
public Inventory getInventory() { public Inventory getInventory() {
return inventory; return inventory;
} }
@NotNull
public Block getSpigot() { public Block getSpigot() {
return spigot; return spigot;
} }
@NotNull
public BarrelBody getBody() { public BarrelBody getBody() {
return body; return body;
} }
@ -239,7 +250,7 @@ public class Barrel implements InventoryHolder {
for (Barrel barrel : barrels) { for (Barrel barrel : barrels) {
if (barrel.body.isSignOfBarrel(signoffset)) { if (barrel.body.isSignOfBarrel(signoffset)) {
if (barrel.body.equals(spigot)) { if (barrel.spigot.equals(spigot)) {
if (barrel.body.getSignoffset() == 0 && signoffset != 0) { if (barrel.body.getSignoffset() == 0 && signoffset != 0) {
// Barrel has no signOffset even though we clicked a sign, may be old // Barrel has no signOffset even though we clicked a sign, may be old
barrel.body.setSignoffset(signoffset); barrel.body.setSignoffset(signoffset);
@ -316,7 +327,7 @@ public class Barrel implements InventoryHolder {
P.p.getServer().getPluginManager().callEvent(event); P.p.getServer().getPluginManager().callEvent(event);
if (inventory != null) { if (inventory != null) {
List<HumanEntity> viewers = new ArrayList(inventory.getViewers()); List<HumanEntity> viewers = new ArrayList<>(inventory.getViewers());
// Copy List to fix ConcModExc // Copy List to fix ConcModExc
for (HumanEntity viewer : viewers) { for (HumanEntity viewer : viewers) {
viewer.closeInventory(); viewer.closeInventory();
@ -368,7 +379,7 @@ public class Barrel implements InventoryHolder {
// is this a Small barrel? // is this a Small barrel?
public boolean isSmall() { 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 // returns the Sign of a large barrel, the spigot if there is none

View File

@ -2,6 +2,7 @@ package com.dre.brewery;
import com.dre.brewery.api.events.brew.BrewModifyEvent; import com.dre.brewery.api.events.brew.BrewModifyEvent;
import com.dre.brewery.filedata.BConfig; import com.dre.brewery.filedata.BConfig;
import com.dre.brewery.filedata.ConfigUpdater;
import com.dre.brewery.lore.*; import com.dre.brewery.lore.*;
import com.dre.brewery.utility.PotionColor; import com.dre.brewery.utility.PotionColor;
import org.bukkit.Material; import org.bukkit.Material;
@ -12,20 +13,24 @@ import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.PotionMeta; import org.bukkit.inventory.meta.PotionMeta;
import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType; import org.bukkit.potion.PotionEffectType;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nullable;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
public class Brew { public class Brew {
// represents the liquid in the brewed Potions // represents the liquid in the brewed Potions
private static long saveSeed; private static long saveSeed;
private static List<Long> prevSaveSeeds = new ArrayList<>(); // Save Seeds that have been used in the past, stored to decode brews made at that time
public static Map<Integer, Brew> legacyPotions = new HashMap<>(); public static Map<Integer, Brew> legacyPotions = new HashMap<>();
public static long installTime = System.currentTimeMillis(); // plugin install time in millis after epoch 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 // returns a Brew by ItemMeta
@Nullable
public static Brew get(ItemMeta meta) { public static Brew get(ItemMeta meta) {
if (!meta.hasLore()) return null; if (!meta.hasLore()) return null;
if (meta instanceof PotionMeta && ((PotionMeta) meta).hasCustomEffect(PotionEffectType.REGENERATION)) {
Brew brew = load(meta); Brew brew = load(meta);
if (brew != null) {
if (brew == null && meta instanceof PotionMeta && ((PotionMeta) meta).hasCustomEffect(PotionEffectType.REGENERATION)) {
// Load Legacy // Load Legacy
brew = getFromPotionEffect(((PotionMeta) meta), false); return getFromPotionEffect(((PotionMeta) meta), false);
} }
return brew; return brew;
} else {
return load(meta);
}
} }
// returns a Brew by ItemStack // returns a Brew by ItemStack
@Nullable
public static Brew get(ItemStack item) { public static Brew get(ItemStack item) {
if (item.getType() != Material.POTION) return null; if (item.getType() != Material.POTION) return null;
if (!item.hasItemMeta()) return null; if (!item.hasItemMeta()) return null;
@ -94,21 +98,17 @@ public class Brew {
ItemMeta meta = item.getItemMeta(); ItemMeta meta = item.getItemMeta();
if (!meta.hasLore()) return null; if (!meta.hasLore()) return null;
if (meta instanceof PotionMeta && ((PotionMeta) meta).hasCustomEffect(PotionEffectType.REGENERATION)) {
Brew brew = load(meta); Brew brew = load(meta);
if (brew != null) {
((PotionMeta) meta).removeCustomEffect(PotionEffectType.REGENERATION); if (brew == null && meta instanceof PotionMeta && ((PotionMeta) meta).hasCustomEffect(PotionEffectType.REGENERATION)) {
} else {
// Load Legacy and convert // Load Legacy and convert
brew = getFromPotionEffect(((PotionMeta) meta), true); brew = getFromPotionEffect(((PotionMeta) meta), true);
if (brew == null) return null; if (brew == null) return null;
new BrewLore(brew, ((PotionMeta) meta)).removeLegacySpacing();
brew.save(meta); brew.save(meta);
}
item.setItemMeta(meta); item.setItemMeta(meta);
return brew;
} else {
return load(meta);
} }
return brew;
} }
// Legacy Brew Loading // Legacy Brew Loading
@ -117,7 +117,16 @@ public class Brew {
if (effect.getType().equals(PotionEffectType.REGENERATION)) { if (effect.getType().equals(PotionEffectType.REGENERATION)) {
if (effect.getDuration() < -1) { if (effect.getDuration() < -1) {
if (remove) { if (remove) {
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 legacyPotions.remove(effect.getDuration());
}
}
return null;
} else { } else {
return legacyPotions.get(effect.getDuration()); 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 * @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 * @return The created Item, null if the Event is cancelled
*/ */
@Contract("_, false -> !null")
public ItemStack createItem(BRecipe recipe, boolean event) { public ItemStack createItem(BRecipe recipe, boolean event) {
if (recipe == null) { if (recipe == null) {
recipe = getCurrentRecipe(); recipe = getCurrentRecipe();
@ -654,9 +664,10 @@ public class Brew {
try { try {
loreStream = new LoreLoadStream(meta, 0); loreStream = new LoreLoadStream(meta, 0);
} catch (IllegalArgumentException ignored) { } catch (IllegalArgumentException ignored) {
// No Brew data found in Meta
return null; 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)) { try (DataInputStream in = new DataInputStream(unscrambler)) {
boolean parityFailed = false; boolean parityFailed = false;
if (in.readByte() != 86) { if (in.readByte() != 86) {
@ -683,7 +694,7 @@ public class Brew {
P.p.errorLog("IO Error while loading Brew"); P.p.errorLog("IO Error while loading Brew");
e.printStackTrace(); e.printStackTrace();
} catch (InvalidKeyException e) { } 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(); e.printStackTrace();
} }
return null; return null;
@ -718,7 +729,11 @@ public class Brew {
try (DataOutputStream out = new DataOutputStream(scrambler)) { try (DataOutputStream out = new DataOutputStream(scrambler)) {
out.writeByte(86); // Parity/sanity out.writeByte(86); // Parity/sanity
out.writeByte(1); // Version out.writeByte(1); // Version
if (BConfig.enableEncode) {
scrambler.start(); scrambler.start();
} else {
scrambler.startUnscrambled();
}
saveToStream(out); saveToStream(out);
} catch (IOException e) { } catch (IOException e) {
P.p.errorLog("IO Error while saving Brew"); P.p.errorLog("IO Error while saving Brew");
@ -768,17 +783,33 @@ public class Brew {
ingredients.save(out); ingredients.save(out);
} }
public static void writeSeed(ConfigurationSection section) { public static void loadPrevSeeds(ConfigurationSection section) {
section.set("BrewDataSeed", saveSeed); if (section.contains("prevSaveSeeds")) {
prevSaveSeeds = section.getLongList("prevSaveSeeds");
if (!prevSaveSeeds.contains(saveSeed)) {
prevSaveSeeds.add(saveSeed);
}
}
} }
public static void loadSeed(ConfigurationSection section) { public static void writePrevSeeds(ConfigurationSection section) {
if (section.contains("BrewDataSeed")) { if (!prevSaveSeeds.isEmpty()) {
saveSeed = section.getLong("BrewDataSeed"); section.set("prevSaveSeeds", prevSaveSeeds);
} else { }
}
public static void loadSeed(ConfigurationSection config, File file) {
saveSeed = config.getLong("encodeKey", 0);
if (saveSeed == 0) {
while (saveSeed == 0) { while (saveSeed == 0) {
saveSeed = new SecureRandom().nextLong(); 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); legacyPotions.put(uid, brew);
} }
// remove legacy potiondata from item // remove legacy potiondata for an item
public static void removeLegacy(ItemStack item) { public static void removeLegacy(ItemStack item) {
if (legacyPotions.isEmpty()) return; if (legacyPotions.isEmpty()) return;
if (!item.hasItemMeta()) return; if (!item.hasItemMeta()) return;
ItemMeta meta = item.getItemMeta(); ItemMeta meta = item.getItemMeta();
if (!(meta instanceof PotionMeta)) return; if (!(meta instanceof PotionMeta)) return;
for (PotionEffect effect : ((PotionMeta) meta).getCustomEffects()) { getFromPotionEffect(((PotionMeta) meta), true);
if (effect.getType().equals(PotionEffectType.REGENERATION)) {
if (effect.getDuration() < -1) {
legacyPotions.remove(effect.getDuration());
return;
}
}
}
} }
public void convertLegacy(ItemStack item) { public void convertPre19(ItemStack item) {
removeLegacy(item); removeLegacy(item);
PotionMeta potionMeta = ((PotionMeta) item.getItemMeta()); PotionMeta potionMeta = ((PotionMeta) item.getItemMeta());
if (hasRecipe()) { if (hasRecipe()) {
BrewLore lore = new BrewLore(this, potionMeta); BrewLore lore = new BrewLore(this, potionMeta);
lore.removeEffects(); lore.removeEffects();
PotionColor.fromString(currentRecipe.getColor()).colorBrew(potionMeta, item, canDistill()); PotionColor.fromString(currentRecipe.getColor()).colorBrew(potionMeta, item, canDistill());
lore.removeLegacySpacing();
} else { } else {
PotionColor.GREY.colorBrew(potionMeta, item, canDistill()); PotionColor.GREY.colorBrew(potionMeta, item, canDistill());
new BrewLore(this, potionMeta).removeLegacySpacing();
} }
save(potionMeta); save(potionMeta);
item.setItemMeta(potionMeta); item.setItemMeta(potionMeta);

View File

@ -10,6 +10,7 @@ import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory; import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.Nullable;
public class BreweryApi { public class BreweryApi {
@ -46,6 +47,7 @@ public class BreweryApi {
* Checks if item is actually a Brew * Checks if item is actually a Brew
* Returns null if item is not a Brew * Returns null if item is not a Brew
*/ */
@Nullable
public static Brew getBrew(ItemStack item) { public static Brew getBrew(ItemStack item) {
return Brew.get(item); return Brew.get(item);
} }
@ -56,6 +58,7 @@ public class BreweryApi {
* Checks if meta has a Brew saved * Checks if meta has a Brew saved
* Returns null if meta is not a Brew * Returns null if meta is not a Brew
*/ */
@Nullable
public static Brew getBrew(ItemMeta meta) { public static Brew getBrew(ItemMeta meta) {
return Brew.get(meta); return Brew.get(meta);
} }
@ -68,6 +71,7 @@ public class BreweryApi {
* May be any Wood, Fence, Sign that is part of a Barrel * May be any Wood, Fence, Sign that is part of a Barrel
* Returns null if block is not part of a Barrel * Returns null if block is not part of a Barrel
*/ */
@Nullable
public static Barrel getBarrel(Block block) { public static Barrel getBarrel(Block block) {
return Barrel.get(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 * May be any Wood, Fence or Sign that is part of a Barrel
* Returns null if block is not part of a Barrel * Returns null if block is not part of a Barrel
*/ */
@Nullable
public static Inventory getBarrelInventory(Block block) { public static Inventory getBarrelInventory(Block block) {
Barrel barrel = Barrel.get(block); Barrel barrel = Barrel.get(block);
if (barrel != null) { if (barrel != null) {
@ -115,6 +120,7 @@ public class BreweryApi {
* Get a BCauldron from a Block * Get a BCauldron from a Block
* Returns null if block is not a BCauldron * Returns null if block is not a BCauldron
*/ */
@Nullable
public static BCauldron getCauldron(Block block) { public static BCauldron getCauldron(Block block) {
return BCauldron.get(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 * 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 * Returns null if recipe with that name does not exist
*/ */
@Nullable
public static BRecipe getRecipe(String name) { public static BRecipe getRecipe(String name) {
return BRecipe.get(name); return BRecipe.get(name);
} }

View File

@ -3,20 +3,23 @@ package com.dre.brewery.api.events.brew;
import com.dre.brewery.Brew; import com.dre.brewery.Brew;
import org.bukkit.event.Event; import org.bukkit.event.Event;
import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
public abstract class BrewEvent extends Event { public abstract class BrewEvent extends Event {
protected final Brew brew; protected final Brew brew;
protected final ItemMeta meta; protected final ItemMeta meta;
public BrewEvent(Brew brew, ItemMeta meta) { public BrewEvent(@NotNull Brew brew, @NotNull ItemMeta meta) {
this.brew = brew; this.brew = brew;
this.meta = meta; this.meta = meta;
} }
@NotNull
public Brew getBrew() { public Brew getBrew() {
return brew; return brew;
} }
@NotNull
public ItemMeta getItemMeta() { public ItemMeta getItemMeta() {
return meta; return meta;
} }

View File

@ -6,6 +6,7 @@ import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList; import org.bukkit.event.HandlerList;
import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.PotionMeta; import org.bukkit.inventory.meta.PotionMeta;
import org.jetbrains.annotations.NotNull;
/* /*
* A Brew has been created or modified * A Brew has been created or modified
@ -19,15 +20,17 @@ public class BrewModifyEvent extends BrewEvent implements Cancellable {
private boolean cancelled; 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); super(brew, meta);
this.type = type; this.type = type;
} }
@NotNull
public Type getType() { public Type getType() {
return type; return type;
} }
@NotNull
public BrewLore getLore() { public BrewLore getLore() {
return new BrewLore(getBrew(), (PotionMeta) getItemMeta()); return new BrewLore(getBrew(), (PotionMeta) getItemMeta());
} }
@ -46,6 +49,7 @@ public class BrewModifyEvent extends BrewEvent implements Cancellable {
this.cancelled = cancelled; this.cancelled = cancelled;
} }
@NotNull
@Override @Override
public HandlerList getHandlers() { public HandlerList getHandlers() {
return handlers; return handlers;

View File

@ -54,6 +54,7 @@ public class BConfig {
//Brew //Brew
public static boolean colorInBarrels; // color the Lore while in Barrels public static boolean colorInBarrels; // color the Lore while in Barrels
public static boolean colorInBrewer; // color the Lore while in Brewer public static boolean colorInBrewer; // color the Lore while in Brewer
public static boolean enableEncode;
public static P p = P.p; public static P p = P.p;
@ -173,9 +174,12 @@ public class BConfig {
homeType = config.getString("homeType", null); homeType = config.getString("homeType", null);
colorInBarrels = config.getBoolean("colorInBarrels", false); colorInBarrels = config.getBoolean("colorInBarrels", false);
colorInBrewer = config.getBoolean("colorInBrewer", false); colorInBrewer = config.getBoolean("colorInBrewer", false);
enableEncode = config.getBoolean("enableEncode", false);
openEverywhere = config.getBoolean("openLargeBarrelEverywhere", false); openEverywhere = config.getBoolean("openLargeBarrelEverywhere", false);
MCBarrel.maxBrews = config.getInt("maxBrewsInMCBarrels", 6); MCBarrel.maxBrews = config.getInt("maxBrewsInMCBarrels", 6);
Brew.loadSeed(config, file);
// loading recipes // loading recipes
ConfigurationSection configSection = config.getConfigurationSection("recipes"); ConfigurationSection configSection = config.getConfigurationSection("recipes");
if (configSection != null) { if (configSection != null) {

View File

@ -31,7 +31,7 @@ public class BData {
Brew.installTime = data.getLong("installTime", System.currentTimeMillis()); Brew.installTime = data.getLong("installTime", System.currentTimeMillis());
MCBarrel.mcBarrelTime = data.getLong("MCBarrelTime", 0); MCBarrel.mcBarrelTime = data.getLong("MCBarrelTime", 0);
Brew.loadSeed(data); Brew.loadPrevSeeds(data);
// Check if data is the newest version // Check if data is the newest version
String version = data.getString("Version", null); String version = data.getString("Version", null);

View File

@ -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 ---- // ---- Updating to newer Versions ----
// Update from a specified Config version and language to the newest version // Update from a specified Config version and language to the newest version

View File

@ -64,7 +64,7 @@ public class DataSave extends BukkitRunnable {
configFile.set("installTime", Brew.installTime); configFile.set("installTime", Brew.installTime);
configFile.set("MCBarrelTime", MCBarrel.mcBarrelTime); configFile.set("MCBarrelTime", MCBarrel.mcBarrelTime);
Brew.writeSeed(configFile); Brew.writePrevSeeds(configFile);
if (!Brew.legacyPotions.isEmpty()) { if (!Brew.legacyPotions.isEmpty()) {
Brew.save(configFile.createSection("Brew")); Brew.save(configFile.createSection("Brew"));

View File

@ -111,14 +111,14 @@ public class InventoryListener implements Listener {
if (P.use1_9 && !potion.hasItemFlag(ItemFlag.HIDE_POTION_EFFECTS)) { if (P.use1_9 && !potion.hasItemFlag(ItemFlag.HIDE_POTION_EFFECTS)) {
Brew brew = Brew.get(potion); Brew brew = Brew.get(potion);
if (brew != null) { if (brew != null) {
brew.convertLegacy(item); brew.convertPre19(item);
} }
} }
Brew brew = Brew.get(item); Brew brew = Brew.get(item);
if (brew != null) { if (brew != null) {
P.p.log(brew.toString()); P.p.log(brew.toString());
P.p.log(potion.getLore().get(0).replaceAll("§", "")); 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(); brew.touch();

View File

@ -179,7 +179,7 @@ public class BrewLore {
// Removes all effects // Removes all effects
public void removeEffects() { public void removeEffects() {
if (meta.hasCustomEffects()) { if (meta.hasCustomEffects()) {
for (PotionEffect effect : meta.getCustomEffects()) { for (PotionEffect effect : new ArrayList<>(meta.getCustomEffects())) {
PotionEffectType type = effect.getType(); PotionEffectType type = effect.getType();
//if (!type.equals(PotionEffectType.REGENERATION)) { //if (!type.equals(PotionEffectType.REGENERATION)) {
meta.removeCustomEffect(type); 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 // True if the PotionMeta has Lore in quality color
public static boolean hasColorLore(PotionMeta meta) { public static boolean hasColorLore(PotionMeta meta) {
if (!meta.hasLore()) return false; if (!meta.hasLore()) return false;

View File

@ -1,5 +1,7 @@
package com.dre.brewery.lore; package com.dre.brewery.lore;
import org.jetbrains.annotations.NotNull;
import java.io.InputStream; import java.io.InputStream;
import java.util.Arrays; import java.util.Arrays;
@ -34,7 +36,7 @@ public class SeedInputStream extends InputStream {
} }
@Override @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++) { for (int i = off; i < len; i++) {
if (reader >= 4) { if (reader >= 4) {
genNext(); genNext();

View File

@ -5,17 +5,35 @@ import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Random; 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 { public class XORScrambleStream extends FilterOutputStream {
private final long seed; private final long seed;
private SeedInputStream xorStream; private SeedInputStream xorStream;
private boolean running; 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) { public XORScrambleStream(OutputStream out, long seed) {
super(out); super(out);
this.seed = seed; 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 { public void start() throws IOException {
running = true; running = true;
if (xorStream == null) { 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() { public void stop() {
running = false; 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 @Override
public void write(int b) throws IOException { public void write(int b) throws IOException {
if (!running) { if (!running) {

View File

@ -1,38 +1,99 @@
package com.dre.brewery.lore; package com.dre.brewery.lore;
import com.dre.brewery.P;
import org.jetbrains.annotations.NotNull;
import java.io.FilterInputStream; import java.io.FilterInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.security.InvalidKeyException; 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 { public class XORUnscrambleStream extends FilterInputStream {
private final long seed; private long seed;
private final List<Long> prevSeeds;
private SeedInputStream xorStream; private SeedInputStream xorStream;
private boolean running; private boolean running;
private boolean markRunning; private boolean markRunning;
private boolean markxor; 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) { public XORUnscrambleStream(InputStream in, long seed) {
super(in); super(in);
this.seed = seed; 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<Long> 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 { public void start() throws IOException, InvalidKeyException {
running = true; running = true;
if (xorStream == null) { if (xorStream == null) {
short id = (short) (in.read() << 8 | in.read()); short id = (short) (in.read() << 8 | in.read());
if (id == 0) { if (id == 0) {
running = false; running = false;
P.p.debugLog("Unscrambled data");
return; return;
} }
int parity = in.read();
xorStream = new SeedInputStream(seed ^ id); 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?"); 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() { public void stop() {
running = false; running = false;
} }
@ -46,7 +107,7 @@ public class XORUnscrambleStream extends FilterInputStream {
} }
@Override @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) { if (!running) {
return in.read(b, off, len); return in.read(b, off, len);
} }
@ -57,6 +118,7 @@ public class XORUnscrambleStream extends FilterInputStream {
return len; return len;
} }
@SuppressWarnings("ResultOfMethodCallIgnored")
@Override @Override
public long skip(long n) throws IOException { public long skip(long n) throws IOException {
long skipped = in.skip(n); long skipped = in.skip(n);