diff --git a/.gitignore b/.gitignore deleted file mode 100644 index ed6b2d8..0000000 --- a/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -.idea/ -target/ -bin/ diff --git a/src/main/resources/config.yml b/config.yml similarity index 97% rename from src/main/resources/config.yml rename to config.yml index d4cfb59..25179be 100644 --- a/src/main/resources/config.yml +++ b/config.yml @@ -1,176 +1,176 @@ -######################### -##### ChestSort ##### -######################### - -# -# Please note that players will need the chestsort.use permission -# or have to be OP to be able to use automatic chest sorting. -# - -# when set to false, new players will have to run /chestsort -# once to enable automatic chest sorting. -sorting-enabled-by-default: false - -# when set to true, players with sorting DISABLED will be -# shown a message on how to enable automatic chest sorting -# when they use a chest for the first time. -# consider setting this to true when you disable sorting by default. -# see also -> message-when-using-chest -show-message-when-using-chest: true - -# when set to true, players with sorting ENABLED will be -# shown a message on how to disable automatic chest sorting -# when they use a chest for the first time. -# consider setting this to true when you enable sorting by default. -# see also -> message-when-using-chest2 -show-message-when-using-chest-and-sorting-is-enabled: false - -# when set to true, the messages are shown again when a player -# logs out and back in and then uses a chest again. -show-message-again-after-logout: true - -# to sort by category, we need category files. ChestSort comes -# with a number of pregenerated category files, named -# 900-valuables.txt, 910-tools.txt, 920-combat.txt, ... -# If you wish to edit those, you can disable the generation of these -# files, because otherwise all your changes in the pregenerated -# files will be overwritten on each server startup. -auto-generate-category-files: true - -# should we check for updates? -# when enabled, a message is printed in the console if a new -# version has been found, and OPs will be notified when they -# join the server -# When set to true, we will check for updates on startup and every 24 hours -# When set to on-startup, we will only check on startup -# When set to false, don't check for updates -check-for-updates: true - -# when set to true, show some verbose information on startup -verbose: true - -######################### -#### disabled worlds #### -######################### - -# You can disable ChestSort for certain worlds. Each world's name has to -# be on a separate line, starting with a hyphen and followed by a space - -# Example: -# -# disabled-worlds: -# - world_nether -# - world_the_end - -disabled-worlds: - -########################## -##### Sorting Method ##### -########################## - -# Advanced: how to sort things! See below for examples. -# Only change this if you know what you are doing. -# -# Available variables: -# {category} order stuff by category as defined in plugins/ChestSort/categories/.txt -# {itemsFirst} put items before blocks -# {blocksFirst} put blocks before items -# {name} returns the name (e.g. DIRT, GRASS_BLOCK, BIRCH_LOG, DIAMOND_SWORD, ...) -# {color} returns the color, e.g. light_blue for wool. Empty if block/item is not dyeable -# -# Warning: You must not use spaces and fields have to be separated by commas. -# -# Examples: -# sort by name and color: -# '{name},{color}' -# -# sort by name and color, but put items before blocks: -# '{itemsFirst},{name},{color}' -# -# sort by name and color, but put blocks before items: -# '{blocksFirst},{name},{color}' -# -# sort by category, then put items before blocks and sort by name and color -# '{category},{itemsFirst},{name},{color}' -# -sorting-method: '{category},{itemsFirst},{name},{color}' - -######################### -##### localization ###### -######################### - -# Available color codes: -# &0 Black &6 Gold &c Red -# &1 Dark Blue &7 Gray &d Light Purple -# &2 Dark Green &8 Dark Gray &e Yellow -# &3 Dark Aqua &9 Blue &f White -# &4 Dark Red &a Green -# &5 Dark Purple &b Aqua - -# Available formatting codes: -# &k Obfuscated &m Strikethrough -# &l Bold &o Italic -# &n Underline &r Reset - -##### English -message-when-using-chest: "&7Hint: Type &6/chestsort&7 to enable automatic chest sorting." -message-when-using-chest2: "&7Hint: Type &6/chestsort&7 to disable automatic chest sorting." -message-sorting-disabled: "&7Automatic chest sorting has been &cdisabled&7." -message-sorting-enabled: "&7Automatic chest sorting has been &aenabled&7." -message-error-players-only: "&cError: This command can only be run by players." - -##### Chinese - Thanks to qsefthuopq for translating! -> https://www.spigotmc.org/members/qsefthuopq.339953/ -#message-when-using-chest: "&7提示: 输入 &6/chestsort&7 来启用自动整理箱子." -#message-when-using-chest2: "&7提示: 输入 &6/chestsort&7 来关闭自动整理箱子." -#message-sorting-disabled: "&7自动整理箱子已 &c关闭&7." -#message-sorting-enabled: "&7自动整理箱子已 &a启用&7." -#message-error-players-only: "&c错误: 指令只能由玩家运行." - -##### Traditional Chinese 繁體中文 -#message-when-using-chest: "&7小提醒: 輸入 &6/chestsort&7 來開啟自動整理箱子" -#message-when-using-chest2: "&7小提醒: 輸入 &6/chestsort&7 來關閉自動整理箱子" -#message-sorting-disabled: "&7自動整理箱子已 &c關閉&7" -#message-sorting-enabled: "&7自動整理箱子已 &a開啟&7" -#message-error-players-only: "&c錯誤: 這個指令只能由玩家使用" - -##### French - Thanks to automatizer for translating! -> https://www.spigotmc.org/members/automatizer.26188/ -#message-when-using-chest: "&7Astuce: Écris &6/chestsort&7 pour activer le classement automatique." -#message-when-using-chest2: "&7Astuce: Écris &6/chestsort&7 pour désactiver le classement automatique." -#message-sorting-disabled: "&7Le classement automatique a été &cdésactivé&7." -#message-sorting-enabled: "&7Le classement automatique a été &aactivé&7." -#message-error-players-only: "&cErreur: Cette commande ne peut être utilisée que par des joueurs." - -##### German -#message-when-using-chest: "&7Hinweis: Benutze &6/chestsort&7 um die automatische Kistensortierung zu aktivieren." -#message-when-using-chest2: "&7Hinweis: Benutze &6/chestsort&7 um die automatische Kistensortierung zu deaktivieren." -#message-sorting-disabled: "&7Automatische Kistensortierung &cdeaktiviert&7." -#message-sorting-enabled: "&7Automatische Kistensortierung &aaktiviert&7." -#message-error-players-only: "&cFehler: Dieser Befehl ist nur für Spieler verfügbar." - -##### Italian - Translated with Google. Please tell me if something is wrong :) -#message-when-using-chest: "&7Nota: inserire &6/chestsort&7 per abilitare l'ordinamento automatico dei bauli." -#message-when-using-chest2: "&7Nota: inserire &6/chestsort&7 per disabilitare l'ordinamento automatico dei bauli." -#message-sorting-disabled: "&7L'ordinamento automatico dei bauli è stato &cdisattivato&7." -#message-sorting-enabled: "&7L'ordinamento automatico dei bauli è stato &aattivato&7." -#message-error-players-only: "&cErrore: questo comando è disponibile solo per i giocatori." - -##### Japanese -#message-when-using-chest: "&7ヒント: &6/chestsort&7 と入力して自動チェスト整理を有効にできます。" -#message-when-using-chest2: "&7ヒント: &6/chestsort&7 と入力すると自動チェスト整理を無効にできます。" -#message-sorting-disabled: "&7自動チェスト整理は現在 &cOFF&7です。" -#message-sorting-enabled: "&7自動チェスト整理は現在 &aON&7です。" -#message-error-players-only: "&cエラー: このコマンドはプレイヤーのみ実行できます。" - -##### Spanish - Thanks to Bers_ for translating! -> https://www.spigotmc.org/members/bers_.146126/ -#message-when-using-chest: "&7Pista: Usa &6/chestsort&7 para activar el orden automático de los cofres." -#message-when-using-chest2: "&7Pista: Usa &6/chestsort&7 para desactivar el orden automático de los cofres." -#message-sorting-disabled: "&7Orden automático de los cofres &cdesactivado&7." -#message-sorting-enabled: "&7Orden automático de los cofres &aactivado&7." -#message-error-players-only: "&cError: Este comando solo puede ser ejecutado por jugadores." - -######################### -##### Done! ##### -######################### - -# please do not change the following line manually! -config-version: 6 +######################### +##### ChestSort ##### +######################### + +# +# Please note that players will need the chestsort.use permission +# or have to be OP to be able to use automatic chest sorting. +# + +# when set to false, new players will have to run /chestsort +# once to enable automatic chest sorting. +sorting-enabled-by-default: false + +# when set to true, players with sorting DISABLED will be +# shown a message on how to enable automatic chest sorting +# when they use a chest for the first time. +# consider setting this to true when you disable sorting by default. +# see also -> message-when-using-chest +show-message-when-using-chest: true + +# when set to true, players with sorting ENABLED will be +# shown a message on how to disable automatic chest sorting +# when they use a chest for the first time. +# consider setting this to true when you enable sorting by default. +# see also -> message-when-using-chest2 +show-message-when-using-chest-and-sorting-is-enabled: false + +# when set to true, the messages are shown again when a player +# logs out and back in and then uses a chest again. +show-message-again-after-logout: true + +# to sort by category, we need category files. ChestSort comes +# with a number of pregenerated category files, named +# 900-valuables.txt, 910-tools.txt, 920-combat.txt, ... +# If you wish to edit those, you can disable the generation of these +# files, because otherwise all your changes in the pregenerated +# files will be overwritten on each server startup. +auto-generate-category-files: true + +# should we check for updates? +# when enabled, a message is printed in the console if a new +# version has been found, and OPs will be notified when they +# join the server +# When set to true, we will check for updates on startup and every 24 hours +# When set to on-startup, we will only check on startup +# When set to false, don't check for updates +check-for-updates: true + +# when set to true, show some verbose information on startup +verbose: true + +######################### +#### disabled worlds #### +######################### + +# You can disable ChestSort for certain worlds. Each world's name has to +# be on a separate line, starting with a hyphen and followed by a space + +# Example: +# +# disabled-worlds: +# - world_nether +# - world_the_end + +disabled-worlds: + +########################## +##### Sorting Method ##### +########################## + +# Advanced: how to sort things! See below for examples. +# Only change this if you know what you are doing. +# +# Available variables: +# {category} order stuff by category as defined in plugins/ChestSort/categories/.txt +# {itemsFirst} put items before blocks +# {blocksFirst} put blocks before items +# {name} returns the name (e.g. DIRT, GRASS_BLOCK, BIRCH_LOG, DIAMOND_SWORD, ...) +# {color} returns the color, e.g. light_blue for wool. Empty if block/item is not dyeable +# +# Warning: You must not use spaces and fields have to be separated by commas. +# +# Examples: +# sort by name and color: +# '{name},{color}' +# +# sort by name and color, but put items before blocks: +# '{itemsFirst},{name},{color}' +# +# sort by name and color, but put blocks before items: +# '{blocksFirst},{name},{color}' +# +# sort by category, then put items before blocks and sort by name and color +# '{category},{itemsFirst},{name},{color}' +# +sorting-method: '{category},{itemsFirst},{name},{color}' + +######################### +##### localization ###### +######################### + +# Available color codes: +# &0 Black &6 Gold &c Red +# &1 Dark Blue &7 Gray &d Light Purple +# &2 Dark Green &8 Dark Gray &e Yellow +# &3 Dark Aqua &9 Blue &f White +# &4 Dark Red &a Green +# &5 Dark Purple &b Aqua + +# Available formatting codes: +# &k Obfuscated &m Strikethrough +# &l Bold &o Italic +# &n Underline &r Reset + +##### English +message-when-using-chest: "&7Hint: Type &6/chestsort&7 to enable automatic chest sorting." +message-when-using-chest2: "&7Hint: Type &6/chestsort&7 to disable automatic chest sorting." +message-sorting-disabled: "&7Automatic chest sorting has been &cdisabled&7." +message-sorting-enabled: "&7Automatic chest sorting has been &aenabled&7." +message-error-players-only: "&cError: This command can only be run by players." + +##### Chinese - Thanks to qsefthuopq for translating! -> https://www.spigotmc.org/members/qsefthuopq.339953/ +#message-when-using-chest: "&7提示: 输入 &6/chestsort&7 来启用自动整理箱子." +#message-when-using-chest2: "&7提示: 输入 &6/chestsort&7 来关闭自动整理箱子." +#message-sorting-disabled: "&7自动整理箱子已 &c关闭&7." +#message-sorting-enabled: "&7自动整理箱子已 &a启用&7." +#message-error-players-only: "&c错误: 指令只能由玩家运行." + +##### Traditional Chinese 繁體中文 +#message-when-using-chest: "&7小提醒: 輸入 &6/chestsort&7 來開啟自動整理箱子" +#message-when-using-chest2: "&7小提醒: 輸入 &6/chestsort&7 來關閉自動整理箱子" +#message-sorting-disabled: "&7自動整理箱子已 &c關閉&7" +#message-sorting-enabled: "&7自動整理箱子已 &a開啟&7" +#message-error-players-only: "&c錯誤: 這個指令只能由玩家使用" + +##### French - Thanks to automatizer for translating! -> https://www.spigotmc.org/members/automatizer.26188/ +#message-when-using-chest: "&7Astuce: Écris &6/chestsort&7 pour activer le classement automatique." +#message-when-using-chest2: "&7Astuce: Écris &6/chestsort&7 pour désactiver le classement automatique." +#message-sorting-disabled: "&7Le classement automatique a été &cdésactivé&7." +#message-sorting-enabled: "&7Le classement automatique a été &aactivé&7." +#message-error-players-only: "&cErreur: Cette commande ne peut être utilisée que par des joueurs." + +##### German +#message-when-using-chest: "&7Hinweis: Benutze &6/chestsort&7 um die automatische Kistensortierung zu aktivieren." +#message-when-using-chest2: "&7Hinweis: Benutze &6/chestsort&7 um die automatische Kistensortierung zu deaktivieren." +#message-sorting-disabled: "&7Automatische Kistensortierung &cdeaktiviert&7." +#message-sorting-enabled: "&7Automatische Kistensortierung &aaktiviert&7." +#message-error-players-only: "&cFehler: Dieser Befehl ist nur für Spieler verfügbar." + +##### Italian - Translated with Google. Please tell me if something is wrong :) +#message-when-using-chest: "&7Nota: inserire &6/chestsort&7 per abilitare l'ordinamento automatico dei bauli." +#message-when-using-chest2: "&7Nota: inserire &6/chestsort&7 per disabilitare l'ordinamento automatico dei bauli." +#message-sorting-disabled: "&7L'ordinamento automatico dei bauli è stato &cdisattivato&7." +#message-sorting-enabled: "&7L'ordinamento automatico dei bauli è stato &aattivato&7." +#message-error-players-only: "&cErrore: questo comando è disponibile solo per i giocatori." + +##### Japanese +#message-when-using-chest: "&7ヒント: &6/chestsort&7 と入力して自動チェスト整理を有効にできます。" +#message-when-using-chest2: "&7ヒント: &6/chestsort&7 と入力すると自動チェスト整理を無効にできます。" +#message-sorting-disabled: "&7自動チェスト整理は現在 &cOFF&7です。" +#message-sorting-enabled: "&7自動チェスト整理は現在 &aON&7です。" +#message-error-players-only: "&cエラー: このコマンドはプレイヤーのみ実行できます。" + +##### Spanish - Thanks to Bers_ for translating! -> https://www.spigotmc.org/members/bers_.146126/ +#message-when-using-chest: "&7Pista: Usa &6/chestsort&7 para activar el orden automático de los cofres." +#message-when-using-chest2: "&7Pista: Usa &6/chestsort&7 para desactivar el orden automático de los cofres." +#message-sorting-disabled: "&7Orden automático de los cofres &cdesactivado&7." +#message-sorting-enabled: "&7Orden automático de los cofres &aactivado&7." +#message-error-players-only: "&cError: Este comando solo puede ser ejecutado por jugadores." + +######################### +##### Done! ##### +######################### + +# please do not change the following line manually! +config-version: 6 diff --git a/src/main/resources/plugin.yml b/plugin.yml similarity index 96% rename from src/main/resources/plugin.yml rename to plugin.yml index 3e6eeeb..dbe6f0d 100644 --- a/src/main/resources/plugin.yml +++ b/plugin.yml @@ -1,20 +1,20 @@ -main: de.jeffclan.JeffChestSort.JeffChestSortPlugin -name: ChestSort -version: 3.4 -api-version: 1.13 -description: Allows automatic chest sorting -author: mfnalex -website: https://www.spigotmc.org/resources/1-13-chestsort.59773/ -prefix: ChestSort -database: false -loadbefore: -- InvUnload -commands: - chestsort: - description: Toggle automatic chest sorting - usage: / - aliases: sort - permission: chestsort.use -permissions: - chestsort.use: - description: Allows usage of automatic chest sorting +main: de.jeffclan.JeffChestSort.JeffChestSortPlugin +name: ChestSort +version: 3.4 +api-version: 1.13 +description: Allows automatic chest sorting +author: mfnalex +website: https://www.spigotmc.org/resources/1-13-chestsort.59773/ +prefix: ChestSort +database: false +loadbefore: +- InvUnload +commands: + chestsort: + description: Toggle automatic chest sorting + usage: / + aliases: sort + permission: chestsort.use +permissions: + chestsort.use: + description: Allows usage of automatic chest sorting diff --git a/pom.xml b/pom.xml deleted file mode 100644 index 3718da9..0000000 --- a/pom.xml +++ /dev/null @@ -1,60 +0,0 @@ - - 4.0.0 - - de.jeffclan - JeffChestSort - 3.4 - jar - - JeffChestSort - https://www.spigotmc.org/resources/1-11-1-13-chestsort-api.59773/ - - - UTF-8 - 1.8 - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.8.0 - - ${java.version} - ${java.version} - - - - org.apache.maven.plugins - maven-jar-plugin - 3.1.1 - - ChestSort - - - - - - - - spigot-repo - https://hub.spigotmc.org/nexus/content/repositories/snapshots/ - - - bungeecord-repo - https://oss.sonatype.org/content/repositories/snapshots - - - - - - org.spigotmc - spigot-api - 1.13.2-R0.1-SNAPSHOT - provided - - - diff --git a/src/main/java/de/jeffclan/JeffChestSort/JeffChestSortCategory.java b/src/de/jeffclan/JeffChestSort/JeffChestSortCategory.java similarity index 96% rename from src/main/java/de/jeffclan/JeffChestSort/JeffChestSortCategory.java rename to src/de/jeffclan/JeffChestSort/JeffChestSortCategory.java index 33bb8b2..babb83c 100644 --- a/src/main/java/de/jeffclan/JeffChestSort/JeffChestSortCategory.java +++ b/src/de/jeffclan/JeffChestSort/JeffChestSortCategory.java @@ -1,60 +1,60 @@ -package de.jeffclan.JeffChestSort; - -public class JeffChestSortCategory { - - // Represents a sorting category - // Includes an array of strings called typeMatches - // A typeMatch is like a regular expression, but it only supports * as - // placeholders - // e.g. "DIRT" will match the typeMatch "dirt" - // "COARSE_DIRT" will not match the typeMatch "dirt" - // "COARSE_DIRT" will match the typeMatch "*dirt" - - String name; - String[] typeMatches; - - JeffChestSortCategory(String name, String[] typeMatches) { - this.name = name; - this.typeMatches = typeMatches; - } - - boolean matches(String itemname) { - - boolean asteriskBefore = false; - boolean asteriskAfter = false; - - for (String typeMatch : typeMatches) { - - if (typeMatch.startsWith("*")) { - asteriskBefore = true; - typeMatch = typeMatch.substring(1); - } - if (typeMatch.endsWith("*")) { - asteriskAfter = true; - typeMatch = typeMatch.substring(0, typeMatch.length() - 1); - } - - if (asteriskBefore == false && asteriskAfter == false) { - if (itemname.equalsIgnoreCase(typeMatch)) { - - return true; - } - } else if (asteriskBefore == true && asteriskAfter == true) { - if (itemname.contains(typeMatch)) { - return true; - } - } else if (asteriskBefore == true && asteriskAfter == false) { - if (itemname.endsWith(typeMatch)) { - return true; - } - } else { - if (itemname.startsWith(typeMatch)) { - return true; - } - } - } - - return false; - } - -} +package de.jeffclan.JeffChestSort; + +public class JeffChestSortCategory { + + // Represents a sorting category + // Includes an array of strings called typeMatches + // A typeMatch is like a regular expression, but it only supports * as + // placeholders + // e.g. "DIRT" will match the typeMatch "dirt" + // "COARSE_DIRT" will not match the typeMatch "dirt" + // "COARSE_DIRT" will match the typeMatch "*dirt" + + String name; + String[] typeMatches; + + JeffChestSortCategory(String name, String[] typeMatches) { + this.name = name; + this.typeMatches = typeMatches; + } + + boolean matches(String itemname) { + + boolean asteriskBefore = false; + boolean asteriskAfter = false; + + for (String typeMatch : typeMatches) { + + if (typeMatch.startsWith("*")) { + asteriskBefore = true; + typeMatch = typeMatch.substring(1); + } + if (typeMatch.endsWith("*")) { + asteriskAfter = true; + typeMatch = typeMatch.substring(0, typeMatch.length() - 1); + } + + if (asteriskBefore == false && asteriskAfter == false) { + if (itemname.equalsIgnoreCase(typeMatch)) { + + return true; + } + } else if (asteriskBefore == true && asteriskAfter == true) { + if (itemname.contains(typeMatch)) { + return true; + } + } else if (asteriskBefore == true && asteriskAfter == false) { + if (itemname.endsWith(typeMatch)) { + return true; + } + } else { + if (itemname.startsWith(typeMatch)) { + return true; + } + } + } + + return false; + } + +} diff --git a/src/main/java/de/jeffclan/JeffChestSort/JeffChestSortCommandExecutor.java b/src/de/jeffclan/JeffChestSort/JeffChestSortCommandExecutor.java similarity index 96% rename from src/main/java/de/jeffclan/JeffChestSort/JeffChestSortCommandExecutor.java rename to src/de/jeffclan/JeffChestSort/JeffChestSortCommandExecutor.java index 6dcf811..dea4504 100644 --- a/src/main/java/de/jeffclan/JeffChestSort/JeffChestSortCommandExecutor.java +++ b/src/de/jeffclan/JeffChestSort/JeffChestSortCommandExecutor.java @@ -1,48 +1,48 @@ -package de.jeffclan.JeffChestSort; - -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; - -public class JeffChestSortCommandExecutor implements CommandExecutor { - - JeffChestSortPlugin plugin; - - JeffChestSortCommandExecutor(JeffChestSortPlugin plugin) { - this.plugin = plugin; - } - - @Override - public boolean onCommand(CommandSender sender, Command arg1, String arg2, String[] arg3) { - - if (arg1.getName().equalsIgnoreCase("chestsort")) { - - if (!(sender instanceof Player)) { - sender.sendMessage(plugin.messages.MSG_PLAYERSONLY); - return true; - } - - Player p = (Player) sender; - - // fix for Spigot's stupid /reload function - plugin.listener.registerPlayerIfNeeded(p); - - JeffChestSortPlayerSetting setting = plugin.PerPlayerSettings.get(p.getUniqueId().toString()); - setting.sortingEnabled = !setting.sortingEnabled; - setting.hasSeenMessage=true; - - if (setting.sortingEnabled) { - p.sendMessage(plugin.messages.MSG_ACTIVATED); - } else { - p.sendMessage(plugin.messages.MSG_DEACTIVATED); - } - - return true; - - } - - return false; - } - -} +package de.jeffclan.JeffChestSort; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +public class JeffChestSortCommandExecutor implements CommandExecutor { + + JeffChestSortPlugin plugin; + + JeffChestSortCommandExecutor(JeffChestSortPlugin plugin) { + this.plugin = plugin; + } + + @Override + public boolean onCommand(CommandSender sender, Command arg1, String arg2, String[] arg3) { + + if (arg1.getName().equalsIgnoreCase("chestsort")) { + + if (!(sender instanceof Player)) { + sender.sendMessage(plugin.messages.MSG_PLAYERSONLY); + return true; + } + + Player p = (Player) sender; + + // fix for Spigot's stupid /reload function + plugin.listener.registerPlayerIfNeeded(p); + + JeffChestSortPlayerSetting setting = plugin.PerPlayerSettings.get(p.getUniqueId().toString()); + setting.sortingEnabled = !setting.sortingEnabled; + setting.hasSeenMessage=true; + + if (setting.sortingEnabled) { + p.sendMessage(plugin.messages.MSG_ACTIVATED); + } else { + p.sendMessage(plugin.messages.MSG_DEACTIVATED); + } + + return true; + + } + + return false; + } + +} diff --git a/src/main/java/de/jeffclan/JeffChestSort/JeffChestSortListener.java b/src/de/jeffclan/JeffChestSort/JeffChestSortListener.java similarity index 96% rename from src/main/java/de/jeffclan/JeffChestSort/JeffChestSortListener.java rename to src/de/jeffclan/JeffChestSort/JeffChestSortListener.java index 4ce9df6..4ea12c8 100644 --- a/src/main/java/de/jeffclan/JeffChestSort/JeffChestSortListener.java +++ b/src/de/jeffclan/JeffChestSort/JeffChestSortListener.java @@ -1,125 +1,125 @@ -package de.jeffclan.JeffChestSort; - -import java.util.UUID; -import java.io.File; - -import org.bukkit.GameMode; -import org.bukkit.block.Chest; -import org.bukkit.block.DoubleChest; -import org.bukkit.block.ShulkerBox; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.inventory.InventoryCloseEvent; -import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.event.player.PlayerQuitEvent; - -public class JeffChestSortListener implements Listener { - - JeffChestSortPlugin plugin; - - JeffChestSortListener(JeffChestSortPlugin plugin) { - this.plugin = plugin; - } - - @EventHandler - public void onPlayerJoin(PlayerJoinEvent event) { - - if (event.getPlayer().getName().equalsIgnoreCase("mfnalex")) { - plugin.debug = true; - } - - if (event.getPlayer().isOp()) { - plugin.updateChecker.sendUpdateMessage(event.getPlayer()); - } - - registerPlayerIfNeeded(event.getPlayer()); - - - } - - void registerPlayerIfNeeded(Player p) { - UUID uniqueId = p.getUniqueId(); - if (!plugin.PerPlayerSettings.containsKey(uniqueId.toString())) { - - File playerFile = new File(plugin.getDataFolder() + File.separator + "playerdata", - p.getUniqueId().toString() + ".yml"); - YamlConfiguration playerConfig = YamlConfiguration.loadConfiguration(playerFile); - - boolean activeForThisPlayer; - - if (!playerFile.exists()) { - activeForThisPlayer = plugin.getConfig().getBoolean("sorting-enabled-by-default"); - } else { - activeForThisPlayer = playerConfig.getBoolean("sortingEnabled"); - } - - JeffChestSortPlayerSetting newSettings = new JeffChestSortPlayerSetting(activeForThisPlayer); - if (!plugin.getConfig().getBoolean("show-message-again-after-logout")) { - newSettings.hasSeenMessage = playerConfig.getBoolean("hasSeenMessage"); - } - plugin.PerPlayerSettings.put(uniqueId.toString(), newSettings); - - } - } - - @EventHandler - public void onPlayerQuit(PlayerQuitEvent event) { - plugin.unregisterPlayer(event.getPlayer()); - } - - @EventHandler - public void onInventoryClose(InventoryCloseEvent event) { - - if (!(event.getPlayer() instanceof Player)) { - return; - } - - Player p = (Player) event.getPlayer(); - - if (!p.hasPermission("chestsort.use")) { - return; - } - - if(plugin.disabledWorlds.contains(p.getWorld().getName().toLowerCase())) { - return; - } - - // Don't sort automatically when player is spectator or in adventure mode - if (p.getGameMode() == GameMode.SPECTATOR || p.getGameMode() == GameMode.ADVENTURE) { - return; - } - - // Fixes exception when using /reload - registerPlayerIfNeeded(p); - - JeffChestSortPlayerSetting setting = plugin.PerPlayerSettings.get(p.getUniqueId().toString()); - - if (!(event.getInventory().getHolder() instanceof Chest) - && !(event.getInventory().getHolder() instanceof DoubleChest) - && !(event.getInventory().getHolder() instanceof ShulkerBox)) { - return; - } - - if (!plugin.sortingEnabled(p)) { - if (!setting.hasSeenMessage) { - setting.hasSeenMessage = true; - if (plugin.getConfig().getBoolean("show-message-when-using-chest")) { - p.sendMessage(plugin.messages.MSG_COMMANDMESSAGE); - } - } - return; - } else { - if (!setting.hasSeenMessage) { - setting.hasSeenMessage = true; - if (plugin.getConfig().getBoolean("show-message-when-using-chest-and-sorting-is-enabled")) { - p.sendMessage(plugin.messages.MSG_COMMANDMESSAGE2); - } - } - } - - plugin.organizer.sortInventory(event.getInventory()); - - } -} +package de.jeffclan.JeffChestSort; + +import java.util.UUID; +import java.io.File; + +import org.bukkit.GameMode; +import org.bukkit.block.Chest; +import org.bukkit.block.DoubleChest; +import org.bukkit.block.ShulkerBox; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +public class JeffChestSortListener implements Listener { + + JeffChestSortPlugin plugin; + + JeffChestSortListener(JeffChestSortPlugin plugin) { + this.plugin = plugin; + } + + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + + if (event.getPlayer().getName().equalsIgnoreCase("mfnalex")) { + plugin.debug = true; + } + + if (event.getPlayer().isOp()) { + plugin.updateChecker.sendUpdateMessage(event.getPlayer()); + } + + registerPlayerIfNeeded(event.getPlayer()); + + + } + + void registerPlayerIfNeeded(Player p) { + UUID uniqueId = p.getUniqueId(); + if (!plugin.PerPlayerSettings.containsKey(uniqueId.toString())) { + + File playerFile = new File(plugin.getDataFolder() + File.separator + "playerdata", + p.getUniqueId().toString() + ".yml"); + YamlConfiguration playerConfig = YamlConfiguration.loadConfiguration(playerFile); + + boolean activeForThisPlayer; + + if (!playerFile.exists()) { + activeForThisPlayer = plugin.getConfig().getBoolean("sorting-enabled-by-default"); + } else { + activeForThisPlayer = playerConfig.getBoolean("sortingEnabled"); + } + + JeffChestSortPlayerSetting newSettings = new JeffChestSortPlayerSetting(activeForThisPlayer); + if (!plugin.getConfig().getBoolean("show-message-again-after-logout")) { + newSettings.hasSeenMessage = playerConfig.getBoolean("hasSeenMessage"); + } + plugin.PerPlayerSettings.put(uniqueId.toString(), newSettings); + + } + } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent event) { + plugin.unregisterPlayer(event.getPlayer()); + } + + @EventHandler + public void onInventoryClose(InventoryCloseEvent event) { + + if (!(event.getPlayer() instanceof Player)) { + return; + } + + Player p = (Player) event.getPlayer(); + + if (!p.hasPermission("chestsort.use")) { + return; + } + + if(plugin.disabledWorlds.contains(p.getWorld().getName().toLowerCase())) { + return; + } + + // Don't sort automatically when player is spectator or in adventure mode + if (p.getGameMode() == GameMode.SPECTATOR || p.getGameMode() == GameMode.ADVENTURE) { + return; + } + + // Fixes exception when using /reload + registerPlayerIfNeeded(p); + + JeffChestSortPlayerSetting setting = plugin.PerPlayerSettings.get(p.getUniqueId().toString()); + + if (!(event.getInventory().getHolder() instanceof Chest) + && !(event.getInventory().getHolder() instanceof DoubleChest) + && !(event.getInventory().getHolder() instanceof ShulkerBox)) { + return; + } + + if (!plugin.sortingEnabled(p)) { + if (!setting.hasSeenMessage) { + setting.hasSeenMessage = true; + if (plugin.getConfig().getBoolean("show-message-when-using-chest")) { + p.sendMessage(plugin.messages.MSG_COMMANDMESSAGE); + } + } + return; + } else { + if (!setting.hasSeenMessage) { + setting.hasSeenMessage = true; + if (plugin.getConfig().getBoolean("show-message-when-using-chest-and-sorting-is-enabled")) { + p.sendMessage(plugin.messages.MSG_COMMANDMESSAGE2); + } + } + } + + plugin.organizer.sortInventory(event.getInventory()); + + } +} diff --git a/src/main/java/de/jeffclan/JeffChestSort/JeffChestSortMessages.java b/src/de/jeffclan/JeffChestSort/JeffChestSortMessages.java similarity index 97% rename from src/main/java/de/jeffclan/JeffChestSort/JeffChestSortMessages.java rename to src/de/jeffclan/JeffChestSort/JeffChestSortMessages.java index cf8c39a..70cd695 100644 --- a/src/main/java/de/jeffclan/JeffChestSort/JeffChestSortMessages.java +++ b/src/de/jeffclan/JeffChestSort/JeffChestSortMessages.java @@ -1,30 +1,30 @@ -package de.jeffclan.JeffChestSort; - -import org.bukkit.ChatColor; - -public class JeffChestSortMessages { - - JeffChestSortPlugin plugin; - - final String MSG_ACTIVATED, MSG_DEACTIVATED, MSG_COMMANDMESSAGE, MSG_COMMANDMESSAGE2, MSG_PLAYERSONLY; - - JeffChestSortMessages(JeffChestSortPlugin plugin) { - this.plugin = plugin; - - MSG_ACTIVATED = ChatColor.translateAlternateColorCodes('&', plugin.getConfig() - .getString("message-sorting-enabled", "&7Automatic chest sorting has been &aenabled&7.&r")); - - MSG_DEACTIVATED = ChatColor.translateAlternateColorCodes('&', plugin.getConfig() - .getString("message-sorting-disabled", "&7Automatic chest sorting has been &cdisabled&7.&r")); - - MSG_COMMANDMESSAGE = ChatColor.translateAlternateColorCodes('&', plugin.getConfig().getString( - "message-when-using-chest", "&7Hint: Type &6/chestsort&7 to enable automatic chest sorting.")); - - MSG_COMMANDMESSAGE2 = ChatColor.translateAlternateColorCodes('&', plugin.getConfig().getString( - "message-when-using-chest2", "&7Hint: Type &6/chestsort&7 to disable automatic chest sorting.")); - - MSG_PLAYERSONLY = ChatColor.translateAlternateColorCodes('&', plugin.getConfig() - .getString("message-error-players-only", "&cError: This command can only be run by players.&r")); - } - -} +package de.jeffclan.JeffChestSort; + +import org.bukkit.ChatColor; + +public class JeffChestSortMessages { + + JeffChestSortPlugin plugin; + + final String MSG_ACTIVATED, MSG_DEACTIVATED, MSG_COMMANDMESSAGE, MSG_COMMANDMESSAGE2, MSG_PLAYERSONLY; + + JeffChestSortMessages(JeffChestSortPlugin plugin) { + this.plugin = plugin; + + MSG_ACTIVATED = ChatColor.translateAlternateColorCodes('&', plugin.getConfig() + .getString("message-sorting-enabled", "&7Automatic chest sorting has been &aenabled&7.&r")); + + MSG_DEACTIVATED = ChatColor.translateAlternateColorCodes('&', plugin.getConfig() + .getString("message-sorting-disabled", "&7Automatic chest sorting has been &cdisabled&7.&r")); + + MSG_COMMANDMESSAGE = ChatColor.translateAlternateColorCodes('&', plugin.getConfig().getString( + "message-when-using-chest", "&7Hint: Type &6/chestsort&7 to enable automatic chest sorting.")); + + MSG_COMMANDMESSAGE2 = ChatColor.translateAlternateColorCodes('&', plugin.getConfig().getString( + "message-when-using-chest2", "&7Hint: Type &6/chestsort&7 to disable automatic chest sorting.")); + + MSG_PLAYERSONLY = ChatColor.translateAlternateColorCodes('&', plugin.getConfig() + .getString("message-error-players-only", "&cError: This command can only be run by players.&r")); + } + +} diff --git a/src/main/java/de/jeffclan/JeffChestSort/JeffChestSortOrganizer.java b/src/de/jeffclan/JeffChestSort/JeffChestSortOrganizer.java similarity index 96% rename from src/main/java/de/jeffclan/JeffChestSort/JeffChestSortOrganizer.java rename to src/de/jeffclan/JeffChestSort/JeffChestSortOrganizer.java index 8860661..775b0ba 100644 --- a/src/main/java/de/jeffclan/JeffChestSort/JeffChestSortOrganizer.java +++ b/src/de/jeffclan/JeffChestSort/JeffChestSortOrganizer.java @@ -1,273 +1,273 @@ -package de.jeffclan.JeffChestSort; - -import java.io.File; -import java.io.FileNotFoundException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; -import java.util.Scanner; - -import org.bukkit.Bukkit; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; - -public class JeffChestSortOrganizer { - - /* - * DEPRECATED: THE FOLLOWING INFOS ARE OUTDATED - * I HAVE REPLACED THE UUID CONNECTION WITH AN ARRAY THAT REFERS TO THE ACTUAL ITEMSTACK - * Thoughts before implementing: - * We create a string from each item that can be sorted. - * We will omit certain parts of the name and put them behind the main name for sorting reasons. - * E.g. ACACIA_LOG -> LOG_ACACIA (so all LOGs are grouped) - * Diamond, Gold, Iron, Stone, Wood does NOT have to be sorted, because they are already alphabetically in the right order - * We identify the ItemStack by its hashcode, which is appended to the sorting string. - */ - - JeffChestSortPlugin plugin; - - static final String[] colors = { "white", "orange", "magenta", "light_blue", "light_gray", "yellow", "lime", "pink", "gray", - "cyan", "purple", "blue", "brown", "green", "red", "black" }; - static final String[] woodNames = { "acacia", "birch", "jungle", "oak", "spruce", "dark_oak" }; - - ArrayList categories = new ArrayList(); - - JeffChestSortOrganizer(JeffChestSortPlugin plugin) { - this.plugin = plugin; - - - // Load Categories - File categoriesFolder = new File(plugin.getDataFolder().getAbsolutePath() + File.separator + "categories" + File.separator); - File[] listOfCategoryFiles = categoriesFolder.listFiles(); - - for (File file : listOfCategoryFiles) { - if (file.isFile()) { - String categoryName = file.getName().replaceFirst(".txt", ""); - - try { - categories.add(new JeffChestSortCategory(categoryName,getArrayFromCategoryFile(file))); - if(plugin.verbose) { - plugin.getLogger().info("Loaded category file "+file.getName()); - } - } catch (FileNotFoundException e) { - plugin.getLogger().warning("Could not load category file: "+file.getName()); - //e.printStackTrace(); - } - } - } - - } - - String[] getArrayFromCategoryFile(File file) throws FileNotFoundException { - Scanner sc = new Scanner(file); - List lines = new ArrayList(); - while (sc.hasNextLine()) { - //if(!sc.nextLine().startsWith("#")) { - lines.add(sc.nextLine()); - //} - } - - String[] arr = lines.toArray(new String[0]); - sc.close(); - return arr; - } - - - String[] getTypeAndColor(String typeName) { - - // [0] = TypeName - // [1] = Color - - String myColor = ""; - typeName = typeName.toLowerCase(); - - for (String color : colors) { - if (typeName.startsWith(color)) { - typeName = typeName.replaceFirst(color + "_", ""); - myColor = color; - } - } - - for(String woodName : woodNames) { - if(typeName.equals(woodName+"_wood")) { - typeName = "log_wood"; - myColor = woodName; - } - else if(typeName.startsWith(woodName)) { - typeName = typeName.replaceFirst(woodName+"_", ""); - myColor = woodName; - } - else if(typeName.equals("stripped_"+woodName+"_log")) { - //typeName = typeName.replaceFirst("stripped_"+woodName+"_", "stripped_"); - typeName = "log_stripped"; - myColor = woodName; - } else if(typeName.equals("stripped_"+woodName+"_wood")) { - typeName = "log_wood_stripped"; - myColor = woodName; - } - } - - // Egg has to be put in front to group spawn eggs - // E.g. cow_spawn_egg -> egg_cow_spawn - if(typeName.endsWith("_egg")) { - typeName = typeName.replaceFirst("_egg", ""); - typeName = "egg_" + typeName; - } - - // polished_andesite -> andesite_polished - if(typeName.startsWith("polished_")) { - typeName = typeName.replaceFirst("polished_", ""); - typeName = typeName + "_polished"; - } - - if(typeName.equalsIgnoreCase("wet_sponge")) { - typeName = "sponge_wet"; - } - - - if(typeName.equalsIgnoreCase("carved_pumpkin")) { - typeName = "pumpkin_carved"; - } - - // Sort armor: helmet, chestplate, leggings, boots - if(typeName.endsWith("helmet")) { - typeName = typeName.replaceFirst("helmet", "1_helmet"); - } else if(typeName.endsWith("chestplate")) { - typeName = typeName.replaceFirst("chestplate", "2_chestplate"); - } else if(typeName.endsWith("leggings")) { - typeName = typeName.replaceFirst("leggings", "3_leggings"); - } else if(typeName.endsWith("boots")) { - typeName = typeName.replaceFirst("boots", "4_boots"); - } - - // Group horse armor - if(typeName.endsWith("horse_armor")) { - typeName = typeName.replaceFirst("_horse_armor", ""); - typeName = "horse_armor_" + typeName; - } - - String[] typeAndColor = new String[2]; - typeAndColor[0] = typeName; - typeAndColor[1] = myColor; - - return typeAndColor; - } - - String getCategory(String typeName) { - - typeName = typeName.toLowerCase(); - - for (JeffChestSortCategory cat : categories) { - if (cat.matches(typeName)) { - return cat.name; - } - } - - return ""; - } - - String getSortableString(ItemStack item) { - char blocksFirst; - char itemsFirst; - if (item.getType().isBlock()) { - blocksFirst = '!'; - itemsFirst = '#'; - } else { - blocksFirst = '#'; - itemsFirst = '!'; - } - - String[] typeAndColor = getTypeAndColor(item.getType().name()); - String typeName = typeAndColor[0]; - String color = typeAndColor[1]; - String category = getCategory(item.getType().name()); - - String hashCode = String.valueOf(getBetterHash(item)); - - String sortableString = plugin.sortingMethod.replaceAll("\\{itemsFirst\\}", String.valueOf(itemsFirst)); - sortableString = sortableString.replaceAll("\\{blocksFirst\\}", String.valueOf(blocksFirst)); - sortableString = sortableString.replaceAll("\\{name\\}", typeName); - sortableString = sortableString.replaceAll("\\{color\\}", color); - sortableString = sortableString.replaceAll("\\{category\\}", category); - sortableString = sortableString + "," + hashCode; - - return sortableString; - - } - - void sortInventory(Inventory inv) { - sortInventory(inv,0,inv.getSize()-1); - } - - void sortInventory(Inventory inv,int startSlot, int endSlot) { - - // This has been optimized as of ChestSort 3.2. - // The hashCode is just kept for legacy reasons, it is actually not needed. - - if(plugin.debug) { - System.out.println(" "); - System.out.println(" "); - } - - - // We copy the complete inventory into an array - ItemStack[] items = inv.getContents(); - - // Get rid of all stuff before startSlot and after endSlot - for(int i = 0; i nonNullItemsList = new ArrayList(); - for(ItemStack item : items) { - if(item!=null) { - nonNullItemsList.add(item); - } - } - - // We don't need the copied inventory anymore - items=null; - - // We need the list as array - ItemStack[] nonNullItems = nonNullItemsList.toArray(new ItemStack[nonNullItemsList.size()]); - - // Sort the array with ItemStacks according to our sortable String - Arrays.sort(nonNullItems,new Comparator(){ - public int compare(ItemStack s1,ItemStack s2){ - return(getSortableString(s1).compareTo(getSortableString(s2))); - }}); - - // put everything back in a temporary inventory to combine ItemStacks even when using strict slot sorting - // Thanks to SnackMix for this idea! - Inventory tempInventory = Bukkit.createInventory(null, 63); - - for(ItemStack item : nonNullItems) { - if(plugin.debug) System.out.println(getSortableString(item)); - tempInventory.addItem(item); - } - - int currentSlot = startSlot; - for(ItemStack item : tempInventory.getContents()) { - if(item==null) continue; - inv.setItem(currentSlot, item); - currentSlot++; - } - } - - private static int getBetterHash(ItemStack item) { - // I wanted to fix the skull problems here. Instead, I ended up not using the hashCode at all. - // I still left this here because it is nice to see the hashcodes when debug is enabled - return item.hashCode(); - } - -} +package de.jeffclan.JeffChestSort; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Scanner; + +import org.bukkit.Bukkit; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +public class JeffChestSortOrganizer { + + /* + * DEPRECATED: THE FOLLOWING INFOS ARE OUTDATED + * I HAVE REPLACED THE UUID CONNECTION WITH AN ARRAY THAT REFERS TO THE ACTUAL ITEMSTACK + * Thoughts before implementing: + * We create a string from each item that can be sorted. + * We will omit certain parts of the name and put them behind the main name for sorting reasons. + * E.g. ACACIA_LOG -> LOG_ACACIA (so all LOGs are grouped) + * Diamond, Gold, Iron, Stone, Wood does NOT have to be sorted, because they are already alphabetically in the right order + * We identify the ItemStack by its hashcode, which is appended to the sorting string. + */ + + JeffChestSortPlugin plugin; + + static final String[] colors = { "white", "orange", "magenta", "light_blue", "light_gray", "yellow", "lime", "pink", "gray", + "cyan", "purple", "blue", "brown", "green", "red", "black" }; + static final String[] woodNames = { "acacia", "birch", "jungle", "oak", "spruce", "dark_oak" }; + + ArrayList categories = new ArrayList(); + + JeffChestSortOrganizer(JeffChestSortPlugin plugin) { + this.plugin = plugin; + + + // Load Categories + File categoriesFolder = new File(plugin.getDataFolder().getAbsolutePath() + File.separator + "categories" + File.separator); + File[] listOfCategoryFiles = categoriesFolder.listFiles(); + + for (File file : listOfCategoryFiles) { + if (file.isFile()) { + String categoryName = file.getName().replaceFirst(".txt", ""); + + try { + categories.add(new JeffChestSortCategory(categoryName,getArrayFromCategoryFile(file))); + if(plugin.verbose) { + plugin.getLogger().info("Loaded category file "+file.getName()); + } + } catch (FileNotFoundException e) { + plugin.getLogger().warning("Could not load category file: "+file.getName()); + //e.printStackTrace(); + } + } + } + + } + + String[] getArrayFromCategoryFile(File file) throws FileNotFoundException { + Scanner sc = new Scanner(file); + List lines = new ArrayList(); + while (sc.hasNextLine()) { + //if(!sc.nextLine().startsWith("#")) { + lines.add(sc.nextLine()); + //} + } + + String[] arr = lines.toArray(new String[0]); + sc.close(); + return arr; + } + + + String[] getTypeAndColor(String typeName) { + + // [0] = TypeName + // [1] = Color + + String myColor = ""; + typeName = typeName.toLowerCase(); + + for (String color : colors) { + if (typeName.startsWith(color)) { + typeName = typeName.replaceFirst(color + "_", ""); + myColor = color; + } + } + + for(String woodName : woodNames) { + if(typeName.equals(woodName+"_wood")) { + typeName = "log_wood"; + myColor = woodName; + } + else if(typeName.startsWith(woodName)) { + typeName = typeName.replaceFirst(woodName+"_", ""); + myColor = woodName; + } + else if(typeName.equals("stripped_"+woodName+"_log")) { + //typeName = typeName.replaceFirst("stripped_"+woodName+"_", "stripped_"); + typeName = "log_stripped"; + myColor = woodName; + } else if(typeName.equals("stripped_"+woodName+"_wood")) { + typeName = "log_wood_stripped"; + myColor = woodName; + } + } + + // Egg has to be put in front to group spawn eggs + // E.g. cow_spawn_egg -> egg_cow_spawn + if(typeName.endsWith("_egg")) { + typeName = typeName.replaceFirst("_egg", ""); + typeName = "egg_" + typeName; + } + + // polished_andesite -> andesite_polished + if(typeName.startsWith("polished_")) { + typeName = typeName.replaceFirst("polished_", ""); + typeName = typeName + "_polished"; + } + + if(typeName.equalsIgnoreCase("wet_sponge")) { + typeName = "sponge_wet"; + } + + + if(typeName.equalsIgnoreCase("carved_pumpkin")) { + typeName = "pumpkin_carved"; + } + + // Sort armor: helmet, chestplate, leggings, boots + if(typeName.endsWith("helmet")) { + typeName = typeName.replaceFirst("helmet", "1_helmet"); + } else if(typeName.endsWith("chestplate")) { + typeName = typeName.replaceFirst("chestplate", "2_chestplate"); + } else if(typeName.endsWith("leggings")) { + typeName = typeName.replaceFirst("leggings", "3_leggings"); + } else if(typeName.endsWith("boots")) { + typeName = typeName.replaceFirst("boots", "4_boots"); + } + + // Group horse armor + if(typeName.endsWith("horse_armor")) { + typeName = typeName.replaceFirst("_horse_armor", ""); + typeName = "horse_armor_" + typeName; + } + + String[] typeAndColor = new String[2]; + typeAndColor[0] = typeName; + typeAndColor[1] = myColor; + + return typeAndColor; + } + + String getCategory(String typeName) { + + typeName = typeName.toLowerCase(); + + for (JeffChestSortCategory cat : categories) { + if (cat.matches(typeName)) { + return cat.name; + } + } + + return ""; + } + + String getSortableString(ItemStack item) { + char blocksFirst; + char itemsFirst; + if (item.getType().isBlock()) { + blocksFirst = '!'; + itemsFirst = '#'; + } else { + blocksFirst = '#'; + itemsFirst = '!'; + } + + String[] typeAndColor = getTypeAndColor(item.getType().name()); + String typeName = typeAndColor[0]; + String color = typeAndColor[1]; + String category = getCategory(item.getType().name()); + + String hashCode = String.valueOf(getBetterHash(item)); + + String sortableString = plugin.sortingMethod.replaceAll("\\{itemsFirst\\}", String.valueOf(itemsFirst)); + sortableString = sortableString.replaceAll("\\{blocksFirst\\}", String.valueOf(blocksFirst)); + sortableString = sortableString.replaceAll("\\{name\\}", typeName); + sortableString = sortableString.replaceAll("\\{color\\}", color); + sortableString = sortableString.replaceAll("\\{category\\}", category); + sortableString = sortableString + "," + hashCode; + + return sortableString; + + } + + void sortInventory(Inventory inv) { + sortInventory(inv,0,inv.getSize()-1); + } + + void sortInventory(Inventory inv,int startSlot, int endSlot) { + + // This has been optimized as of ChestSort 3.2. + // The hashCode is just kept for legacy reasons, it is actually not needed. + + if(plugin.debug) { + System.out.println(" "); + System.out.println(" "); + } + + + // We copy the complete inventory into an array + ItemStack[] items = inv.getContents(); + + // Get rid of all stuff before startSlot and after endSlot + for(int i = 0; i nonNullItemsList = new ArrayList(); + for(ItemStack item : items) { + if(item!=null) { + nonNullItemsList.add(item); + } + } + + // We don't need the copied inventory anymore + items=null; + + // We need the list as array + ItemStack[] nonNullItems = nonNullItemsList.toArray(new ItemStack[nonNullItemsList.size()]); + + // Sort the array with ItemStacks according to our sortable String + Arrays.sort(nonNullItems,new Comparator(){ + public int compare(ItemStack s1,ItemStack s2){ + return(getSortableString(s1).compareTo(getSortableString(s2))); + }}); + + // put everything back in a temporary inventory to combine ItemStacks even when using strict slot sorting + // Thanks to SnackMix for this idea! + Inventory tempInventory = Bukkit.createInventory(null, 63); + + for(ItemStack item : nonNullItems) { + if(plugin.debug) System.out.println(getSortableString(item)); + tempInventory.addItem(item); + } + + int currentSlot = startSlot; + for(ItemStack item : tempInventory.getContents()) { + if(item==null) continue; + inv.setItem(currentSlot, item); + currentSlot++; + } + } + + private static int getBetterHash(ItemStack item) { + // I wanted to fix the skull problems here. Instead, I ended up not using the hashCode at all. + // I still left this here because it is nice to see the hashcodes when debug is enabled + return item.hashCode(); + } + +} diff --git a/src/main/java/de/jeffclan/JeffChestSort/JeffChestSortPlayerSetting.java b/src/de/jeffclan/JeffChestSort/JeffChestSortPlayerSetting.java similarity index 95% rename from src/main/java/de/jeffclan/JeffChestSort/JeffChestSortPlayerSetting.java rename to src/de/jeffclan/JeffChestSort/JeffChestSortPlayerSetting.java index 083f393..0d17566 100644 --- a/src/main/java/de/jeffclan/JeffChestSort/JeffChestSortPlayerSetting.java +++ b/src/de/jeffclan/JeffChestSort/JeffChestSortPlayerSetting.java @@ -1,15 +1,15 @@ -package de.jeffclan.JeffChestSort; - -public class JeffChestSortPlayerSetting { - - // Sorting enabled for this player? - boolean sortingEnabled; - - // Did we already show the message how to activate sorting? - boolean hasSeenMessage = false; - - JeffChestSortPlayerSetting(boolean sortingEnabled) { - this.sortingEnabled = sortingEnabled; - } - -} +package de.jeffclan.JeffChestSort; + +public class JeffChestSortPlayerSetting { + + // Sorting enabled for this player? + boolean sortingEnabled; + + // Did we already show the message how to activate sorting? + boolean hasSeenMessage = false; + + JeffChestSortPlayerSetting(boolean sortingEnabled) { + this.sortingEnabled = sortingEnabled; + } + +} diff --git a/src/main/java/de/jeffclan/JeffChestSort/JeffChestSortPlugin.java b/src/de/jeffclan/JeffChestSort/JeffChestSortPlugin.java similarity index 96% rename from src/main/java/de/jeffclan/JeffChestSort/JeffChestSortPlugin.java rename to src/de/jeffclan/JeffChestSort/JeffChestSortPlugin.java index 1238332..0b07444 100644 --- a/src/main/java/de/jeffclan/JeffChestSort/JeffChestSortPlugin.java +++ b/src/de/jeffclan/JeffChestSort/JeffChestSortPlugin.java @@ -1,244 +1,244 @@ -package de.jeffclan.JeffChestSort; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -import org.bukkit.Bukkit; -import org.bukkit.configuration.InvalidConfigurationException; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Player; -import org.bukkit.inventory.Inventory; -import org.bukkit.plugin.java.JavaPlugin; - -import de.jeffclan.utils.Utils; - -public class JeffChestSortPlugin extends JavaPlugin { - - Map PerPlayerSettings = new HashMap(); - JeffChestSortMessages messages; - JeffChestSortOrganizer organizer; - JeffChestSortUpdateChecker updateChecker; - JeffChestSortListener listener; - String sortingMethod; - ArrayList disabledWorlds; - int currentConfigVersion = 6; - boolean usingMatchingConfig = true; - boolean debug = false; - boolean verbose = true; - private long updateCheckInterval = 86400; // in seconds. We check on startup and every 24 hours (if you never restart your server) - - public void sortInventory(Inventory inv) { - this.organizer.sortInventory(inv); - } - - public void sortInventory(Inventory inv, int startSlot, int endSlot) { - this.organizer.sortInventory(inv,startSlot,endSlot); - } - - void createConfig() { - this.saveDefaultConfig(); - - // Config version prior to 5? Then it must have been generated by ChestSort 1.x - if (getConfig().getInt("config-version", 0) < 5) { - getLogger().warning("========================================================"); - getLogger().warning("You are using a config file that has been generated"); - getLogger().warning("prior to ChestSort version 2.0.0."); - getLogger().warning("To allow everyone to use the new features, your config"); - getLogger().warning("has been renamed to config.old.yml and a new one has"); - getLogger().warning("been generated. Please examine the new config file to"); - getLogger().warning("see the new possibilities and adjust your settings."); - getLogger().warning("========================================================"); - - File configFile = new File(getDataFolder().getAbsolutePath() + File.separator + "config.yml"); - File oldConfigFile = new File(getDataFolder().getAbsolutePath() + File.separator + "config.old.yml"); - if (oldConfigFile.getAbsoluteFile().exists()) { - oldConfigFile.getAbsoluteFile().delete(); - } - configFile.getAbsoluteFile().renameTo(oldConfigFile.getAbsoluteFile()); - saveDefaultConfig(); - try { - getConfig().load(configFile.getAbsoluteFile()); - } catch (IOException | InvalidConfigurationException e) { - getLogger().warning("Could not load freshly generated config file!"); - e.printStackTrace(); - } - } else if (getConfig().getInt("config-version", 0) != currentConfigVersion) { - getLogger().warning("========================================================"); - getLogger().warning("YOU ARE USING AN OLD CONFIG FILE!"); - getLogger().warning("This is not a problem, as ChestSort will just use the"); - getLogger().warning("default settings for unset values. However, if you want"); - getLogger().warning("to configure the new options, please go to"); - getLogger().warning("https://www.spigotmc.org/resources/1-13-chestsort.59773/"); - getLogger().warning("and replace your config.yml with the new one. You can"); - getLogger().warning("then insert your old changes into the new file."); - getLogger().warning("========================================================"); - usingMatchingConfig = false; - } - - File playerDataFolder = new File(getDataFolder().getPath() + File.separator + "playerdata"); - if (!playerDataFolder.getAbsoluteFile().exists()) { - playerDataFolder.mkdir(); - } - File categoriesFolder = new File(getDataFolder().getPath() + File.separator + "categories"); - if (!categoriesFolder.getAbsoluteFile().exists()) { - categoriesFolder.mkdir(); - } - - getConfig().addDefault("sorting-enabled-by-default", false); - getConfig().addDefault("show-message-when-using-chest", true); - getConfig().addDefault("show-message-when-using-chest-and-sorting-is-enabled", false); - getConfig().addDefault("show-message-again-after-logout", true); - getConfig().addDefault("sorting-method", "{category},{itemsFirst},{name},{color}"); - getConfig().addDefault("check-for-updates", "true"); - getConfig().addDefault("auto-generate-category-files", true); - getConfig().addDefault("verbose", true); - - disabledWorlds = (ArrayList) getConfig().getStringList("disabled-worlds"); - } - - @Override - public void onDisable() { - for (Player p : getServer().getOnlinePlayers()) { - unregisterPlayer(p); - } - } - - @Override - public void onEnable() { - - createConfig(); - saveDefaultCategories(); - verbose = getConfig().getBoolean("verbose"); - messages = new JeffChestSortMessages(this); - organizer = new JeffChestSortOrganizer(this); - updateChecker = new JeffChestSortUpdateChecker(this); - listener = new JeffChestSortListener(this); - sortingMethod = getConfig().getString("sorting-method"); - getServer().getPluginManager().registerEvents(listener, this); - JeffChestSortCommandExecutor commandExecutor = new JeffChestSortCommandExecutor(this); - this.getCommand("chestsort").setExecutor(commandExecutor); - - if (verbose) { - getLogger().info("Current sorting method: " + sortingMethod); - getLogger().info("Sorting enabled by default: " + getConfig().getBoolean("sorting-enabled-by-default")); - getLogger().info("Auto generate category files: " + getConfig().getBoolean("auto-generate-category-files")); - getLogger().info("Check for updates: " + getConfig().getString("check-for-updates")); - } - if (getConfig().getString("check-for-updates", "true").equalsIgnoreCase("true")) { - Bukkit.getScheduler().scheduleSyncRepeatingTask(this, new Runnable() { - public void run() { - updateChecker.checkForUpdate(); - } - }, 0L, updateCheckInterval * 20); - } else if (getConfig().getString("check-for-updates", "true").equalsIgnoreCase("on-startup")) { - updateChecker.checkForUpdate(); - } - - Metrics metrics = new Metrics(this); - - metrics.addCustomChart(new Metrics.SimplePie("sorting_method", () -> sortingMethod)); - metrics.addCustomChart(new Metrics.SimplePie("config_version", - () -> Integer.toString(getConfig().getInt("config-version", 0)))); - metrics.addCustomChart( - new Metrics.SimplePie("check_for_updates", () -> getConfig().getString("check-for-updates", "true"))); - metrics.addCustomChart(new Metrics.SimplePie("show_message_when_using_chest", - () -> Boolean.toString(getConfig().getBoolean("show-message-when-using-chest")))); - metrics.addCustomChart(new Metrics.SimplePie("show_message_when_using_chest_and_sorting_is_enabl", - () -> Boolean.toString(getConfig().getBoolean("show-message-when-using-chest-and-sorting-is-enabled")))); - metrics.addCustomChart(new Metrics.SimplePie("show_message_again_after_logout", - () -> Boolean.toString(getConfig().getBoolean("show-message-again-after-logout")))); - metrics.addCustomChart(new Metrics.SimplePie("sorting_enabled_by_default", - () -> Boolean.toString(getConfig().getBoolean("sorting-enabled-by-default")))); - metrics.addCustomChart( - new Metrics.SimplePie("using_matching_config_version", () -> Boolean.toString(usingMatchingConfig))); - - } - - private void saveDefaultCategories() { - String[] defaultCategories = { "900-valuables", "910-tools", "920-combat", "930-brewing", "940-food", - "950-redstone", "960-wood", "970-stone", "980-plants", "981-corals" }; - - if (getConfig().getBoolean("auto-generate-category-files", true) != true) { - - return; - } - - for (String category : defaultCategories) { - - FileOutputStream fopDefault = null; - File fileDefault; - - try { - InputStream in = getClass() - .getResourceAsStream("/categories/" + category + ".default.txt"); - - fileDefault = new File(getDataFolder().getAbsolutePath() + File.separator + "categories" - + File.separator + category + ".txt"); - fopDefault = new FileOutputStream(fileDefault); - - // if file doesnt exists, then create it - // if (!fileDefault.getAbsoluteFile().exists()) { - fileDefault.createNewFile(); - // } - - // get the content in bytes - byte[] contentInBytes = Utils.getBytes(in); - - fopDefault.write(contentInBytes); - fopDefault.flush(); - fopDefault.close(); - - } catch (IOException e) { - e.printStackTrace(); - } finally { - try { - if (fopDefault != null) { - fopDefault.close(); - } - } catch (IOException e) { - e.printStackTrace(); - } - } - } - } - - boolean sortingEnabled(Player p) { - - // The following is for all the lazy server admins who use /reload instead of properly restarting their - // server ;) I am sometimes getting stacktraces although it is clearly stated that /reload is NOT - // supported. So, here is a quick fix - if(PerPlayerSettings == null) { - PerPlayerSettings = new HashMap(); - } - listener.registerPlayerIfNeeded(p); - // End of quick fix - - return PerPlayerSettings.get(p.getUniqueId().toString()).sortingEnabled; - } - - void unregisterPlayer(Player p) { - UUID uniqueId = p.getUniqueId(); - if (PerPlayerSettings.containsKey(uniqueId.toString())) { - JeffChestSortPlayerSetting setting = PerPlayerSettings.get(p.getUniqueId().toString()); - File playerFile = new File(getDataFolder() + File.separator + "playerdata", - p.getUniqueId().toString() + ".yml"); - YamlConfiguration playerConfig = YamlConfiguration.loadConfiguration(playerFile); - playerConfig.set("sortingEnabled", setting.sortingEnabled); - playerConfig.set("hasSeenMessage", setting.hasSeenMessage); - try { - playerConfig.save(playerFile); - } catch (IOException e) { - e.printStackTrace(); - } - - PerPlayerSettings.remove(uniqueId.toString()); - } - } - -} +package de.jeffclan.JeffChestSort; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.plugin.java.JavaPlugin; + +import de.jeffclan.utils.Utils; + +public class JeffChestSortPlugin extends JavaPlugin { + + Map PerPlayerSettings = new HashMap(); + JeffChestSortMessages messages; + JeffChestSortOrganizer organizer; + JeffChestSortUpdateChecker updateChecker; + JeffChestSortListener listener; + String sortingMethod; + ArrayList disabledWorlds; + int currentConfigVersion = 6; + boolean usingMatchingConfig = true; + boolean debug = false; + boolean verbose = true; + private long updateCheckInterval = 86400; // in seconds. We check on startup and every 24 hours (if you never restart your server) + + public void sortInventory(Inventory inv) { + this.organizer.sortInventory(inv); + } + + public void sortInventory(Inventory inv, int startSlot, int endSlot) { + this.organizer.sortInventory(inv,startSlot,endSlot); + } + + void createConfig() { + this.saveDefaultConfig(); + + // Config version prior to 5? Then it must have been generated by ChestSort 1.x + if (getConfig().getInt("config-version", 0) < 5) { + getLogger().warning("========================================================"); + getLogger().warning("You are using a config file that has been generated"); + getLogger().warning("prior to ChestSort version 2.0.0."); + getLogger().warning("To allow everyone to use the new features, your config"); + getLogger().warning("has been renamed to config.old.yml and a new one has"); + getLogger().warning("been generated. Please examine the new config file to"); + getLogger().warning("see the new possibilities and adjust your settings."); + getLogger().warning("========================================================"); + + File configFile = new File(getDataFolder().getAbsolutePath() + File.separator + "config.yml"); + File oldConfigFile = new File(getDataFolder().getAbsolutePath() + File.separator + "config.old.yml"); + if (oldConfigFile.getAbsoluteFile().exists()) { + oldConfigFile.getAbsoluteFile().delete(); + } + configFile.getAbsoluteFile().renameTo(oldConfigFile.getAbsoluteFile()); + saveDefaultConfig(); + try { + getConfig().load(configFile.getAbsoluteFile()); + } catch (IOException | InvalidConfigurationException e) { + getLogger().warning("Could not load freshly generated config file!"); + e.printStackTrace(); + } + } else if (getConfig().getInt("config-version", 0) != currentConfigVersion) { + getLogger().warning("========================================================"); + getLogger().warning("YOU ARE USING AN OLD CONFIG FILE!"); + getLogger().warning("This is not a problem, as ChestSort will just use the"); + getLogger().warning("default settings for unset values. However, if you want"); + getLogger().warning("to configure the new options, please go to"); + getLogger().warning("https://www.spigotmc.org/resources/1-13-chestsort.59773/"); + getLogger().warning("and replace your config.yml with the new one. You can"); + getLogger().warning("then insert your old changes into the new file."); + getLogger().warning("========================================================"); + usingMatchingConfig = false; + } + + File playerDataFolder = new File(getDataFolder().getPath() + File.separator + "playerdata"); + if (!playerDataFolder.getAbsoluteFile().exists()) { + playerDataFolder.mkdir(); + } + File categoriesFolder = new File(getDataFolder().getPath() + File.separator + "categories"); + if (!categoriesFolder.getAbsoluteFile().exists()) { + categoriesFolder.mkdir(); + } + + getConfig().addDefault("sorting-enabled-by-default", false); + getConfig().addDefault("show-message-when-using-chest", true); + getConfig().addDefault("show-message-when-using-chest-and-sorting-is-enabled", false); + getConfig().addDefault("show-message-again-after-logout", true); + getConfig().addDefault("sorting-method", "{category},{itemsFirst},{name},{color}"); + getConfig().addDefault("check-for-updates", "true"); + getConfig().addDefault("auto-generate-category-files", true); + getConfig().addDefault("verbose", true); + + disabledWorlds = (ArrayList) getConfig().getStringList("disabled-worlds"); + } + + @Override + public void onDisable() { + for (Player p : getServer().getOnlinePlayers()) { + unregisterPlayer(p); + } + } + + @Override + public void onEnable() { + + createConfig(); + saveDefaultCategories(); + verbose = getConfig().getBoolean("verbose"); + messages = new JeffChestSortMessages(this); + organizer = new JeffChestSortOrganizer(this); + updateChecker = new JeffChestSortUpdateChecker(this); + listener = new JeffChestSortListener(this); + sortingMethod = getConfig().getString("sorting-method"); + getServer().getPluginManager().registerEvents(listener, this); + JeffChestSortCommandExecutor commandExecutor = new JeffChestSortCommandExecutor(this); + this.getCommand("chestsort").setExecutor(commandExecutor); + + if (verbose) { + getLogger().info("Current sorting method: " + sortingMethod); + getLogger().info("Sorting enabled by default: " + getConfig().getBoolean("sorting-enabled-by-default")); + getLogger().info("Auto generate category files: " + getConfig().getBoolean("auto-generate-category-files")); + getLogger().info("Check for updates: " + getConfig().getString("check-for-updates")); + } + if (getConfig().getString("check-for-updates", "true").equalsIgnoreCase("true")) { + Bukkit.getScheduler().scheduleSyncRepeatingTask(this, new Runnable() { + public void run() { + updateChecker.checkForUpdate(); + } + }, 0L, updateCheckInterval * 20); + } else if (getConfig().getString("check-for-updates", "true").equalsIgnoreCase("on-startup")) { + updateChecker.checkForUpdate(); + } + + Metrics metrics = new Metrics(this); + + metrics.addCustomChart(new Metrics.SimplePie("sorting_method", () -> sortingMethod)); + metrics.addCustomChart(new Metrics.SimplePie("config_version", + () -> Integer.toString(getConfig().getInt("config-version", 0)))); + metrics.addCustomChart( + new Metrics.SimplePie("check_for_updates", () -> getConfig().getString("check-for-updates", "true"))); + metrics.addCustomChart(new Metrics.SimplePie("show_message_when_using_chest", + () -> Boolean.toString(getConfig().getBoolean("show-message-when-using-chest")))); + metrics.addCustomChart(new Metrics.SimplePie("show_message_when_using_chest_and_sorting_is_enabl", + () -> Boolean.toString(getConfig().getBoolean("show-message-when-using-chest-and-sorting-is-enabled")))); + metrics.addCustomChart(new Metrics.SimplePie("show_message_again_after_logout", + () -> Boolean.toString(getConfig().getBoolean("show-message-again-after-logout")))); + metrics.addCustomChart(new Metrics.SimplePie("sorting_enabled_by_default", + () -> Boolean.toString(getConfig().getBoolean("sorting-enabled-by-default")))); + metrics.addCustomChart( + new Metrics.SimplePie("using_matching_config_version", () -> Boolean.toString(usingMatchingConfig))); + + } + + private void saveDefaultCategories() { + String[] defaultCategories = { "900-valuables", "910-tools", "920-combat", "930-brewing", "940-food", + "950-redstone", "960-wood", "970-stone", "980-plants", "981-corals" }; + + if (getConfig().getBoolean("auto-generate-category-files", true) != true) { + + return; + } + + for (String category : defaultCategories) { + + FileOutputStream fopDefault = null; + File fileDefault; + + try { + InputStream in = getClass() + .getResourceAsStream("/de/jeffclan/utils/categories/" + category + ".default.txt"); + + fileDefault = new File(getDataFolder().getAbsolutePath() + File.separator + "categories" + + File.separator + category + ".txt"); + fopDefault = new FileOutputStream(fileDefault); + + // if file doesnt exists, then create it + // if (!fileDefault.getAbsoluteFile().exists()) { + fileDefault.createNewFile(); + // } + + // get the content in bytes + byte[] contentInBytes = Utils.getBytes(in); + + fopDefault.write(contentInBytes); + fopDefault.flush(); + fopDefault.close(); + + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if (fopDefault != null) { + fopDefault.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + boolean sortingEnabled(Player p) { + + // The following is for all the lazy server admins who use /reload instead of properly restarting their + // server ;) I am sometimes getting stacktraces although it is clearly stated that /reload is NOT + // supported. So, here is a quick fix + if(PerPlayerSettings == null) { + PerPlayerSettings = new HashMap(); + } + listener.registerPlayerIfNeeded(p); + // End of quick fix + + return PerPlayerSettings.get(p.getUniqueId().toString()).sortingEnabled; + } + + void unregisterPlayer(Player p) { + UUID uniqueId = p.getUniqueId(); + if (PerPlayerSettings.containsKey(uniqueId.toString())) { + JeffChestSortPlayerSetting setting = PerPlayerSettings.get(p.getUniqueId().toString()); + File playerFile = new File(getDataFolder() + File.separator + "playerdata", + p.getUniqueId().toString() + ".yml"); + YamlConfiguration playerConfig = YamlConfiguration.loadConfiguration(playerFile); + playerConfig.set("sortingEnabled", setting.sortingEnabled); + playerConfig.set("hasSeenMessage", setting.hasSeenMessage); + try { + playerConfig.save(playerFile); + } catch (IOException e) { + e.printStackTrace(); + } + + PerPlayerSettings.remove(uniqueId.toString()); + } + } + +} diff --git a/src/main/java/de/jeffclan/JeffChestSort/JeffChestSortUpdateChecker.java b/src/de/jeffclan/JeffChestSort/JeffChestSortUpdateChecker.java similarity index 97% rename from src/main/java/de/jeffclan/JeffChestSort/JeffChestSortUpdateChecker.java rename to src/de/jeffclan/JeffChestSort/JeffChestSortUpdateChecker.java index 124eac0..86ccef9 100644 --- a/src/main/java/de/jeffclan/JeffChestSort/JeffChestSortUpdateChecker.java +++ b/src/de/jeffclan/JeffChestSort/JeffChestSortUpdateChecker.java @@ -1,70 +1,70 @@ -package de.jeffclan.JeffChestSort; - -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.net.HttpURLConnection; -import java.net.URL; - -import org.bukkit.ChatColor; -import org.bukkit.entity.Player; - -public class JeffChestSortUpdateChecker { - - private JeffChestSortPlugin plugin; - - JeffChestSortUpdateChecker(JeffChestSortPlugin plugin) { - this.plugin = plugin; - } - - String latestVersionLink = "https://api.jeff-media.de/chestsort/chestsort-latest-version.txt"; - String downloadLink = "https://www.spigotmc.org/resources/1-13-chestsort.59773/"; - private String currentVersion = "undefined"; - private String latestVersion = "undefined"; - - void sendUpdateMessage(Player p) { - if(!latestVersion.equals("undefined")) { - if (!currentVersion.equals(latestVersion)) { - p.sendMessage(ChatColor.GRAY + "There is a new version of " + ChatColor.GOLD + "ChestSort" + ChatColor.GRAY - + " available."); - p.sendMessage(ChatColor.GRAY + "Please download at " + downloadLink); - } - } - } - - void checkForUpdate() { - - plugin.getLogger().info("Checking for available updates..."); - - try { - - HttpURLConnection httpcon = (HttpURLConnection) new URL(latestVersionLink).openConnection(); - httpcon.addRequestProperty("User-Agent", "ChestSort/"+plugin.getDescription().getVersion()); - - BufferedReader reader = new BufferedReader(new InputStreamReader(httpcon.getInputStream())); - - String inputLine = reader.readLine().trim(); - - latestVersion = inputLine; - currentVersion = plugin.getDescription().getVersion().trim(); - - - if (latestVersion.equals(currentVersion)) { - plugin.getLogger().info("You are using the latest version of ChestSort."); - } else { - plugin.getLogger().warning("========================================================"); - plugin.getLogger().warning("There is a new version of ChestSort available!"); - plugin.getLogger().warning("Latest : " + inputLine); - plugin.getLogger().warning("Current: " + currentVersion); - plugin.getLogger().warning("Please update to the newest version. Download:"); - plugin.getLogger().warning(downloadLink); - plugin.getLogger().warning("========================================================"); - } - - reader.close(); - } catch (Exception e) { - plugin.getLogger().warning("Could not check for updates."); - } - - } - -} +package de.jeffclan.JeffChestSort; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; + +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; + +public class JeffChestSortUpdateChecker { + + private JeffChestSortPlugin plugin; + + JeffChestSortUpdateChecker(JeffChestSortPlugin plugin) { + this.plugin = plugin; + } + + String latestVersionLink = "https://api.jeff-media.de/chestsort/chestsort-latest-version.txt"; + String downloadLink = "https://www.spigotmc.org/resources/1-13-chestsort.59773/"; + private String currentVersion = "undefined"; + private String latestVersion = "undefined"; + + void sendUpdateMessage(Player p) { + if(!latestVersion.equals("undefined")) { + if (!currentVersion.equals(latestVersion)) { + p.sendMessage(ChatColor.GRAY + "There is a new version of " + ChatColor.GOLD + "ChestSort" + ChatColor.GRAY + + " available."); + p.sendMessage(ChatColor.GRAY + "Please download at " + downloadLink); + } + } + } + + void checkForUpdate() { + + plugin.getLogger().info("Checking for available updates..."); + + try { + + HttpURLConnection httpcon = (HttpURLConnection) new URL(latestVersionLink).openConnection(); + httpcon.addRequestProperty("User-Agent", "ChestSort/"+plugin.getDescription().getVersion()); + + BufferedReader reader = new BufferedReader(new InputStreamReader(httpcon.getInputStream())); + + String inputLine = reader.readLine().trim(); + + latestVersion = inputLine; + currentVersion = plugin.getDescription().getVersion().trim(); + + + if (latestVersion.equals(currentVersion)) { + plugin.getLogger().info("You are using the latest version of ChestSort."); + } else { + plugin.getLogger().warning("========================================================"); + plugin.getLogger().warning("There is a new version of ChestSort available!"); + plugin.getLogger().warning("Latest : " + inputLine); + plugin.getLogger().warning("Current: " + currentVersion); + plugin.getLogger().warning("Please update to the newest version. Download:"); + plugin.getLogger().warning(downloadLink); + plugin.getLogger().warning("========================================================"); + } + + reader.close(); + } catch (Exception e) { + plugin.getLogger().warning("Could not check for updates."); + } + + } + +} diff --git a/src/main/java/de/jeffclan/JeffChestSort/Metrics.java b/src/de/jeffclan/JeffChestSort/Metrics.java similarity index 97% rename from src/main/java/de/jeffclan/JeffChestSort/Metrics.java rename to src/de/jeffclan/JeffChestSort/Metrics.java index 5efb10d..740c2e1 100644 --- a/src/main/java/de/jeffclan/JeffChestSort/Metrics.java +++ b/src/de/jeffclan/JeffChestSort/Metrics.java @@ -1,669 +1,669 @@ -package de.jeffclan.JeffChestSort; - -import org.bukkit.Bukkit; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Player; -import org.bukkit.plugin.RegisteredServiceProvider; -import org.bukkit.plugin.ServicePriority; -import org.bukkit.plugin.java.JavaPlugin; -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; - -import javax.net.ssl.HttpsURLConnection; -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; -import java.io.File; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.net.URL; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Timer; -import java.util.TimerTask; -import java.util.UUID; -import java.util.concurrent.Callable; -import java.util.logging.Level; -import java.util.zip.GZIPOutputStream; - -/** - * bStats collects some data for plugin authors. - * - * Check out https://bStats.org/ to learn more about bStats! - */ -@SuppressWarnings("unchecked") -public class Metrics { - - static { - // You can use the property to disable the check in your test environment - if (System.getProperty("bstats.relocatecheck") == null || !System.getProperty("bstats.relocatecheck").equals("false")) { - // Maven's Relocate is clever and changes strings, too. So we have to use this little "trick" ... :D - final String defaultPackage = new String( - new byte[]{'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's', '.', 'b', 'u', 'k', 'k', 'i', 't'}); - final String examplePackage = new String(new byte[]{'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'}); - // We want to make sure nobody just copy & pastes the example and use the wrong package names - if (Metrics.class.getPackage().getName().equals(defaultPackage) || Metrics.class.getPackage().getName().equals(examplePackage)) { - throw new IllegalStateException("bStats Metrics class has not been relocated correctly!"); - } - } - } - - // The version of this bStats class - public static final int B_STATS_VERSION = 1; - - // The url to which the data is sent - private static final String URL = "https://bStats.org/submitData/bukkit"; - - // Should failed requests be logged? - private static boolean logFailedRequests; - - // The uuid of the server - private static String serverUUID; - - // The plugin - private final JavaPlugin plugin; - - // A list with all custom charts - private final List charts = new ArrayList<>(); - - /** - * Class constructor. - * - * @param plugin The plugin which stats should be submitted. - */ - public Metrics(JavaPlugin plugin) { - if (plugin == null) { - throw new IllegalArgumentException("Plugin cannot be null!"); - } - this.plugin = plugin; - - // Get the config file - File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats"); - File configFile = new File(bStatsFolder, "config.yml"); - YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile); - - // Check if the config file exists - if (!config.isSet("serverUuid")) { - - // Add default values - config.addDefault("enabled", true); - // Every server gets it's unique random id. - config.addDefault("serverUuid", UUID.randomUUID().toString()); - // Should failed request be logged? - config.addDefault("logFailedRequests", false); - - // Inform the server owners about bStats - config.options().header( - "bStats collects some data for plugin authors like how many servers are using their plugins.\n" + - "To honor their work, you should not disable it.\n" + - "This has nearly no effect on the server performance!\n" + - "Check out https://bStats.org/ to learn more :)" - ).copyDefaults(true); - try { - config.save(configFile); - } catch (IOException ignored) { } - } - - // Load the data - serverUUID = config.getString("serverUuid"); - logFailedRequests = config.getBoolean("logFailedRequests", false); - if (config.getBoolean("enabled", true)) { - boolean found = false; - // Search for all other bStats Metrics classes to see if we are the first one - for (Class service : Bukkit.getServicesManager().getKnownServices()) { - try { - service.getField("B_STATS_VERSION"); // Our identifier :) - found = true; // We aren't the first - break; - } catch (NoSuchFieldException ignored) { } - } - // Register our service - Bukkit.getServicesManager().register(Metrics.class, this, plugin, ServicePriority.Normal); - if (!found) { - // We are the first! - startSubmitting(); - } - } - } - - /** - * Adds a custom chart. - * - * @param chart The chart to add. - */ - public void addCustomChart(CustomChart chart) { - if (chart == null) { - throw new IllegalArgumentException("Chart cannot be null!"); - } - charts.add(chart); - } - - /** - * Starts the Scheduler which submits our data every 30 minutes. - */ - private void startSubmitting() { - final Timer timer = new Timer(true); // We use a timer cause the Bukkit scheduler is affected by server lags - timer.scheduleAtFixedRate(new TimerTask() { - @Override - public void run() { - if (!plugin.isEnabled()) { // Plugin was disabled - timer.cancel(); - return; - } - // Nevertheless we want our code to run in the Bukkit main thread, so we have to use the Bukkit scheduler - // Don't be afraid! The connection to the bStats server is still async, only the stats collection is sync ;) - Bukkit.getScheduler().runTask(plugin, new Runnable() { - @Override - public void run() { - submitData(); - } - }); - } - }, 1000*60*5, 1000*60*30); - // Submit the data every 30 minutes, first time after 5 minutes to give other plugins enough time to start - // WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted! - // WARNING: Just don't do it! - } - - /** - * Gets the plugin specific data. - * This method is called using Reflection. - * - * @return The plugin specific data. - */ - - public JSONObject getPluginData() { - JSONObject data = new JSONObject(); - - String pluginName = plugin.getDescription().getName(); - String pluginVersion = plugin.getDescription().getVersion(); - - data.put("pluginName", pluginName); // Append the name of the plugin - data.put("pluginVersion", pluginVersion); // Append the version of the plugin - JSONArray customCharts = new JSONArray(); - for (CustomChart customChart : charts) { - // Add the data of the custom charts - JSONObject chart = customChart.getRequestJsonObject(); - if (chart == null) { // If the chart is null, we skip it - continue; - } - customCharts.add(chart); - } - data.put("customCharts", customCharts); - - return data; - } - - /** - * Gets the server specific data. - * - * @return The server specific data. - */ - private JSONObject getServerData() { - // Minecraft specific data - int playerAmount; - try { - // Around MC 1.8 the return type was changed to a collection from an array, - // This fixes java.lang.NoSuchMethodError: org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection; - Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers"); - playerAmount = onlinePlayersMethod.getReturnType().equals(Collection.class) - ? ((Collection) onlinePlayersMethod.invoke(Bukkit.getServer())).size() - : ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length; - } catch (Exception e) { - playerAmount = Bukkit.getOnlinePlayers().size(); // Just use the new method if the Reflection failed - } - int onlineMode = Bukkit.getOnlineMode() ? 1 : 0; - String bukkitVersion = org.bukkit.Bukkit.getVersion(); - bukkitVersion = bukkitVersion.substring(bukkitVersion.indexOf("MC: ") + 4, bukkitVersion.length() - 1); - - // OS/Java specific data - String javaVersion = System.getProperty("java.version"); - String osName = System.getProperty("os.name"); - String osArch = System.getProperty("os.arch"); - String osVersion = System.getProperty("os.version"); - int coreCount = Runtime.getRuntime().availableProcessors(); - - JSONObject data = new JSONObject(); - - data.put("serverUUID", serverUUID); - - data.put("playerAmount", playerAmount); - data.put("onlineMode", onlineMode); - data.put("bukkitVersion", bukkitVersion); - - data.put("javaVersion", javaVersion); - data.put("osName", osName); - data.put("osArch", osArch); - data.put("osVersion", osVersion); - data.put("coreCount", coreCount); - - return data; - } - - /** - * Collects the data and sends it afterwards. - */ - private void submitData() { - final JSONObject data = getServerData(); - - JSONArray pluginData = new JSONArray(); - // Search for all other bStats Metrics classes to get their plugin data - for (Class service : Bukkit.getServicesManager().getKnownServices()) { - try { - service.getField("B_STATS_VERSION"); // Our identifier :) - - for (RegisteredServiceProvider provider : Bukkit.getServicesManager().getRegistrations(service)) { - try { - pluginData.add(provider.getService().getMethod("getPluginData").invoke(provider.getProvider())); - } catch (NullPointerException | NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) { } - } - } catch (NoSuchFieldException ignored) { } - } - - data.put("plugins", pluginData); - - // Create a new thread for the connection to the bStats server - new Thread(new Runnable() { - @Override - public void run() { - try { - // Send the data - sendData(data); - } catch (Exception e) { - // Something went wrong! :( - if (logFailedRequests) { - plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats of " + plugin.getName(), e); - } - } - } - }).start(); - } - - /** - * Sends the data to the bStats server. - * - * @param data The data to send. - * @throws Exception If the request failed. - */ - private static void sendData(JSONObject data) throws Exception { - if (data == null) { - throw new IllegalArgumentException("Data cannot be null!"); - } - if (Bukkit.isPrimaryThread()) { - throw new IllegalAccessException("This method must not be called from the main thread!"); - } - HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection(); - - // Compress the data to save bandwidth - byte[] compressedData = compress(data.toString()); - - // Add headers - connection.setRequestMethod("POST"); - connection.addRequestProperty("Accept", "application/json"); - connection.addRequestProperty("Connection", "close"); - connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request - connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length)); - connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format - connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION); - - // Send data - connection.setDoOutput(true); - DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream()); - outputStream.write(compressedData); - outputStream.flush(); - outputStream.close(); - - connection.getInputStream().close(); // We don't care about the response - Just send our data :) - } - - /** - * Gzips the given String. - * - * @param str The string to gzip. - * @return The gzipped String. - * @throws IOException If the compression failed. - */ - private static byte[] compress(final String str) throws IOException { - if (str == null) { - return null; - } - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - GZIPOutputStream gzip = new GZIPOutputStream(outputStream); - gzip.write(str.getBytes("UTF-8")); - gzip.close(); - return outputStream.toByteArray(); - } - - /** - * Represents a custom chart. - */ - public static abstract class CustomChart { - - // The id of the chart - final String chartId; - - /** - * Class constructor. - * - * @param chartId The id of the chart. - */ - CustomChart(String chartId) { - if (chartId == null || chartId.isEmpty()) { - throw new IllegalArgumentException("ChartId cannot be null or empty!"); - } - this.chartId = chartId; - } - - private JSONObject getRequestJsonObject() { - JSONObject chart = new JSONObject(); - chart.put("chartId", chartId); - try { - JSONObject data = getChartData(); - if (data == null) { - // If the data is null we don't send the chart. - return null; - } - chart.put("data", data); - } catch (Throwable t) { - if (logFailedRequests) { - Bukkit.getLogger().log(Level.WARNING, "Failed to get data for custom chart with id " + chartId, t); - } - return null; - } - return chart; - } - - protected abstract JSONObject getChartData() throws Exception; - - } - - /** - * Represents a custom simple pie. - */ - public static class SimplePie extends CustomChart { - - private final Callable callable; - - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public SimplePie(String chartId, Callable callable) { - super(chartId); - this.callable = callable; - } - - @Override - protected JSONObject getChartData() throws Exception { - JSONObject data = new JSONObject(); - String value = callable.call(); - if (value == null || value.isEmpty()) { - // Null = skip the chart - return null; - } - data.put("value", value); - return data; - } - } - - /** - * Represents a custom advanced pie. - */ - public static class AdvancedPie extends CustomChart { - - private final Callable> callable; - - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public AdvancedPie(String chartId, Callable> callable) { - super(chartId); - this.callable = callable; - } - - @Override - protected JSONObject getChartData() throws Exception { - JSONObject data = new JSONObject(); - JSONObject values = new JSONObject(); - Map map = callable.call(); - if (map == null || map.isEmpty()) { - // Null = skip the chart - return null; - } - boolean allSkipped = true; - for (Map.Entry entry : map.entrySet()) { - if (entry.getValue() == 0) { - continue; // Skip this invalid - } - allSkipped = false; - values.put(entry.getKey(), entry.getValue()); - } - if (allSkipped) { - // Null = skip the chart - return null; - } - data.put("values", values); - return data; - } - } - - /** - * Represents a custom drilldown pie. - */ - public static class DrilldownPie extends CustomChart { - - private final Callable>> callable; - - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public DrilldownPie(String chartId, Callable>> callable) { - super(chartId); - this.callable = callable; - } - - @Override - public JSONObject getChartData() throws Exception { - JSONObject data = new JSONObject(); - JSONObject values = new JSONObject(); - Map> map = callable.call(); - if (map == null || map.isEmpty()) { - // Null = skip the chart - return null; - } - boolean reallyAllSkipped = true; - for (Map.Entry> entryValues : map.entrySet()) { - JSONObject value = new JSONObject(); - boolean allSkipped = true; - for (Map.Entry valueEntry : map.get(entryValues.getKey()).entrySet()) { - value.put(valueEntry.getKey(), valueEntry.getValue()); - allSkipped = false; - } - if (!allSkipped) { - reallyAllSkipped = false; - values.put(entryValues.getKey(), value); - } - } - if (reallyAllSkipped) { - // Null = skip the chart - return null; - } - data.put("values", values); - return data; - } - } - - /** - * Represents a custom single line chart. - */ - public static class SingleLineChart extends CustomChart { - - private final Callable callable; - - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public SingleLineChart(String chartId, Callable callable) { - super(chartId); - this.callable = callable; - } - - @Override - protected JSONObject getChartData() throws Exception { - JSONObject data = new JSONObject(); - int value = callable.call(); - if (value == 0) { - // Null = skip the chart - return null; - } - data.put("value", value); - return data; - } - - } - - /** - * Represents a custom multi line chart. - */ - public static class MultiLineChart extends CustomChart { - - private final Callable> callable; - - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public MultiLineChart(String chartId, Callable> callable) { - super(chartId); - this.callable = callable; - } - - @Override - protected JSONObject getChartData() throws Exception { - JSONObject data = new JSONObject(); - JSONObject values = new JSONObject(); - Map map = callable.call(); - if (map == null || map.isEmpty()) { - // Null = skip the chart - return null; - } - boolean allSkipped = true; - for (Map.Entry entry : map.entrySet()) { - if (entry.getValue() == 0) { - continue; // Skip this invalid - } - allSkipped = false; - values.put(entry.getKey(), entry.getValue()); - } - if (allSkipped) { - // Null = skip the chart - return null; - } - data.put("values", values); - return data; - } - - } - - /** - * Represents a custom simple bar chart. - */ - public static class SimpleBarChart extends CustomChart { - - private final Callable> callable; - - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public SimpleBarChart(String chartId, Callable> callable) { - super(chartId); - this.callable = callable; - } - - @Override - protected JSONObject getChartData() throws Exception { - JSONObject data = new JSONObject(); - JSONObject values = new JSONObject(); - Map map = callable.call(); - if (map == null || map.isEmpty()) { - // Null = skip the chart - return null; - } - for (Map.Entry entry : map.entrySet()) { - JSONArray categoryValues = new JSONArray(); - categoryValues.add(entry.getValue()); - values.put(entry.getKey(), categoryValues); - } - data.put("values", values); - return data; - } - - } - - /** - * Represents a custom advanced bar chart. - */ - public static class AdvancedBarChart extends CustomChart { - - private final Callable> callable; - - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public AdvancedBarChart(String chartId, Callable> callable) { - super(chartId); - this.callable = callable; - } - - @Override - protected JSONObject getChartData() throws Exception { - JSONObject data = new JSONObject(); - JSONObject values = new JSONObject(); - Map map = callable.call(); - if (map == null || map.isEmpty()) { - // Null = skip the chart - return null; - } - boolean allSkipped = true; - for (Map.Entry entry : map.entrySet()) { - if (entry.getValue().length == 0) { - continue; // Skip this invalid - } - allSkipped = false; - JSONArray categoryValues = new JSONArray(); - for (int categoryValue : entry.getValue()) { - categoryValues.add(categoryValue); - } - values.put(entry.getKey(), categoryValues); - } - if (allSkipped) { - // Null = skip the chart - return null; - } - data.put("values", values); - return data; - } - - } +package de.jeffclan.JeffChestSort; + +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.plugin.RegisteredServiceProvider; +import org.bukkit.plugin.ServicePriority; +import org.bukkit.plugin.java.JavaPlugin; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; + +import javax.net.ssl.HttpsURLConnection; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; +import java.util.UUID; +import java.util.concurrent.Callable; +import java.util.logging.Level; +import java.util.zip.GZIPOutputStream; + +/** + * bStats collects some data for plugin authors. + * + * Check out https://bStats.org/ to learn more about bStats! + */ +@SuppressWarnings("unchecked") +public class Metrics { + + static { + // You can use the property to disable the check in your test environment + if (System.getProperty("bstats.relocatecheck") == null || !System.getProperty("bstats.relocatecheck").equals("false")) { + // Maven's Relocate is clever and changes strings, too. So we have to use this little "trick" ... :D + final String defaultPackage = new String( + new byte[]{'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's', '.', 'b', 'u', 'k', 'k', 'i', 't'}); + final String examplePackage = new String(new byte[]{'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'}); + // We want to make sure nobody just copy & pastes the example and use the wrong package names + if (Metrics.class.getPackage().getName().equals(defaultPackage) || Metrics.class.getPackage().getName().equals(examplePackage)) { + throw new IllegalStateException("bStats Metrics class has not been relocated correctly!"); + } + } + } + + // The version of this bStats class + public static final int B_STATS_VERSION = 1; + + // The url to which the data is sent + private static final String URL = "https://bStats.org/submitData/bukkit"; + + // Should failed requests be logged? + private static boolean logFailedRequests; + + // The uuid of the server + private static String serverUUID; + + // The plugin + private final JavaPlugin plugin; + + // A list with all custom charts + private final List charts = new ArrayList<>(); + + /** + * Class constructor. + * + * @param plugin The plugin which stats should be submitted. + */ + public Metrics(JavaPlugin plugin) { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null!"); + } + this.plugin = plugin; + + // Get the config file + File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats"); + File configFile = new File(bStatsFolder, "config.yml"); + YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile); + + // Check if the config file exists + if (!config.isSet("serverUuid")) { + + // Add default values + config.addDefault("enabled", true); + // Every server gets it's unique random id. + config.addDefault("serverUuid", UUID.randomUUID().toString()); + // Should failed request be logged? + config.addDefault("logFailedRequests", false); + + // Inform the server owners about bStats + config.options().header( + "bStats collects some data for plugin authors like how many servers are using their plugins.\n" + + "To honor their work, you should not disable it.\n" + + "This has nearly no effect on the server performance!\n" + + "Check out https://bStats.org/ to learn more :)" + ).copyDefaults(true); + try { + config.save(configFile); + } catch (IOException ignored) { } + } + + // Load the data + serverUUID = config.getString("serverUuid"); + logFailedRequests = config.getBoolean("logFailedRequests", false); + if (config.getBoolean("enabled", true)) { + boolean found = false; + // Search for all other bStats Metrics classes to see if we are the first one + for (Class service : Bukkit.getServicesManager().getKnownServices()) { + try { + service.getField("B_STATS_VERSION"); // Our identifier :) + found = true; // We aren't the first + break; + } catch (NoSuchFieldException ignored) { } + } + // Register our service + Bukkit.getServicesManager().register(Metrics.class, this, plugin, ServicePriority.Normal); + if (!found) { + // We are the first! + startSubmitting(); + } + } + } + + /** + * Adds a custom chart. + * + * @param chart The chart to add. + */ + public void addCustomChart(CustomChart chart) { + if (chart == null) { + throw new IllegalArgumentException("Chart cannot be null!"); + } + charts.add(chart); + } + + /** + * Starts the Scheduler which submits our data every 30 minutes. + */ + private void startSubmitting() { + final Timer timer = new Timer(true); // We use a timer cause the Bukkit scheduler is affected by server lags + timer.scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + if (!plugin.isEnabled()) { // Plugin was disabled + timer.cancel(); + return; + } + // Nevertheless we want our code to run in the Bukkit main thread, so we have to use the Bukkit scheduler + // Don't be afraid! The connection to the bStats server is still async, only the stats collection is sync ;) + Bukkit.getScheduler().runTask(plugin, new Runnable() { + @Override + public void run() { + submitData(); + } + }); + } + }, 1000*60*5, 1000*60*30); + // Submit the data every 30 minutes, first time after 5 minutes to give other plugins enough time to start + // WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted! + // WARNING: Just don't do it! + } + + /** + * Gets the plugin specific data. + * This method is called using Reflection. + * + * @return The plugin specific data. + */ + + public JSONObject getPluginData() { + JSONObject data = new JSONObject(); + + String pluginName = plugin.getDescription().getName(); + String pluginVersion = plugin.getDescription().getVersion(); + + data.put("pluginName", pluginName); // Append the name of the plugin + data.put("pluginVersion", pluginVersion); // Append the version of the plugin + JSONArray customCharts = new JSONArray(); + for (CustomChart customChart : charts) { + // Add the data of the custom charts + JSONObject chart = customChart.getRequestJsonObject(); + if (chart == null) { // If the chart is null, we skip it + continue; + } + customCharts.add(chart); + } + data.put("customCharts", customCharts); + + return data; + } + + /** + * Gets the server specific data. + * + * @return The server specific data. + */ + private JSONObject getServerData() { + // Minecraft specific data + int playerAmount; + try { + // Around MC 1.8 the return type was changed to a collection from an array, + // This fixes java.lang.NoSuchMethodError: org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection; + Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers"); + playerAmount = onlinePlayersMethod.getReturnType().equals(Collection.class) + ? ((Collection) onlinePlayersMethod.invoke(Bukkit.getServer())).size() + : ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length; + } catch (Exception e) { + playerAmount = Bukkit.getOnlinePlayers().size(); // Just use the new method if the Reflection failed + } + int onlineMode = Bukkit.getOnlineMode() ? 1 : 0; + String bukkitVersion = org.bukkit.Bukkit.getVersion(); + bukkitVersion = bukkitVersion.substring(bukkitVersion.indexOf("MC: ") + 4, bukkitVersion.length() - 1); + + // OS/Java specific data + String javaVersion = System.getProperty("java.version"); + String osName = System.getProperty("os.name"); + String osArch = System.getProperty("os.arch"); + String osVersion = System.getProperty("os.version"); + int coreCount = Runtime.getRuntime().availableProcessors(); + + JSONObject data = new JSONObject(); + + data.put("serverUUID", serverUUID); + + data.put("playerAmount", playerAmount); + data.put("onlineMode", onlineMode); + data.put("bukkitVersion", bukkitVersion); + + data.put("javaVersion", javaVersion); + data.put("osName", osName); + data.put("osArch", osArch); + data.put("osVersion", osVersion); + data.put("coreCount", coreCount); + + return data; + } + + /** + * Collects the data and sends it afterwards. + */ + private void submitData() { + final JSONObject data = getServerData(); + + JSONArray pluginData = new JSONArray(); + // Search for all other bStats Metrics classes to get their plugin data + for (Class service : Bukkit.getServicesManager().getKnownServices()) { + try { + service.getField("B_STATS_VERSION"); // Our identifier :) + + for (RegisteredServiceProvider provider : Bukkit.getServicesManager().getRegistrations(service)) { + try { + pluginData.add(provider.getService().getMethod("getPluginData").invoke(provider.getProvider())); + } catch (NullPointerException | NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) { } + } + } catch (NoSuchFieldException ignored) { } + } + + data.put("plugins", pluginData); + + // Create a new thread for the connection to the bStats server + new Thread(new Runnable() { + @Override + public void run() { + try { + // Send the data + sendData(data); + } catch (Exception e) { + // Something went wrong! :( + if (logFailedRequests) { + plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats of " + plugin.getName(), e); + } + } + } + }).start(); + } + + /** + * Sends the data to the bStats server. + * + * @param data The data to send. + * @throws Exception If the request failed. + */ + private static void sendData(JSONObject data) throws Exception { + if (data == null) { + throw new IllegalArgumentException("Data cannot be null!"); + } + if (Bukkit.isPrimaryThread()) { + throw new IllegalAccessException("This method must not be called from the main thread!"); + } + HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection(); + + // Compress the data to save bandwidth + byte[] compressedData = compress(data.toString()); + + // Add headers + connection.setRequestMethod("POST"); + connection.addRequestProperty("Accept", "application/json"); + connection.addRequestProperty("Connection", "close"); + connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request + connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length)); + connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format + connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION); + + // Send data + connection.setDoOutput(true); + DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream()); + outputStream.write(compressedData); + outputStream.flush(); + outputStream.close(); + + connection.getInputStream().close(); // We don't care about the response - Just send our data :) + } + + /** + * Gzips the given String. + * + * @param str The string to gzip. + * @return The gzipped String. + * @throws IOException If the compression failed. + */ + private static byte[] compress(final String str) throws IOException { + if (str == null) { + return null; + } + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + GZIPOutputStream gzip = new GZIPOutputStream(outputStream); + gzip.write(str.getBytes("UTF-8")); + gzip.close(); + return outputStream.toByteArray(); + } + + /** + * Represents a custom chart. + */ + public static abstract class CustomChart { + + // The id of the chart + final String chartId; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + */ + CustomChart(String chartId) { + if (chartId == null || chartId.isEmpty()) { + throw new IllegalArgumentException("ChartId cannot be null or empty!"); + } + this.chartId = chartId; + } + + private JSONObject getRequestJsonObject() { + JSONObject chart = new JSONObject(); + chart.put("chartId", chartId); + try { + JSONObject data = getChartData(); + if (data == null) { + // If the data is null we don't send the chart. + return null; + } + chart.put("data", data); + } catch (Throwable t) { + if (logFailedRequests) { + Bukkit.getLogger().log(Level.WARNING, "Failed to get data for custom chart with id " + chartId, t); + } + return null; + } + return chart; + } + + protected abstract JSONObject getChartData() throws Exception; + + } + + /** + * Represents a custom simple pie. + */ + public static class SimplePie extends CustomChart { + + private final Callable callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SimplePie(String chartId, Callable callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JSONObject getChartData() throws Exception { + JSONObject data = new JSONObject(); + String value = callable.call(); + if (value == null || value.isEmpty()) { + // Null = skip the chart + return null; + } + data.put("value", value); + return data; + } + } + + /** + * Represents a custom advanced pie. + */ + public static class AdvancedPie extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public AdvancedPie(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JSONObject getChartData() throws Exception { + JSONObject data = new JSONObject(); + JSONObject values = new JSONObject(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() == 0) { + continue; // Skip this invalid + } + allSkipped = false; + values.put(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + data.put("values", values); + return data; + } + } + + /** + * Represents a custom drilldown pie. + */ + public static class DrilldownPie extends CustomChart { + + private final Callable>> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public DrilldownPie(String chartId, Callable>> callable) { + super(chartId); + this.callable = callable; + } + + @Override + public JSONObject getChartData() throws Exception { + JSONObject data = new JSONObject(); + JSONObject values = new JSONObject(); + Map> map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean reallyAllSkipped = true; + for (Map.Entry> entryValues : map.entrySet()) { + JSONObject value = new JSONObject(); + boolean allSkipped = true; + for (Map.Entry valueEntry : map.get(entryValues.getKey()).entrySet()) { + value.put(valueEntry.getKey(), valueEntry.getValue()); + allSkipped = false; + } + if (!allSkipped) { + reallyAllSkipped = false; + values.put(entryValues.getKey(), value); + } + } + if (reallyAllSkipped) { + // Null = skip the chart + return null; + } + data.put("values", values); + return data; + } + } + + /** + * Represents a custom single line chart. + */ + public static class SingleLineChart extends CustomChart { + + private final Callable callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SingleLineChart(String chartId, Callable callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JSONObject getChartData() throws Exception { + JSONObject data = new JSONObject(); + int value = callable.call(); + if (value == 0) { + // Null = skip the chart + return null; + } + data.put("value", value); + return data; + } + + } + + /** + * Represents a custom multi line chart. + */ + public static class MultiLineChart extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public MultiLineChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JSONObject getChartData() throws Exception { + JSONObject data = new JSONObject(); + JSONObject values = new JSONObject(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() == 0) { + continue; // Skip this invalid + } + allSkipped = false; + values.put(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + data.put("values", values); + return data; + } + + } + + /** + * Represents a custom simple bar chart. + */ + public static class SimpleBarChart extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SimpleBarChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JSONObject getChartData() throws Exception { + JSONObject data = new JSONObject(); + JSONObject values = new JSONObject(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + for (Map.Entry entry : map.entrySet()) { + JSONArray categoryValues = new JSONArray(); + categoryValues.add(entry.getValue()); + values.put(entry.getKey(), categoryValues); + } + data.put("values", values); + return data; + } + + } + + /** + * Represents a custom advanced bar chart. + */ + public static class AdvancedBarChart extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public AdvancedBarChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JSONObject getChartData() throws Exception { + JSONObject data = new JSONObject(); + JSONObject values = new JSONObject(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue().length == 0) { + continue; // Skip this invalid + } + allSkipped = false; + JSONArray categoryValues = new JSONArray(); + for (int categoryValue : entry.getValue()) { + categoryValues.add(categoryValue); + } + values.put(entry.getKey(), categoryValues); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + data.put("values", values); + return data; + } + + } } \ No newline at end of file diff --git a/src/main/java/de/jeffclan/utils/Utils.java b/src/de/jeffclan/utils/Utils.java similarity index 95% rename from src/main/java/de/jeffclan/utils/Utils.java rename to src/de/jeffclan/utils/Utils.java index a5ebcbd..cc228fe 100644 --- a/src/main/java/de/jeffclan/utils/Utils.java +++ b/src/de/jeffclan/utils/Utils.java @@ -1,30 +1,30 @@ -package de.jeffclan.utils; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; - -public class Utils { - public static byte[] getBytes(InputStream is) throws IOException { - - int len; - int size = 1024; - byte[] buf; - - if (is instanceof ByteArrayInputStream) { - size = is.available(); - buf = new byte[size]; - len = is.read(buf, 0, size); - } else { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - buf = new byte[size]; - while ((len = is.read(buf, 0, size)) != -1) - bos.write(buf, 0, len); - buf = bos.toByteArray(); - } - return buf; - } - - -} +package de.jeffclan.utils; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +public class Utils { + public static byte[] getBytes(InputStream is) throws IOException { + + int len; + int size = 1024; + byte[] buf; + + if (is instanceof ByteArrayInputStream) { + size = is.available(); + buf = new byte[size]; + len = is.read(buf, 0, size); + } else { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + buf = new byte[size]; + while ((len = is.read(buf, 0, size)) != -1) + bos.write(buf, 0, len); + buf = bos.toByteArray(); + } + return buf; + } + + +} diff --git a/src/main/resources/categories/900-valuables.default.txt b/src/de/jeffclan/utils/categories/900-valuables.default.txt similarity index 91% rename from src/main/resources/categories/900-valuables.default.txt rename to src/de/jeffclan/utils/categories/900-valuables.default.txt index d8f3d52..f7d9887 100644 --- a/src/main/resources/categories/900-valuables.default.txt +++ b/src/de/jeffclan/utils/categories/900-valuables.default.txt @@ -1,27 +1,27 @@ -diamond -emerald -diamond_ore -emerald_ore -iron_ingot -iron_ore -gold_ingot -gold_ore -gold_block -diamond_block -iron_block -lapis_lazuli -lapis_block -obsidian -beacon -conduit -scute -elytra -ender_pearl -ender_eye -*_horse_armor -music_disc_* -heart_of_the_sea -nautilus_shell -gold_nugget -iron_nugget +diamond +emerald +diamond_ore +emerald_ore +iron_ingot +iron_ore +gold_ingot +gold_ore +gold_block +diamond_block +iron_block +lapis_lazuli +lapis_block +obsidian +beacon +conduit +scute +elytra +ender_pearl +ender_eye +*_horse_armor +music_disc_* +heart_of_the_sea +nautilus_shell +gold_nugget +iron_nugget enchanted_book \ No newline at end of file diff --git a/src/main/resources/categories/910-tools.default.txt b/src/de/jeffclan/utils/categories/910-tools.default.txt similarity index 90% rename from src/main/resources/categories/910-tools.default.txt rename to src/de/jeffclan/utils/categories/910-tools.default.txt index 316334d..667decc 100644 --- a/src/main/resources/categories/910-tools.default.txt +++ b/src/de/jeffclan/utils/categories/910-tools.default.txt @@ -1,10 +1,10 @@ -*_pickaxe -*_shovel -*_hoe -*_axe -flint_and_steel -fishing_rod -compass -clock -shears +*_pickaxe +*_shovel +*_hoe +*_axe +flint_and_steel +fishing_rod +compass +clock +shears lead \ No newline at end of file diff --git a/src/main/resources/categories/920-combat.default.txt b/src/de/jeffclan/utils/categories/920-combat.default.txt similarity index 91% rename from src/main/resources/categories/920-combat.default.txt rename to src/de/jeffclan/utils/categories/920-combat.default.txt index ac853ca..4ec1951 100644 --- a/src/main/resources/categories/920-combat.default.txt +++ b/src/de/jeffclan/utils/categories/920-combat.default.txt @@ -1,13 +1,13 @@ -turtle_helmet -bow -arrow -*_sword -*_helmet -*_chestplate -*_leggings -*_boots -spectral_arrow -tipped_arrow -shield -totem_of_undying +turtle_helmet +bow +arrow +*_sword +*_helmet +*_chestplate +*_leggings +*_boots +spectral_arrow +tipped_arrow +shield +totem_of_undying trident \ No newline at end of file diff --git a/src/main/resources/categories/930-brewing.default.txt b/src/de/jeffclan/utils/categories/930-brewing.default.txt similarity index 93% rename from src/main/resources/categories/930-brewing.default.txt rename to src/de/jeffclan/utils/categories/930-brewing.default.txt index 9b7370c..5ad8c90 100644 --- a/src/main/resources/categories/930-brewing.default.txt +++ b/src/de/jeffclan/utils/categories/930-brewing.default.txt @@ -1,15 +1,15 @@ -ghast_tear -potion -glass_bottle -fermented_spider_eye -blaze_powder -magma_cream -brewing_stand -cauldron -glistering_melon_slice -golden_carrot -rabbit_foot -dragon_breath -splash_potion -lingering_potion +ghast_tear +potion +glass_bottle +fermented_spider_eye +blaze_powder +magma_cream +brewing_stand +cauldron +glistering_melon_slice +golden_carrot +rabbit_foot +dragon_breath +splash_potion +lingering_potion phantom_membrane \ No newline at end of file diff --git a/src/main/resources/categories/940-food.default.txt b/src/de/jeffclan/utils/categories/940-food.default.txt similarity index 90% rename from src/main/resources/categories/940-food.default.txt rename to src/de/jeffclan/utils/categories/940-food.default.txt index eccfedb..80b1de6 100644 --- a/src/main/resources/categories/940-food.default.txt +++ b/src/de/jeffclan/utils/categories/940-food.default.txt @@ -1,30 +1,30 @@ -apple -baked_potato -beef -beetroot -beetroot_soup -bread -mushroom_stew -porkchop -cooked_porkchop -cod -salmon -cooked_cod -cooked_salmon -cake -cookie -melon_slice -dried_kelp -cooked_beef -chicken -cooked_chicken -carrot -potato -pumpkin_pie -rabbit -cooked_rabbit -rabbit_stew -mutton -cooked_mutton -wheat +apple +baked_potato +beef +beetroot +beetroot_soup +bread +mushroom_stew +porkchop +cooked_porkchop +cod +salmon +cooked_cod +cooked_salmon +cake +cookie +melon_slice +dried_kelp +cooked_beef +chicken +cooked_chicken +carrot +potato +pumpkin_pie +rabbit +cooked_rabbit +rabbit_stew +mutton +cooked_mutton +wheat *_seeds \ No newline at end of file diff --git a/src/main/resources/categories/950-redstone.default.txt b/src/de/jeffclan/utils/categories/950-redstone.default.txt similarity index 92% rename from src/main/resources/categories/950-redstone.default.txt rename to src/de/jeffclan/utils/categories/950-redstone.default.txt index ff37152..29c2e9b 100644 --- a/src/main/resources/categories/950-redstone.default.txt +++ b/src/de/jeffclan/utils/categories/950-redstone.default.txt @@ -1,23 +1,23 @@ -dispenser -note_block -sticky_piston -piston -tnt -lever -*_pressure_plate -redstone -*_button -redstone_lamp -redstone_torch -tripwire_hook -trapped_chest -daylight_detector -redstone_block -redstone_ore -hopper -dropper -iron_trapdoor -observer -iron_door -repeater +dispenser +note_block +sticky_piston +piston +tnt +lever +*_pressure_plate +redstone +*_button +redstone_lamp +redstone_torch +tripwire_hook +trapped_chest +daylight_detector +redstone_block +redstone_ore +hopper +dropper +iron_trapdoor +observer +iron_door +repeater comparator \ No newline at end of file diff --git a/src/main/resources/categories/960-wood.default.txt b/src/de/jeffclan/utils/categories/960-wood.default.txt similarity index 100% rename from src/main/resources/categories/960-wood.default.txt rename to src/de/jeffclan/utils/categories/960-wood.default.txt diff --git a/src/main/resources/categories/970-stone.default.txt b/src/de/jeffclan/utils/categories/970-stone.default.txt similarity index 100% rename from src/main/resources/categories/970-stone.default.txt rename to src/de/jeffclan/utils/categories/970-stone.default.txt diff --git a/src/main/resources/categories/980-plants.default.txt b/src/de/jeffclan/utils/categories/980-plants.default.txt similarity index 100% rename from src/main/resources/categories/980-plants.default.txt rename to src/de/jeffclan/utils/categories/980-plants.default.txt diff --git a/src/main/resources/categories/981-corals.default.txt b/src/de/jeffclan/utils/categories/981-corals.default.txt similarity index 100% rename from src/main/resources/categories/981-corals.default.txt rename to src/de/jeffclan/utils/categories/981-corals.default.txt