diff --git a/pom.xml b/pom.xml index 09a7613..f6b4965 100644 --- a/pom.xml +++ b/pom.xml @@ -3,8 +3,8 @@ 4.0.0 com.dre - brewery - 1.8.2 + Brewery + 2.0-beta1 Brewery @@ -14,6 +14,7 @@ src + test @@ -22,6 +23,7 @@ ${project.basedir}/resources **/*.yml + **/*.txt target/** @@ -76,7 +78,7 @@ spigot-repo - https://hub.spigotmc.org/nexus/content/groups/public/ + https://hub.spigotmc.org/nexus/content/repositories/snapshots/ vault-repo @@ -87,7 +89,7 @@ http://maven.sk89q.com/repo/ - + jitpack.io https://jitpack.io @@ -111,7 +113,7 @@ org.bukkit bukkit - 1.14-R0.1-SNAPSHOT + 1.14.4-R0.1-SNAPSHOT provided @@ -226,12 +228,31 @@ + + com.github.TheBusyBiscuit + Slimefun4 + 4bb9abd247 + provided + + + + com.dre + ExtPluginBridge + 1.0 + provided + org.bstats bstats-bukkit 1.5 compile + + org.jetbrains + annotations + 16.0.2 + compile + diff --git a/resources/config/patches/de18.txt b/resources/config/patches/de18.txt new file mode 100644 index 0000000..d123be4 --- /dev/null +++ b/resources/config/patches/de18.txt @@ -0,0 +1,58 @@ + + + +# -- Eigene Items Definieren -- +# Die festgelegte id kann dann in einem Rezept verwendet werden + +# matchAny: true wenn es schon reicht wenn eine der Angaben zutrifft +# material: Welche Art das Item haben muss +# name: Welchen Namen das Item haben muss (Farbcodes möglich: z.b. &6) +# lore: Was in der Lore des Items stehen muss + +customItems: + bsp-item: +# Ein Barriere Item das Mauer heißt und in der Lore die angegebene Zeile hat +%%%%MAT1%%%% + name: 'Mauer' + lore: + - '&7Besonders gut geschützt' + + bsp-item2: +# Mit matchAny muss nur eine der Angaben zutreffen. +# Hier also eine der Türarten, oder ein Item namens Buchenholztür, oder ein Item mit 'Eine Tür' in der Lore + matchAny: true +%%%%MAT2%%%% + name: + - 'Buchenholztür' + lore: + - 'Eine Tür' + + himbeere: + name: '&cHimbeere' + + +# -- Zutaten im Kessel -- +# Hier kann angegeben werden welche Zutaten in den Kessel getan werden können und was mit ihnen geschieht. + + # name: Name des Basistrankes der aus dem Kessel kommt (Farbcodes möglich: z.b. &6) + # ingredients: Auflistung von 'Material/Anzahl' + # Halte ein Item in der Hand und benutze /brew ItemName um dessen Material herauszufinden und für ein Rezept zu benutzen + # (Item-ids anstatt Material können in Bukkit nicht mehr benutzt werden) + # Eine Liste von allen Materialien kann hier gefunden werden: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html + # color: Farbe des Trankes der aus dem Kessel kommt + # Benutzbare Farben: DARK_RED, RED, BRIGHT_RED, ORANGE, PINK, BLUE, CYAN, WATER, GREEN, BLACK, GREY, BRIGHT_GREY + # Oder RGB Farben (Hex: also zB '99FF33') (Ohne #) (mit '') (Einfach nach "HTML color" im Internet suchen) + # lore: Auflistung von zusätzlichem Text auf dem Trank. (Farbcodes möglich: z.b. &6) + +cauldron: + bsp: + name: Beispiel + ingredients: + - Bedrock/2 + - Diamond + color: BLACK + lore: + - Ein Beispiel für einen Basistrank + - So kommt er aus dem Kessel + +# -- Eine Zutat: -- diff --git a/resources/config/patches/en18.txt b/resources/config/patches/en18.txt new file mode 100644 index 0000000..5e34330 --- /dev/null +++ b/resources/config/patches/en18.txt @@ -0,0 +1,58 @@ + + + +# -- Define custom items -- +# The defined id can then be used in recipes + +# matchAny: true if it is already enough if one of the info matches +# material: Which type the item has to be +# name: Which name the item has to be (Formatting codes possible: such as &6) +# lore: What has to be in the lore of the item + +customItems: + ex-item: + # A Barrier item called Wall and has the given line in its lore +%%%%MAT1%%%% + name: 'Wall' + lore: + - '&7Very well protected' + + ex-item2: + # Using matchAny only one of the following has to match. + # In this case on of the door types, or an item called Beechwood Door, or an item with 'A door' in its lore + matchAny: true +%%%%MAT2%%%% + name: + - 'Beechwood Door' + lore: + - 'A door' + + rasp: + name: '&cRaspberry' + + +# -- Ingredients in the Cauldron -- +# Which Ingredients are accepted by the Cauldron and the base potion resulting from them + + # name: Name of the base potion coming out of the Cauldron (Formatting codes possible: such as &6) + # ingredients: List of 'material/amount' + # With an item in your hand, use /brew ItemName to get its material for use in a recipe + # (Item-ids instead of material are not supported by bukkit anymore and will not work) + # A list of materials can be found here: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html + # color: Color of the potion from a cauldron. + # Usable Colors: DARK_RED, RED, BRIGHT_RED, ORANGE, PINK, BLUE, CYAN, WATER, GREEN, BLACK, GREY, BRIGHT_GREY + # Or RGB colors (hex: for example '99FF33') (with '') (search for "HTML color" on the internet) + # lore: List of additional text on the base potion. (Formatting codes possible: such as &6) + +cauldron: + ex: + name: Example + ingredients: + - Bedrock/2 + - Diamond + color: BLACK + lore: + - An example for a Base Potion + - This is how it comes out of a Cauldron + + # -- One Ingredient: -- diff --git a/resources/config/patches/fr18.txt b/resources/config/patches/fr18.txt new file mode 100644 index 0000000..f2bda51 --- /dev/null +++ b/resources/config/patches/fr18.txt @@ -0,0 +1,59 @@ + + + +# -- Définir des objets personnalisés -- +# L'id défini peut ensuite être utilisé dans les recettes + +# matchAny: si c'est déjà assez si l'une des infos correspond +# material: le type d'article à utiliser +# name: Quel nom l'article doit porter (codes de formatage possibles : tels que &6) +# lore: Ce qui doit être dans la lore de l'objet + +customItems: + ex-item: + # Un objet Barrière appelé "Wall" et qui a la ligne donnée dans sa lore +%%%%MAT1%%%% + name: 'Wall' + lore: + - '&7Very well protected' + + ex-item2: + # En utilisant matchAny, un seul des éléments suivants doit correspondre. + # Dans ce cas, l'un des types de porte, ou un article appelé "Beechwood Door", ou un objet avec "A door" dans sa lore. + matchAny: true +%%%%MAT2%%%% + name: + - 'Beechwood Door' + lore: + - 'A door' + + rasp: + name: '&cRaspberry' + + +# -- Ingrédients dans le chaudron -- +# Quels sont les ingrédients acceptés par le chaudron et la potion de base qui en résulte + + # name: Nom de la potion de base qui sort du chaudron (codes de formatage possibles : tels que &6) + # ingredients: Liste des 'matériaux/montant' + # Avec un objet en main, utilisez /brew ItemName pour obtenir son matériau pour une recette de cuisine + # (Les id d'objets à la place des matériaux sont obsolètes pour bukkit) + # Une liste des matériaux peuvent-être trouvés ici: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html + # color : Couleur de la potion provenant d'un chaudron. + # Couleurs disponibles : DARK_RED, RED, BRIGHT_RED, ORANGE, PINK, BLUE, CYAN, WATER, GREEN, BLACK, GREY, BRIGHT_GREY + # (Dans l'ordre : Rouge foncé, Rouge, Rouge clair, Orange, Rose, Bleu, Cyan, Eau, Vert, Noir, Gris, Gris clair) + # Ou couleurs RGB (hex: par exemple '99FF33') (avec '') (recherche de "HTML color" sur internet) + # lore: Liste de texte supplémentaire sur la potion de base. (Formatting codes possible: such as &6) + +cauldron: + ex: + name: Exemple + ingredients: + - Bedrock/2 + - Diamond + color: BLACK + lore: + - Un exemple pour une potion de base + - Voici comment il sort d'un chaudron + + # -- Un ingrédient: -- diff --git a/resources/config/v12/de/config.yml b/resources/config/v12/de/config.yml index 8be0305..ab2c9ea 100644 --- a/resources/config/v12/de/config.yml +++ b/resources/config/v12/de/config.yml @@ -3,7 +3,6 @@ # -- Verschiedene Einstellungen -- # Standardeinstellungen sind in [] angegeben -# Löschen einzelner Einstellungen deaktiviert sie # Sprachedatei die genutzt werden sollte (befindet sich in plugins/Brewery/languages) language: de @@ -51,14 +50,24 @@ hangoverDays: 7 colorInBarrels: true colorInBrewer: true +# Ob in den Iteminformationen immer 1-5 Sterne für die Qualität angezeigt werden sollen, oder nur beim brauen [true] +alwaysShowQuality: true + +# Ob in den Iteminformationen immer der Alkoholgehalt angezeigt weden soll, oder nur im Braustand [false] +alwaysShowAlc: false + # Ob große Fässer an jedem Block geöffnet werden können, nicht nur an Zapfhahn und Schild. Bei kleinen Fässern geht dies immer. [true] openLargeBarrelEverywhere: true # Wie viele Brewery Getränke in die Minecraft Fässer getan werden können [6] maxBrewsInMCBarrels: 6 -# In den Serverlog loggen was der Spieler tatsächlich geschrieben hat, bevor seine Worte verändert wurden [false] -logRealChat: false +# Benutzte Zutaten und andere Brau-Daten werden in allen Brewery Tränken gespeichert. Um zu verhindern, +# dass gehackte clients diese Daten auslesen um Rezepte herauszufinden, können diese encodiert werden. +# Einziger Nachteil: Tränke können nur auf Servern mit dem gleichen encodeKey benutzt werden. +# Dies kann also aktiviert werden um Rezept-cheating schwerer zu machen, aber keine Tränke per World Download, Schematic, o.ä. geteilt werden. [false] +enableEncode: false +encodeKey: 0 # Aktiviert das Suchen nach Updates für Brewery mit der curseforge api [true] # Wenn ein Update gefunden wurde, wird dies bei Serverstart im log angezeigt, sowie OPs benachrichtigt @@ -67,11 +76,119 @@ updateCheck: true # Autosave Intervall in Minuten [3] autosave: 3 +# Debug Nachrichten im Log anzeigen [false] +debug: false + # Config Version -version: '1.8' +version: '2.0' oldMat: true + +# -- Eigene Items Definieren -- +# Die festgelegte id kann dann in einem Rezept verwendet werden + +# matchAny: true wenn es schon reicht wenn eine der Angaben zutrifft +# material: Welche Art das Item haben muss +# name: Welchen Namen das Item haben muss (Farbcodes möglich: z.b. &6) +# lore: Was in der Lore des Items stehen muss + +customItems: + bsp-item: + # Ein Bedrock Item das Mauer heißt und in der Lore die angegebene Zeile hat + material: BEDROCK + name: 'Mauer' + lore: + - '&7Besonders gut geschützt' + + bsp-item2: + # Mit matchAny muss nur eine der Angaben zutreffen. + # Hier also eine der Türarten, oder ein Item namens Buchenholztür, oder ein Item mit 'Eine Tür' in der Lore + matchAny: true + material: + - WOODEN_DOOR + - IRON_DOOR + name: + - 'Buchenholztür' + lore: + - 'Eine Tür' + + himbeere: + name: '&cHimbeere' + + +# -- Zutaten im Kessel -- +# Hier kann angegeben werden welche Zutaten in den Kessel getan werden können und was mit ihnen geschieht. + + # name: Name des Basistrankes der aus dem Kessel kommt (Farbcodes möglich: z.b. &6) + # ingredients: Auflistung von 'Material/Anzahl' + # Halte ein Item in der Hand und benutze /brew ItemName um dessen Material herauszufinden und für ein Rezept zu benutzen + # (Item-ids anstatt Material können in Bukkit nicht mehr benutzt werden) + # Eine Liste von allen Materialien kann hier gefunden werden: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html + # color: Farbe des Trankes der aus dem Kessel kommt + # Benutzbare Farben: DARK_RED, RED, BRIGHT_RED, ORANGE, PINK, BLUE, CYAN, WATER, GREEN, BLACK, GREY, BRIGHT_GREY + # Oder RGB Farben (Hex: also zB '99FF33') (Ohne #) (mit '') (Einfach nach "HTML color" im Internet suchen) + # lore: Auflistung von zusätzlichem Text auf dem Trank. (Farbcodes möglich: z.b. &6) + + +cauldron: + bsp: + name: Beispiel + ingredients: + - BEDROCK/2 + - DIAMOND + color: BLACK + lore: + - Ein Beispiel für einen Basistrank + - So kommt er aus dem Kessel + +# -- Eine Zutat: -- + wheat: + name: Getreideferment + ingredients: WHEAT + + sugarcane: + name: Zuckersud + ingredients: SUGAR_CANE + color: 'f1ffad' # gelbliches grün + + apple: + name: Apfelmost + ingredients: APPLE + + potato: + name: Kartoffelmaische + ingredients: POTATO_ITEM + + grass: + name: Kräuterbrühe + ingredients: LONG_GRASS + color: '99ff66' # helles grün + + rmushroom: + name: Pilzsud + ingredients: RED_MUSHROOM + color: 'ff5c33' # bernsteinrot + + cocoa: + name: Kakaobrühe + ingredients: INK_SACK + color: '804600' # mokka + + milk: + name: Milchiges Wasser + ingredients: MILK_BUCKET + color: BRIGHT_GREY + + # -- Mehrere Zutaten: -- + apfelmet_basis: + name: Apfel-Zuckersud + ingredients: + - SUGAR_CANE/3 + - APPLE + color: 'e1ff4d' # grünliches gelb + + # -- Rezepte für Getränke -- # name: Verschiedene Namen für schlecht/mittel/gut (Farbcodes möglich: z.b. &6) @@ -80,8 +197,8 @@ oldMat: true # (Item-ids anstatt Material können in Bukkit nicht mehr benutzt werden) # Eine Liste von allen Materialien kann hier gefunden werden: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html # Es kann ein Data-Wert (durability) angegeben werden, weglassen ignoriert diesen beim hinzufügen einer Zutat -# Wenn Vault installiert ist können normale englische Item Namen verwendet werden, anstatt Material, ID und Data! -# Vault erkennt Namen wie "Jungle Leaves" anstatt "LEAVES,3". Dies macht es viel einfacher! +# Plugin Items mit 'Plugin:Id' (Im Moment ExoticGarden, Slimefun, MMOItems, Brewery) +# Oder ein oben definiertes Custom Item # cookingtime: Zeit in Echtminuten die die Zutaten kochen müssen # distillruns: Wie oft destilliert werden muss für vollen Alkoholgehalt (0=ohne Destillieren) # distilltime: Wie lange (in sekunden) ein Destillations-Durchlauf braucht (0=Standard Zeit von 40 sek) MC Standard wäre 20 sek @@ -93,6 +210,12 @@ oldMat: true # Oder RGB Farben (Hex: also zB '99FF33') (Ohne #) (mit '') (Einfach nach "HTML color" im Internet suchen) # difficulty: 1-10 Genauigkeit der Einhaltung der Vorgaben (1 = ungenau/einfach 10 = sehr genau/schwer) # alcohol: Alkoholgehalt 0-100 in absoluter Menge bei perfektem Getränk (wird dem Spieler hinzugefügt, bei 100 = tot) +# lore: Auflistung von zusätzlichem Text auf dem fertigen Trank. (Farbcodes möglich: z.b. &6) +# Lore nur für bestimmte Qualität möglich mit + Schlecht, ++ Mittel, +++ Gut, vorne anhängen. +# servercommands: Liste von Befehlen ausgeführt vom Server wenn der Trank getrunken wird +# playercommands: Liste von Befehlen ausgeführt vom Spieler wenn der Trank getrunken wird +# drinkmessage: Nachricht im Chat beim trinken des Trankes +# drinktitle: Nachricht als Titel auf dem Bildschirm an den Spieler beim trinken des Trankes # effects: Auflistung Effekt/Level/Dauer Besonderere Trank-Effekte beim Trinken, Dauer in sek. # Ein 'X' an den Namen anhängen, um ihn zu verbergen. Bsp: 'POISONX/2/10' (WEAKNESS, INCREASE_DAMAGE, SLOW und SPEED sind immer verborgen.) # Effekte sind ab der 1.9 immer verborgen, wegen Änderungen an den Tränken. @@ -103,16 +226,16 @@ oldMat: true recipes: # Ein vollständiges Beispiel zuerst: - 0: + bsp: name: Schlechtes Beispiel/Beispiel/Gutes Beispiel ingredients: - - SUGAR_CANE/5 - DIAMOND/1 - INK_SACK,3/20 - WOOD,1/8 - BEDROCK/1 -# - Jungle Leaves/64 # Nur mit Vault -# - Green Dye/6 # Nur mit Vault + - Brewery:Weißbier/2 +# - ExoticGarden:Grape/3 + - bsp-item/4 cookingtime: 3 distillruns: 2 distilltime: 60 @@ -121,12 +244,26 @@ recipes: color: DARK_RED difficulty: 3 alcohol: 23 + lore: + - Dies ist ein Beispiel Trank + - ++Ganz normales Beispiel + - Dies würde auf dem Trank stehen + - + Riecht eklig + - ++ Riecht ganz ok + - +++ Riecht richtig gut + servercommands: + - weather clear + playercommands: + - homes + drinkmessage: Schmeckt toll + drinktitle: Wärmt dich von innen effects: - FIRE_RESISTANCE/20 - HEAL/1 - WEAKNESS/2-3/50-60 - POISONX/1-0/20-0 - 1: + + weißbier: name: Ranziges Weißbier/Weißbier/Feines Weißbier ingredients: - WHEAT/3 @@ -137,7 +274,8 @@ recipes: color: BRIGHT_GREY difficulty: 1 alcohol: 5 - 2: + + bier: name: Ranziges Bier/Bier/Feines Bier ingredients: - WHEAT/6 @@ -147,8 +285,11 @@ recipes: age: 3 color: ORANGE difficulty: 1 + lore: + - +++ &8Das perlt alcohol: 6 - 3: + + dunkelbier: name: Ranziges Dunkelbier/Dunkelbier/Feines Dunkelbier ingredients: - WHEAT/6 @@ -159,7 +300,8 @@ recipes: color: BLACK difficulty: 2 alcohol: 7 - 4: + + met: name: Scheußlicher Met/Met/&6Goldener Met ingredients: - SUGAR_CANE/6 @@ -170,7 +312,10 @@ recipes: color: ORANGE difficulty: 2 alcohol: 9 - 5: + lore: + - +++ Hat einen goldenen Schein + + apfelmet: name: Apfelmet/Süßer Apfelmet/&6Goldensüßer Apfelmet ingredients: - SUGAR_CANE/6 @@ -182,9 +327,14 @@ recipes: color: ORANGE difficulty: 4 alcohol: 12 + lore: + - + Ist da wirklich Apfel drin? + - ++ Schmeckt nach süßem Apfel + - +++ Hat eine wunderbare Apfelnote effects: - WATER_BREATHINGX/1-2/150 - 6: + + rum: name: Bitterer Rum/Würziger Rum/&6Goldener Rum ingredients: - SUGAR_CANE/14 @@ -199,7 +349,8 @@ recipes: effects: - FIRE_RESISTANCE/1/20-100 - POISONX/1-0/30-0 - 7: + + vodka: name: Abgeranzter Vodka/Vodka/Russischer Vodka ingredients: - POTATO_ITEM/10 @@ -209,10 +360,13 @@ recipes: color: BRIGHT_GREY difficulty: 4 alcohol: 20 + lore: + - + &8Fast nicht trinkbar effects: - WEAKNESS/15 - POISON/10 - 8: + + absinth: name: minderwertiger Absinth/Absinth/Starker Absinth ingredients: - LONG_GRASS/15 @@ -224,7 +378,8 @@ recipes: alcohol: 45 effects: - POISON/20-30 - 9: + + kartoffelsuppe: name: Kartoffelsuppe ingredients: - POTATO_ITEM/5 @@ -234,7 +389,8 @@ recipes: difficulty: 1 effects: - HEAL/0-1 - 10: + + kaffee: name: Fader Kaffee/Kaffee/Starker Kaffee ingredients: - INK_SACK,3/12 @@ -242,6 +398,7 @@ recipes: cookingtime: 2 color: BLACK difficulty: 3 + lore: + &8Bestimmt schon eine Woche alt effects: - REGENERATION/1/2-5 - SPEED/1/30-140 @@ -251,27 +408,13 @@ recipes: # Der Serveradmin kann neue Rezepte hinzufügen und bestehende ändern, um das Abschauen aus der Standardconfig zu verhindern. -# cooked: ALLE möglichen Zutaten und die nach dem Gähren daraus entstehenden Tranknamen: -# [Beispiel] MATERIAL: Name nach Gähren - -cooked: - WHEAT: Getreideferment - SUGAR_CANE: Zuckersud - APPLE: Apfelmost - POTATO_ITEM: Kartoffelmaische - LONG_GRASS: Kräuterbrühe - RED_MUSHROOM: Pilzsud - INK_SACK: Farbige Brühe - MILK_BUCKET: Milchiges Wasser - - - # -- Plugin Kompatiblität -- # Andere Plugins (wenn installiert) nach Rechten zum öffnen von Fässern checken [true] useWorldGuard: true useLWC: true useGriefPrevention: true +useGMInventories: true useCitadel: true # Änderungen an Fassinventaren mit LogBlock aufzeichen [true] @@ -286,6 +429,9 @@ useLogBlock: true # Unten kann noch eingestellt werden wie und was verändert wird enableChatDistortion: true +# In den Serverlog loggen was der Spieler tatsächlich geschrieben hat, bevor seine Worte verändert wurden [false] +logRealChat: false + # Text nach den angegebenen Kommandos wird bei Trunkenheit ebenfalls Verändert (Liste) [- /gl] distortCommands: - /gl diff --git a/resources/config/v12/en/config.yml b/resources/config/v12/en/config.yml index 38eadb4..746130e 100644 --- a/resources/config/v12/en/config.yml +++ b/resources/config/v12/en/config.yml @@ -3,7 +3,6 @@ # -- Settings -- # Defaults are written in [] -# Deleting of single settings disables them # Languagefile to be used (found in plugins/Brewery/languages) language: en @@ -51,12 +50,26 @@ hangoverDays: 7 colorInBarrels: true colorInBrewer: true +# Always show the 1-5 stars on the item depending on the quality. If false, they will only appear when brewing [true] +alwaysShowQuality: true + +# Always show the alcohol content on the item. If false, it will only show in the brewing stand [false] +alwaysShowAlc: false + # If a Large Barrel can be opened by clicking on any of its blocks, not just Spigot or Sign. This is always true for Small Barrels. [true] openLargeBarrelEverywhere: true # How many Brewery drinks can be put into the Minecraft barrels [6] maxBrewsInMCBarrels: 6 +# The used Ingredients and other brewing-data is saved to all Brewery Items. To prevent +# hacked clients from reading what exactly was used to brew an item, the data can be encoded/scrambled. +# This is a fast process to stop players from hacking out recipes, once they get hold of a brew. +# Only drawback: brew items can only be used on another server with the same encodeKey. +# So enable this if you want to make recipe cheating harder, but don't share any brews by world download, schematics, or other means. [false] +enableEncode: false +encodeKey: 0 + # Enable checking for Updates, Checks the curseforge api for updates to Brewery [true] # If an Update is found a Message is logged on Server-start and displayed to OPs joining the game updateCheck: true @@ -64,11 +77,119 @@ updateCheck: true # Autosave interval in minutes [3] autosave: 3 +# Show debug messages in log [false] +debug: false + # Config Version -version: '1.8' +version: '2.0' oldMat: true + +# -- Define custom items -- +# The defined id can then be used in recipes + +# matchAny: true if it is already enough if one of the info matches +# material: Which type the item has to be +# name: Which name the item has to be (Formatting codes possible: such as &6) +# lore: What has to be in the lore of the item + +customItems: + ex-item: + # A Bedrock item called Wall and has the given line in its lore + material: BEDROCK + name: 'Wall' + lore: + - '&7Very well protected' + + ex-item2: + # Using matchAny only one of the following has to match. + # In this case on of the door types, or an item called Beechwood Door, or an item with 'A door' in its lore + matchAny: true + material: + - WOODEN_DOOR + - IRON_DOOR + name: + - 'Beechwood Door' + lore: + - 'A door' + + rasp: + name: '&cRaspberry' + + +# -- Ingredients in the Cauldron -- +# Which Ingredients are accepted by the Cauldron and the base potion resulting from them + + # name: Name of the base potion coming out of the Cauldron (Formatting codes possible: such as &6) + # ingredients: List of 'material/amount' + # With an item in your hand, use /brew ItemName to get its material for use in a recipe + # (Item-ids instead of material are not supported by bukkit anymore and will not work) + # A list of materials can be found here: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html + # color: Color of the potion from a cauldron. + # Usable Colors: DARK_RED, RED, BRIGHT_RED, ORANGE, PINK, BLUE, CYAN, WATER, GREEN, BLACK, GREY, BRIGHT_GREY + # Or RGB colors (hex: for example '99FF33') (with '') (search for "HTML color" on the internet) + # lore: List of additional text on the base potion. (Formatting codes possible: such as &6) + +cauldron: + ex: + name: Example + ingredients: + - BEDROCK/2 + - DIAMOND + color: BLACK + lore: + - An example for a Base Potion + - This is how it comes out of a Cauldron + + # -- One Ingredient: -- + wheat: + name: Fermented wheat + ingredients: WHEAT + + sugarcane: + name: Sugar brew + ingredients: SUGAR_CANE + color: 'f1ffad' # yellowish green + + apple: + name: Apple cider + ingredients: APPLE + + potato: + name: Potatomash + ingredients: POTATO_ITEM + + grass: + name: Boiled herbs + ingredients: LONG_GRASS + color: '99ff66' # bright green + + rmushroom: + name: Mushroom brew + ingredients: RED_MUSHROOM + color: 'ff5c33' # amber red + + cocoa: + name: Chocolately brew + ingredients: INK_SACK + color: '804600' # mocca + + milk: + name: Milky water + ingredients: MILK_BUCKET + color: BRIGHT_GREY + + # -- Multiple Ingredients: -- + apfelmet_base: + name: Apple-Sugar brew + ingredients: + - SUGAR_CANE/3 + - APPLE + color: 'e1ff4d' # greenish yellow + + + # -- Recipes for Potions -- # name: Different names for bad/normal/good (Formatting codes possible: such as &6) @@ -77,8 +198,8 @@ oldMat: true # (Item-ids instead of material are not supported by bukkit anymore and will not work) # A list of materials can be found here: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html # You can specify a data (durability) value, omitting it will ignore the data value of the added ingredient -# If Vault is installed normal names can be used instead of material or id, so using Vault is highly recommended. -# Vault will recognize things like "Jungle Leaves" instead of "LEAVES,3" +# Plugin items with 'plugin:id' (Currently supporting ExoticGarden, Slimefun, MMOItems, Brewery) +# Or a custom item defined above # cookingtime: Time in real minutes ingredients have to boil # distillruns: How often it has to be distilled for full alcohol (0=without distilling) # distilltime: How long (in seconds) one distill-run takes (0=Default time of 40 sec) MC Default would be 20 sec @@ -90,6 +211,12 @@ oldMat: true # Or RGB colors (hex: for example '99FF33') (with '') (search for "HTML color" on the internet) # difficulty: 1-10 accuracy needed to get good quality (1 = unaccurate/easy, 10 = very precise/hard) # alcohol: Absolute amount of alcohol 0-100 in a perfect potion (will be added directly to the player, where 100 means fainting) +# lore: List of additional text on the finished brew. (Formatting codes possible: such as &6) +# Specific lore for quality possible, using + bad, ++ normal, +++ good, added to the front of the line. +# servercommands: List of Commands executed by the Server when drinking the brew +# playercommands: List of Commands executed by the Player when drinking the brew +# drinkmessage: Chat-message to the Player when drinking the Brew +# drinktitle: Title on Screen to the Player when drinking the Brew # effects: List of effect/level/duration Special potion-effect when drinking, duration in sek. # Suffix name with 'X' to hide effect from label. Sample: 'POISONX/2/10' (WEAKNESS, INCREASE_DAMAGE, SLOW and SPEED are always hidden.) # Effects are always hidden in 1.9 and newer, because of changes in the potion mechanics. @@ -100,16 +227,16 @@ oldMat: true recipes: # Example Recipe with every possible entry first: - 0: + ex: name: Bad Example/Example/Good Example ingredients: - - SUGAR_CANE/5 - DIAMOND/1 - INK_SACK,3/20 - WOOD,1/8 + - Brewery:Wheatbeer/2 +# - ExoticGarden:Grape/3 - BEDROCK/1 -# - Jungle Leaves/64 # Only with Vault -# - Green Dye/6 # Only with Vault + - ex-item/4 cookingtime: 3 distillruns: 2 distilltime: 60 @@ -118,12 +245,26 @@ recipes: color: DARK_RED difficulty: 3 alcohol: 23 + lore: + - This is an examble brew + - ++Just a normal Example + - This text would be on the brew + - + Smells disgusting + - ++ Smells alright + - +++ Smells really good + servercommands: + - weather clear + playercommands: + - homes + drinkmessage: Tastes good + drinktitle: Warms you from inside effects: - FIRE_RESISTANCE/20 - HEAL/1 - WEAKNESS/2-3/50-60 - POISONX/1-0/20-0 - 1: + + wheatbeer: name: Skunky Wheatbeer/Wheatbeer/Fine Wheatbeer ingredients: - WHEAT/3 @@ -134,7 +275,8 @@ recipes: color: BRIGHT_GREY difficulty: 1 alcohol: 5 - 2: + + beer: name: Skunky Beer/Beer/Fine Beer ingredients: - WHEAT/6 @@ -144,8 +286,11 @@ recipes: age: 3 color: ORANGE difficulty: 1 + lore: + - +++ &8Crisp taste alcohol: 6 - 3: + + darkbeer: name: Skunky Darkbeer/Darkbeer/Fine Darkbeer ingredients: - WHEAT/6 @@ -155,8 +300,11 @@ recipes: age: 8 color: BLACK difficulty: 2 + lore: + - +++ &8Roasted taste alcohol: 7 - 4: + + mead: name: Awkward Mead/Mead/&6Golden Mead ingredients: - SUGAR_CANE/6 @@ -166,8 +314,11 @@ recipes: age: 4 color: ORANGE difficulty: 2 + lore: + - +++ Has a golden shine alcohol: 9 - 5: + + ap_mead: name: Apple Mead/Sweet Apple Mead/&6Sweet Golden Apple Mead ingredients: - SUGAR_CANE/6 @@ -179,9 +330,14 @@ recipes: color: ORANGE difficulty: 4 alcohol: 12 + lore: + - +Is there any Apple in this? + - ++Refreshing taste of Apple + - +++Sweetest hint of Apple effects: - WATER_BREATHINGX/1-2/150 - 6: + + rum: name: Bitter Rum/Spicy Rum/&6Golden Rum ingredients: - SUGAR_CANE/14 @@ -196,7 +352,8 @@ recipes: effects: - FIRE_RESISTANCE/1/20-100 - POISONX/1-0/30-0 - 7: + + vodka: name: Lousy Vodka/Vodka/Russian Vodka ingredients: - POTATO_ITEM/10 @@ -206,10 +363,12 @@ recipes: color: BRIGHT_GREY difficulty: 4 alcohol: 20 + lore: +&8Almost undrinkable effects: - WEAKNESS/15 - POISON/10 - 8: + + absinthe: name: Poor Absinthe/Absinthe/Strong Absinthe ingredients: - LONG_GRASS/15 @@ -221,7 +380,8 @@ recipes: alcohol: 45 effects: - POISON/20-30 - 9: + + potato_soup: name: Potato soup ingredients: - POTATO_ITEM/5 @@ -231,7 +391,8 @@ recipes: difficulty: 1 effects: - HEAL/0-1 - 10: + + coffee: name: Stale Coffee/Coffee/Strong Coffee ingredients: - INK_SACK,3/12 @@ -239,6 +400,7 @@ recipes: cookingtime: 2 color: BLACK difficulty: 3 + lore: + &8Probably a week old effects: - REGENERATION/1/2-5 - SPEED/1/30-140 @@ -248,28 +410,13 @@ recipes: # It is up to the Serveradmin to change and add Recipes, so players cannot cheat from the default config. - -# cooked: EVERY possible ingredient and the names for the originating potions after fermenting: -# [Example] MATERIAL: Name after cooking - -cooked: - WHEAT: Fermented wheat - SUGAR_CANE: Sugar brew - APPLE: Apple cider - POTATO_ITEM: Potatomash - LONG_GRASS: Boiled herbs - RED_MUSHROOM: Mushroom brew - INK_SACK: Colored brew - MILK_BUCKET: Milky water - - - # -- Plugin Compatibility -- # Enable checking of other Plugins (if installed) for Barrel Permissions [true] useWorldGuard: true useLWC: true useGriefPrevention: true +useGMInventories: true useCitadel: true # Enable the Logging of Barrel Inventories to LogBlock [true] @@ -278,8 +425,7 @@ useLogBlock: true # -- Chat Distortion Settings -- -# If written Chat is distorted when the Player is Drunk, -# so that it looks like drunk writing +# If written Chat is distorted when the Player is Drunk, so that it looks like drunk writing # How much the chat is distorted depends on how drunk the Player is # Below are settings for what and how changes in chat occur enableChatDistortion: true diff --git a/resources/config/v12/fr/config.yml b/resources/config/v12/fr/config.yml index e7673d6..5ff25d1 100644 --- a/resources/config/v12/fr/config.yml +++ b/resources/config/v12/fr/config.yml @@ -1,9 +1,9 @@ # config for Brewery.jar +# Quelques traductions en français ont été faites avec DeepL # -- Paramètres -- # Les paramètres par défaut sont entre [] -# Supprimer un paramètre le désactive # Fichier de langage utilisé (trouvable dans plugins/Brewery/languages) language: fr @@ -51,12 +51,26 @@ hangoverDays: 7 colorInBarrels: true colorInBrewer: true +# Toujours montrer les 1-5 étoiles sur les objets en fonction de leur qualité. S'ils sont faux, ils n'apparaîtront que lors de l'infusion. [true] +alwaysShowQuality: true + +# Toujours indiquer la teneur en alcool sur les objets. S'il est false, il n'apparaîtra que dans le stand de brassage. [false] +alwaysShowAlc: false + # Si le grand tonneau peut être ouvert en cliquant sur n'importe quel bloc, non seulement le robinet ou le panneau. Toujours "true" pour les petits tonneaux. [true] openLargeBarrelEverywhere: true -# How many Brewery drinks can be put into the Minecraft barrels [6] +# Combien de boissons de brasserie peuvent être mises dans les barils Minecraft [6] maxBrewsInMCBarrels: 6 +# Les ingrédients et autres données de brassage utilisés sont sauvegardés dans tous les articles de brasserie. [false] +# Pour empêcher les clients piratés de lire exactement ce qui a été utilisé pour infuser un élément, les données peuvent être encodées/brouillées. +# Il s'agit d'un processus rapide pour empêcher les joueurs de pirater des recettes, une fois qu'ils mettent la main sur une bière. +# Seul inconvénient: Les boissons brassicoles ne peuvent être utilisés que sur un autre serveur avec la même clé de chiffrement. +# Activez cette option si vous voulez rendre la tricherie des recettes plus difficile, mais ne partagez pas les infusions par téléchargement mondial, schémas ou autres moyens. +enableEncode: false +encodeKey: 0 + # Enable checking for Updates, Checks the curseforge api for updates to Brewery [true] # If an Update is found a Message is logged on Server-start and displayed to OPs joining the game updateCheck: true @@ -64,33 +78,148 @@ updateCheck: true # Intervale de la sauvegarde automatique en minutes [3] autosave: 3 +# Show debug messages in log [false] +debug: false + # Version de configuration -version: '1.8' +version: '2.0' oldMat: true + +# -- Définir des objets personnalisés -- +# L'id défini peut ensuite être utilisé dans les recettes + +# matchAny: si c'est déjà assez si l'une des infos correspond +# material: le type d'article à utiliser +# name: Quel nom l'article doit porter (codes de formatage possibles : tels que &6) +# lore: Ce qui doit être dans la lore de l'objet + +customItems: + ex-item: + # Un objet Barrière appelé "Wall" et qui a la ligne donnée dans sa lore + material: BEDROCK + name: 'Wall' + lore: + - '&7Very well protected' + + ex-item2: + # En utilisant matchAny, un seul des éléments suivants doit correspondre. + # Dans ce cas, l'un des types de porte, ou un article appelé "Beechwood Door", ou un objet avec "A door" dans sa lore. + matchAny: true + material: + - WOODEN_DOOR + - IRON_DOOR + name: + - 'Beechwood Door' + lore: + - 'A door' + + rasp: + name: '&cRaspberry' + + +# -- Ingrédients dans le chaudron -- +# Quels sont les ingrédients acceptés par le chaudron et la potion de base qui en résulte + + # name: Nom de la potion de base qui sort du chaudron (codes de formatage possibles : tels que &6) + # ingredients: Liste des 'matériaux/montant' + # Avec un objet en main, utilisez /brew ItemName pour obtenir son matériau pour une recette de cuisine + # (Les id d'objets à la place des matériaux sont obsolètes pour bukkit) + # Une liste des matériaux peuvent-être trouvés ici: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html + # color : Couleur de la potion provenant d'un chaudron. + # Couleurs disponibles : DARK_RED, RED, BRIGHT_RED, ORANGE, PINK, BLUE, CYAN, WATER, GREEN, BLACK, GREY, BRIGHT_GREY + # (Dans l'ordre : Rouge foncé, Rouge, Rouge clair, Orange, Rose, Bleu, Cyan, Eau, Vert, Noir, Gris, Gris clair) + # Ou couleurs RGB (hex: par exemple '99FF33') (avec '') (recherche de "HTML color" sur internet) + # lore: Liste de texte supplémentaire sur la potion de base. (Formatting codes possible: such as &6) + +cauldron: + ex: + name: Exemple + ingredients: + - BEDROCK/2 + - DIAMOND + color: BLACK + lore: + - Un exemple pour une potion de base + - Voici comment il sort d'un chaudron + + # -- Un ingrédient: -- + wheat: + name: Blé fermenté + ingredients: WHEAT + + sugarcane: + name: Sucre fermenté + ingredients: SUGAR_CANE + color: 'f1ffad' # yellowish green + + apple: + name: Cidre de pommes + ingredients: APPLE + + potato: + name: Purée de Pommes de Terre + ingredients: POTATO_ITEM + + grass: + name: Herbes bouillies + ingredients: LONG_GRASS + color: '99ff66' # bright green + + rmushroom: + name: Champignons fermentés + ingredients: RED_MUSHROOM + color: 'ff5c33' # amber red + + cocoa: + name: Infusion au chocolat + ingredients: INK_SACK + color: '804600' # mocca + + milk: + name: Eau laiteuse + ingredients: MILK_BUCKET + color: BRIGHT_GREY + + # -- Ingrédients multiples: -- + apfelmet_base: + name: Brassée pomme-sucre + ingredients: + - SUGAR_CANE/3 + - APPLE + color: 'e1ff4d' # greenish yellow + + # -- Recette pour les boissons -- # name: Différents noms pour la mauvaise/moyen/bonne qualité (Les codes de mise en forme sont pris en charge: comme par exemple &6 pour la couleur Or.) -# ingredients: Liste des 'matériaux,data/montant' -# With an item in your hand, use /brew ItemName to get its material for use in a recipe -# (Les id d'objets à la place des matériaux sont obsolètes pour bukkit et pourraient ne pas fonctionner dans le futur!) -# Ex: 'SUGAR_CANE' +# ingredients: Liste des 'matériaux/montant' +# Avec un objet en main, utilisez /brew ItemName pour obtenir son matériau pour une recette de cuisine +# (Les id d'objets à la place des matériaux sont obsolètes pour bukkit) +# Ex: 'Sugar_Cane' # Une liste des matériaux peuvent-être trouvés ici: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html # Vous pouvez spécifier une data (Ex: 5,3 -> Planche de bois de jungle), si vous ne le faites pas la data ne sera pas prise en compte (Ex : 5 -> Bois en général) -# If Vault is installed normal names can be used instead of material or id, so using Vault is highly recommended. -# Vault will recognize things like "Jungle Leaves" instead of "5,3" +# Plugin items avec 'plugin:id' (Actuellement supporté ExoticGarden, Slimefun, MMOItems, Brewery) +# Ou un élément personnalisé défini ci-dessus # cookingtime: Temps en minutes réelles durant lesquelles les ingrédients devront bouillir # distillruns: Combien de fois le breuvage devra être distillé pour un alcool de qualité (0=Ne pas distiller) -# distilltime: How long (in seconds) one distill-run takes (0=Default time of 40 sec) MC Default would be 20 sec +# distilltime: Combien de temps (en secondes) dure une distillation (0=Temps par défaut de 40 secondes) MC Par défaut serait de 20 secondes # wood: Type de bois du baril 0=aucun 1=Bouleau 2=Chêne 3=Jungle 4=Pin 5=Acacia 6=Chêne Noir -# The Minecraft barrel is made of oak +# Le tonneau Minecraft est en chêne. # age: Temps en jours de Minecraft, la potion devra être âgée dans un baril. 0=Pas besoin d'âge # color: Couleur de la potion après distillation/avoir laissé vieillir. -# Couleurs disponibles: DARK_RED, RED, BRIGHT_RED, ORANGE, PINK, BLUE, CYAN, WATER, GREEN, BLACK, GREY, BRIGHT_GREY (Dans l'ordre : Rouge foncé, Rouge, Rouge clair, Orange, Rose, Bleu, Cyan, Eau, Vert, Noir, Gris, Gris clair) -# Or RGB colors (hex: for example '99FF33') (with '') (search for "HTML color" on the internet) +# Couleurs disponibles: DARK_RED, RED, BRIGHT_RED, ORANGE, PINK, BLUE, CYAN, WATER, GREEN, BLACK, GREY, BRIGHT_GREY +# (Dans l'ordre : Rouge foncé, Rouge, Rouge clair, Orange, Rose, Bleu, Cyan, Eau, Vert, Noir, Gris, Gris clair) +# Ou couleurs RGB (hex: par exemple '99FF33') (avec '') (recherche de "HTML color" sur internet) # difficulty: 1-10 précision nécessaire pour obtenir une bonne qualité (1 = imprécis/facile, 10 = très précis/difficile) # alcohol: Le montant d'alcool absolu dans une boisson parfaite (cela sera ajouté directement au joueur, où 100% entraînera l'évanouissement), un degré d'alcooléisme en fait +# lore: Liste des textes supplémentaires sur le breuvage fini. (Codes de formatage possibles : tels que &6) +# Texte spécifique de qualité possible, en utilisant + mauvais, ++ normal, +++ bon, ajouté à l'avant de la ligne. +# servercommands: Liste des commandes exécutées par le serveur lors de la consommation de la potion +# playercommands: Liste des commandes exécutées par le joueur lors de la consommation de la potion +# drinkmessage: Chat-message au joueur lorsqu'il boit la potion +# drinktitle: Titre à l'écran du joueur lorsqu'il boit la potion # effects: Liste des effets/durée en secondes lors de la consommation. # Rajouter le suffixe 'X' pour le cacher du label. Exemple: POISONX/10 # (WEAKNESS, INCREASE_DAMAGE, SLOW et SPEED sont toujours cachés.) @@ -108,13 +237,13 @@ recipes: 0: name: Mauvais Exemple/Exemple/Bonne Exemple ingredients: - - SUGAR_CANE/5 - - 264/1 + - DIAMOND/1 - INK_SACK,3/20 - - 5,1/8 + - WOOD,1/8 + - Brewery:Wheatbeer/2 +# - ExoticGarden:Grape/3 - BEDROCK/1 -# - Jungle Leaves/64 # Only with Vault -# - Green Dye/6 # Only with Vault + - ex-item/4 cookingtime: 3 distillruns: 2 distilltime: 60 @@ -123,11 +252,25 @@ recipes: color: DARK_RED difficulty: 3 alcohol: 23 + lore: + - C'est un breuvage d'exemple + - ++Juste un exemple normal + - Ce texte serait sur le breuvage + - + Ça sent dégueulasse. + - ++ Ça sent bon + - +++ Ça sent vraiment bon + servercommands: + - weather clear + playercommands: + - homes + drinkmessage: C'est bon au goût + drinktitle: vous réchauffe à l'intérieur effects: - FIRE_RESISTANCE/20 - HEAL/1 - WEAKNESS/2-3/50-60 - POISONX/1-0/20-0 + 1: name: Bière Blanche Fade/Bière Blanche/Bonne Bière Blanche ingredients: @@ -139,6 +282,7 @@ recipes: color: BRIGHT_GREY difficulty: 1 alcohol: 5 + 2: name: Bière Fade/Bière/Bonne Bière ingredients: @@ -150,6 +294,7 @@ recipes: color: ORANGE difficulty: 1 alcohol: 6 + 3: name: Bière Brune Fade/Bière Brune/Bonne Bière Brune ingredients: @@ -161,6 +306,7 @@ recipes: color: BLACK difficulty: 2 alcohol: 7 + 4: name: Hydromel Bizarre/Hydromel/&6Hydromel Doré ingredients: @@ -172,6 +318,7 @@ recipes: color: ORANGE difficulty: 2 alcohol: 9 + 5: name: Hydromel de Pommes/Doux Hydromel de Pommes/&6Doux Hydromel de Pommes Dorées ingredients: @@ -186,6 +333,7 @@ recipes: alcohol: 12 effects: - WATER_BREATHINGX/1-2/150 + 6: name: Rhum Amer/Rhum Epicé/&6Rhum Doré ingredients: @@ -201,6 +349,7 @@ recipes: effects: - FIRE_RESISTANCE/1/20-100 - POISONX/1-0/30-0 + 7: name: Vodka Sale/Vodka/Vodka Russe ingredients: @@ -214,6 +363,7 @@ recipes: effects: - WEAKNESS/15 - POISON/10 + 8: name: Absinthe pauvre/Absinthe/Absinthe forte ingredients: @@ -226,6 +376,7 @@ recipes: alcohol: 45 effects: - POISON/20-30 + 9: name: Potato soup ingredients: @@ -236,6 +387,7 @@ recipes: difficulty: 1 effects: - HEAL/0-1 + 10: name: Café fétide/Café/Café fort ingredients: @@ -253,28 +405,13 @@ recipes: # Ce sera aux Admins des serveurs de changer et d'ajouter les recettes, ainsi les joueurs ne pourront pas tricher avec les configuration de base. - -# cooked: CHAQUE ingrédient possible avec le nom donné après la fermentation (la cuisson): -# [Exemple] MATERIEL: Nom après la cuisson - -cooked: - WHEAT: Blé fermenté - SUGAR_CANE: Sucre fermenté - APPLE: Cidre de pommes - POTATO_ITEM: Purée de Pommes de Terre - LONG_GRASS: Herbes bouillies - RED_MUSHROOM: Champignons fermentés - INK_SACK: Fermentation colorée - MILK_BUCKET: Eau laiteuse - - - # -- Compatibilité entre Plugins -- # Activer la vérification des autres plugins (si installés) pour les permissions des tonneaux. [true] useWorldGuard: true useLWC: true useGriefPrevention: true +useGMInventories: true useCitadel: true # Activer l'historique du contenu des tonneaux avec LogBlock [true] @@ -283,10 +420,9 @@ useLogBlock: true # -- Paramètres de la distorsion du Chat -- -# If written Chat is distorted when the Player is Drunk, -# so that it looks like drunk writing -# How much the chat is distorted depends on how drunk the Player is -# Below are settings for what and how changes in chat occur +# Si le Chat écrit est déformé quand le joueur est ivre, de sorte qu'il ressemble à un chat bourré en train d'écrire +# Le degré de distorsion du chat dépend de l'état d'ébriété du joueur +# Ci-dessous sont les paramètres pour ce qui et comment les changements dans le chat se produisent enableChatDistortion: true # Ecrire dans les "logs" du serveur ce que le joueur devrait dire, à la place de la distorsion. [false] diff --git a/resources/config/v12/it/config.yml b/resources/config/v12/it/config.yml index 70af3f2..99bb64b 100644 --- a/resources/config/v12/it/config.yml +++ b/resources/config/v12/it/config.yml @@ -3,7 +3,6 @@ # -- Opzioni -- # I valori di default sono scritti fra [] -# Cancellare una voce la disabilita # Lingua da usare (fra quelle in plugins/Brewery/languages) language: it @@ -51,12 +50,26 @@ hangoverDays: 7 colorInBarrels: true colorInBrewer: true +# Always show the 1-5 stars on the item depending on the quality. If false, they will only appear when brewing [true] +alwaysShowQuality: true + +# Always show the alcohol content on the item. If false, it will only show in the brewing stand [false] +alwaysShowAlc: false + # Se un barile grande può essere aperto cliccandoci sopra, non solo sul cartello e sulla staccionata. Questo è sempre true per i barili piccoli. [true] openLargeBarrelEverywhere: true # How many Brewery drinks can be put into the Minecraft barrels [6] maxBrewsInMCBarrels: 6 +# The used Ingredients and other brewing-data is saved to all Brewery Items. To prevent +# hacked clients from reading what exactly was used to brew an item, the data can be encoded/scrambled. +# This is a fast process to stop players from hacking out recipes, once they get hold of a brew. +# Only drawback: brew items can only be used on another server with the same encodeKey. +# So enable this if you want to make recipe cheating harder, but don't share any brews by world download, schematics, or other means. [false] +enableEncode: false +encodeKey: 0 + # Abilita il controllo degli aggiornamenti, controlla l'API di CurseForge per eventuali aggiornamenti di Brewery [true] # Se quando un aggiornamento viene trovato un messaggio è loggato e mostrato agli OPs quando entrano in gioco. updateCheck: true @@ -64,11 +77,117 @@ updateCheck: true # Intervallo di autosalvataggio in minuti [3] autosave: 3 +# Show debug messages in log [false] +debug: false + # Versione del config -version: '1.8' +version: '2.0' oldMat: true + +# -- Define custom items -- +# The defined id can then be used in recipes + +# matchAny: true if it is already enough if one of the info matches +# material: Which type the item has to be +# name: Which name the item has to be (Formatting codes possible: such as &6) +# lore: What has to be in the lore of the item + +customItems: + ex-item: + # A Bedrock item called Wall and has the given line in its lore + material: BEDROCK + name: 'Wall' + lore: + - '&7Very well protected' + + ex-item2: + # Using matchAny only one of the following has to match. + # In this case on of the door types, or an item called Beechwood Door, or an item with 'A door' in its lore + matchAny: true + material: + - WOODEN_DOOR + - IRON_DOOR + name: + - 'Beechwood Door' + lore: + - 'A door' + + rasp: + name: '&cRaspberry' + + +# -- Ingredients in the Cauldron -- +# Which Ingredients are accepted by the Cauldron and the base potion resulting from them + + # name: Name of the base potion coming out of the Cauldron (Formatting codes possible: such as &6) + # ingredients: List of 'material/amount' + # With an item in your hand, use /brew ItemName to get its material for use in a recipe + # (Item-ids instead of material are not supported by bukkit anymore and will not work) + # A list of materials can be found here: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html + # color: Color of the potion from a cauldron. + # Usable Colors: DARK_RED, RED, BRIGHT_RED, ORANGE, PINK, BLUE, CYAN, WATER, GREEN, BLACK, GREY, BRIGHT_GREY + # Or RGB colors (hex: for example '99FF33') (with '') (search for "HTML color" on the internet) + # lore: List of additional text on the base potion. (Formatting codes possible: such as &6) + +cauldron: + ex: + name: Example + ingredients: + - BEDROCK/2 + - DIAMOND + color: BLACK + lore: + - An example for a Base Potion + - This is how it comes out of a Cauldron + + # -- One Ingredient: -- + wheat: + name: Frumento fermentato + ingredients: WHEAT + + sugarcane: + name: Miscela zuccherata + ingredients: SUGAR_CANE + color: 'f1ffad' # yellowish green + + apple: + name: Sidro di mele + ingredients: APPLE + + potato: + name: Purè di patate + ingredients: POTATO_ITEM + + grass: + name: Erbe bollite + ingredients: LONG_GRASS + color: '99ff66' # bright green + + rmushroom: + name: Miscela ai funghi + ingredients: RED_MUSHROOM + color: 'ff5c33' # amber red + + cocoa: + name: Miscela colorata + ingredients: INK_SACK + color: '804600' # mocca + + milk: + name: Acqua lattea + ingredients: MILK_BUCKET + color: BRIGHT_GREY + + # -- Multiple Ingredients: -- + apfelmet_base: + name: Apple-Sugar brew + ingredients: + - SUGAR_CANE/3 + - APPLE + color: 'e1ff4d' # greenish yellow + # -- Ricette per pozioni -- # name: Tre nomi diversi per diverse qualità (cattivo/normale/buono). I codici come &6 possono essere usati. @@ -77,8 +196,8 @@ oldMat: true # (Gli id invece dei materiali sono "disapprovati" da Bukkit e potrebbero non funzionare in futuro!) # Una lista di materiali può essere trovata qui: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html # Puoi specificare dei dati dell'oggetto, ma se omesso sarà semplicemente ignorato. -# Se Vault è installato i nomi normali possono essere usati invece del materiale o dell'id quindi l'uso di Vault è fortemente consigliato. -# Vault riconoscerà cose come "Jungle Leaves" invece di "LEAVES,3". +# Plugin items with 'plugin:id' (Currently supporting ExoticGarden, Slimefun, MMOItems, Brewery) +# Or a custom item defined above # cookingtime: Tempo in minuti richiesto dagli ingredienti per bollire # distillruns: Quanto spesso deve essere distillato per ottenere la versione perfetta con il volume alcolico impostato (0=non serve distillare). # distilltime: How long (in seconds) one distill-run takes (0=Default time of 40 sec) MC Default would be 20 sec @@ -90,6 +209,12 @@ oldMat: true # Or RGB colors (hex: for example '99FF33') (with '') (search for "HTML color" on the internet) # difficoltà: Precisione richiesta per avere la migliore qualità da 1 a 10(1 = spreciso/più facile, 10 = molto preciso/più difficile) # alcohol: Volume alcolico da 0 a 100 nella versione perfetta (sarà aggiunta direttamente al giocatore, dove 100 è la quantità massima di alcohol assorbibile. +# lore: List of additional text on the finished brew. (Formatting codes possible: such as &6) +# Specific lore for quality possible, using + bad, ++ normal, +++ good, added to the front of the line. +# servercommands: List of Commands executed by the Server when drinking the brew +# playercommands: List of Commands executed by the Player when drinking the brew +# drinkmessage: Chat-message to the Player when drinking the Brew +# drinktitle: Title on Screen to the Player when drinking the Brew # effects: Eventuali effetti come quelli delle pozioni nel formato di effetto/livello/durata. # Aggiungere il suffisso 'X' per nascondere l'effetto dalla descrizione. Esempio: 'POISONX/2/10' (gli effetti WEAKNESS, INCREASE_DAMAGE, SLOW and SPEED sono sempre nascosti). # Gli effetti sono sempre nascosti dalla 1.9 in poi, per via dei cambiamenti nelle meccaniche delle pozioni. @@ -103,13 +228,13 @@ recipes: 0: name: Cattivo esempio/Esempio/Buon esempio ingredients: - - SUGAR_CANE/5 - - 264/1 + - DIAMOND/1 - INK_SACK,3/20 - - 5,1/8 + - WOOD,1/8 + - Brewery:Wheatbeer/2 +# - ExoticGarden:Grape/3 - BEDROCK/1 -# - Jungle Leaves/64 # Solo con Vault -# - Green Dye/6 # Solo con Vault + - ex-item/4 cookingtime: 3 distillruns: 2 distilltime: 60 @@ -118,11 +243,25 @@ recipes: color: DARK_RED difficulty: 3 alcohol: 23 + lore: + - This is an examble brew + - ++Just a normal Example + - This text would be on the brew + - + Smells disgusting + - ++ Smells alright + - +++ Smells really good + servercommands: + - weather clear + playercommands: + - homes + drinkmessage: Tastes good + drinktitle: Warms you from inside effects: - FIRE_RESISTANCE/20 - HEAL/1 - WEAKNESS/2-3/50-60 - POISONX/1-0/20-0 + 1: name: Birra di frumento puzzolente/Birra di frumento/Birra di frumento pregiata ingredients: @@ -134,6 +273,7 @@ recipes: color: BRIGHT_GREY difficulty: 1 alcohol: 5 + 2: name: Birra puzzolente/Birra/Birra pregiata ingredients: @@ -145,6 +285,7 @@ recipes: color: ORANGE difficulty: 1 alcohol: 6 + 3: name: Birra scura puzzolente/Birra scura/Birra scura pregiata ingredients: @@ -156,6 +297,7 @@ recipes: color: BLACK difficulty: 2 alcohol: 7 + 4: name: Idromele scarso/Idromele/&6Idromele dorato ingredients: @@ -167,6 +309,7 @@ recipes: color: ORANGE difficulty: 2 alcohol: 9 + 5: name: Idromele di mele/Idromele di mele dolci/&6Idromele di mele dolci dorato ingredients: @@ -181,6 +324,7 @@ recipes: alcohol: 12 effects: - WATER_BREATHINGX/1-2/150 + 6: name: Rum amaro/Rum speziato/&6Rum dorato ingredients: @@ -196,6 +340,7 @@ recipes: effects: - FIRE_RESISTANCE/1/20-100 - POISONX/1-0/30-0 + 7: name: Vodka schifosa/Vodka/Vodka russa ingredients: @@ -209,6 +354,7 @@ recipes: effects: - WEAKNESS/15 - POISON/10 + 8: name: Assenzio scarso/Assenzio/Assenzio forte ingredients: @@ -221,6 +367,7 @@ recipes: alcohol: 45 effects: - POISON/20-30 + 9: name: Zuppa di patate ingredients: @@ -231,6 +378,7 @@ recipes: difficulty: 1 effects: - HEAL/0-1 + 10: name: Caffè stantio/Caffè/Caffè forte ingredients: @@ -249,27 +397,13 @@ recipes: -# cooked: OGNI possibile ingrediente e i nomi per la pozione originatasi dopo la fermentazione. -# (Esempio) MATERIALE: Nome dopo la cottura - -cooked: - WHEAT: Frumento fermentato - SUGAR_CANE: Miscela zuccherata - APPLE: Sidro di mele - POTATO_ITEM: Purè di patate - LONG_GRASS: Erbe bollite - RED_MUSHROOM: Miscela ai funghi - INK_SACK: Miscela colorata - MILK_BUCKET: Acqua lattea - - - # -- Compatibilità con altri plugin -- # Abilita il controllo della presenza di altri plugin per i permessi relativi ai barili[true] useWorldGuard: true useLWC: true useGriefPrevention: true +useGMInventories: true useCitadel: true # Abilita il logging degli inventari dei barili [true] diff --git a/resources/config/v13/de/config.yml b/resources/config/v13/de/config.yml index f46df44..7c9c1bb 100644 --- a/resources/config/v13/de/config.yml +++ b/resources/config/v13/de/config.yml @@ -3,7 +3,6 @@ # -- Verschiedene Einstellungen -- # Standardeinstellungen sind in [] angegeben -# Löschen einzelner Einstellungen deaktiviert sie # Sprachedatei die genutzt werden sollte (befindet sich in plugins/Brewery/languages) language: de @@ -41,8 +40,8 @@ pukeDespawntime: 60 # Konsumierbares Item/Stärke. Senkt den Alkoholpegel um wenn konsumiert. drainItems: -- Bread/4 -- Milk_Bucket/2 + - Bread/4 + - Milk_Bucket/2 # Zeit (in Tagen) die Trunkenheitsdaten nach offlinegehen eines Spielers im Speicher verbleiben, um z.B. Kater-Effekte anzuwenden. [7] hangoverDays: 7 @@ -51,14 +50,24 @@ hangoverDays: 7 colorInBarrels: true colorInBrewer: true +# Ob in den Iteminformationen immer 1-5 Sterne für die Qualität angezeigt werden sollen, oder nur beim brauen [true] +alwaysShowQuality: true + +# Ob in den Iteminformationen immer der Alkoholgehalt angezeigt weden soll, oder nur im Braustand [false] +alwaysShowAlc: false + # Ob große Fässer an jedem Block geöffnet werden können, nicht nur an Zapfhahn und Schild. Bei kleinen Fässern geht dies immer. [true] openLargeBarrelEverywhere: true # Wie viele Brewery Getränke in die Minecraft Fässer getan werden können [6] maxBrewsInMCBarrels: 6 -# In den Serverlog loggen was der Spieler tatsächlich geschrieben hat, bevor seine Worte verändert wurden [false] -logRealChat: false +# Benutzte Zutaten und andere Brau-Daten werden in allen Brewery Tränken gespeichert. Um zu verhindern, +# dass gehackte clients diese Daten auslesen um Rezepte herauszufinden, können diese encodiert werden. +# Einziger Nachteil: Tränke können nur auf Servern mit dem gleichen encodeKey benutzt werden. +# Dies kann also aktiviert werden um Rezept-cheating schwerer zu machen, aber keine Tränke per World Download, Schematic, o.ä. geteilt werden. [false] +enableEncode: false +encodeKey: 0 # Aktiviert das Suchen nach Updates für Brewery mit der curseforge api [true] # Wenn ein Update gefunden wurde, wird dies bei Serverstart im log angezeigt, sowie OPs benachrichtigt @@ -67,8 +76,116 @@ updateCheck: true # Autosave Intervall in Minuten [3] autosave: 3 +# Debug Nachrichten im Log anzeigen [false] +debug: false + # Config Version -version: '1.8' +version: '2.0' + + + +# -- Eigene Items Definieren -- +# Die festgelegte id kann dann in einem Rezept verwendet werden + +# matchAny: true wenn es schon reicht wenn eine der Angaben zutrifft +# material: Welche Art das Item haben muss +# name: Welchen Namen das Item haben muss (Farbcodes möglich: z.b. &6) +# lore: Was in der Lore des Items stehen muss + +customItems: + bsp-item: +# Ein Barriere Item das Mauer heißt und in der Lore die angegebene Zeile hat + material: Barrier + name: 'Mauer' + lore: + - '&7Besonders gut geschützt' + + bsp-item2: +# Mit matchAny muss nur eine der Angaben zutreffen. +# Hier also eine der Türarten, oder ein Item namens Buchenholztür, oder ein Item mit 'Eine Tür' in der Lore + matchAny: true + material: + - Acacia_Door + - Oak_Door + - Spruce_Door + name: + - 'Buchenholztür' + lore: + - 'Eine Tür' + + himbeere: + name: '&cHimbeere' + + +# -- Zutaten im Kessel -- +# Hier kann angegeben werden welche Zutaten in den Kessel getan werden können und was mit ihnen geschieht. + + # name: Name des Basistrankes der aus dem Kessel kommt (Farbcodes möglich: z.b. &6) + # ingredients: Auflistung von 'Material/Anzahl' + # Halte ein Item in der Hand und benutze /brew ItemName um dessen Material herauszufinden und für ein Rezept zu benutzen + # (Item-ids anstatt Material können in Bukkit nicht mehr benutzt werden) + # Eine Liste von allen Materialien kann hier gefunden werden: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html + # color: Farbe des Trankes der aus dem Kessel kommt + # Benutzbare Farben: DARK_RED, RED, BRIGHT_RED, ORANGE, PINK, BLUE, CYAN, WATER, GREEN, BLACK, GREY, BRIGHT_GREY + # Oder RGB Farben (Hex: also zB '99FF33') (Ohne #) (mit '') (Einfach nach "HTML color" im Internet suchen) + # lore: Auflistung von zusätzlichem Text auf dem Trank. (Farbcodes möglich: z.b. &6) + +cauldron: + bsp: + name: Beispiel + ingredients: + - Bedrock/2 + - Diamond + color: BLACK + lore: + - Ein Beispiel für einen Basistrank + - So kommt er aus dem Kessel + +# -- Eine Zutat: -- + wheat: + name: Getreideferment + ingredients: Wheat + + sugarcane: + name: Zuckersud + ingredients: Sugar_Cane + color: 'f1ffad' # gelbliches grün + + apple: + name: Apfelmost + ingredients: Apple + + potato: + name: Kartoffelmaische + ingredients: Potato + + grass: + name: Kräuterbrühe + ingredients: Grass + color: '99ff66' # helles grün + + rmushroom: + name: Pilzsud + ingredients: Red_Mushroom + color: 'ff5c33' # bernsteinrot + + cocoa: + name: Kakaobrühe + ingredients: Cocoa_Beans + color: '804600' # mokka + + milk: + name: Milchiges Wasser + ingredients: Milk_Bucket + color: BRIGHT_GREY + + # -- Mehrere Zutaten: -- + apfelmet_basis: + name: Apfel-Zuckersud + ingredients: + - Sugar_Cane/3 + - Apple + color: 'e1ff4d' # grünliches gelb # -- Rezepte für Getränke -- @@ -78,6 +195,8 @@ version: '1.8' # Halte ein Item in der Hand und benutze /brew ItemName um dessen Material herauszufinden und für ein Rezept zu benutzen # (Item-ids anstatt Material können in Bukkit nicht mehr benutzt werden) # Eine Liste von allen Materialien kann hier gefunden werden: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html +# Plugin Items mit 'Plugin:Id' (Im Moment ExoticGarden, Slimefun, MMOItems, Brewery) +# Oder ein oben definiertes Custom Item # cookingtime: Zeit in Echtminuten die die Zutaten kochen müssen # distillruns: Wie oft destilliert werden muss für vollen Alkoholgehalt (0=ohne Destillieren) # distilltime: Wie lange (in sekunden) ein Destillations-Durchlauf braucht (0=Standard Zeit von 40 sek) MC Standard wäre 20 sek @@ -89,6 +208,12 @@ version: '1.8' # Oder RGB Farben (Hex: also zB '99FF33') (Ohne #) (mit '') (Einfach nach "HTML color" im Internet suchen) # difficulty: 1-10 Genauigkeit der Einhaltung der Vorgaben (1 = ungenau/einfach 10 = sehr genau/schwer) # alcohol: Alkoholgehalt 0-100 in absoluter Menge bei perfektem Getränk (wird dem Spieler hinzugefügt, bei 100 = tot) +# lore: Auflistung von zusätzlichem Text auf dem fertigen Trank. (Farbcodes möglich: z.b. &6) +# Lore nur für bestimmte Qualität möglich mit + Schlecht, ++ Mittel, +++ Gut, vorne anhängen. +# servercommands: Liste von Befehlen ausgeführt vom Server wenn der Trank getrunken wird +# playercommands: Liste von Befehlen ausgeführt vom Spieler wenn der Trank getrunken wird +# drinkmessage: Nachricht im Chat beim trinken des Trankes +# drinktitle: Nachricht als Titel auf dem Bildschirm an den Spieler beim trinken des Trankes # effects: Auflistung Effekt/Level/Dauer Besonderere Trank-Effekte beim Trinken, Dauer in sek. # Mögliche Effekte: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/potion/PotionEffectType.html # Minimale und Maximale Level/Dauer können durch "-" festgelegt werden, Bsp: 'SPEED/1-2/30-40' = Level 1 und 30 sek minimal, Level 2 und 40 sek maximal @@ -97,144 +222,179 @@ version: '1.8' recipes: # Ein vollständiges Beispiel zuerst: - 0: - name: Schlechtes Beispiel/Beispiel/Gutes Beispiel - ingredients: - - Sugar_Cane/5 + bsp: + name: Schlechtes Beispiel/Beispiel/Gutes Beispiel + ingredients: - Diamond/1 - - Cocoa_Beans/20 - Spruce_Planks/8 - Bedrock/1 - cookingtime: 3 - distillruns: 2 - distilltime: 60 - wood: 4 - age: 11 - color: DARK_RED - difficulty: 3 - alcohol: 23 - effects: + - Brewery:Weißbier/2 +# - ExoticGarden:Grape/3 + - bsp-item/4 + cookingtime: 3 + distillruns: 2 + distilltime: 60 + wood: 4 + age: 11 + color: DARK_RED + difficulty: 3 + alcohol: 23 + lore: + - Dies ist ein Beispiel Trank + - ++Ganz normales Beispiel + - Dies würde auf dem Trank stehen + - + Riecht eklig + - ++ Riecht ganz ok + - +++ Riecht richtig gut + servercommands: + - weather clear + playercommands: + - homes + drinkmessage: Schmeckt toll + drinktitle: Wärmt dich von innen + effects: - FIRE_RESISTANCE/20 - HEAL/1 - WEAKNESS/2-3/50-60 - POISON/1-0/20-0 - 1: - name: Ranziges Weißbier/Weißbier/Feines Weißbier - ingredients: + + weißbier: + name: Ranziges Weißbier/Weißbier/Feines Weißbier + ingredients: - Wheat/3 - cookingtime: 8 - distillruns: 0 - wood: 1 - age: 2 - color: BRIGHT_GREY - difficulty: 1 - alcohol: 5 - 2: - name: Ranziges Bier/Bier/Feines Bier - ingredients: + cookingtime: 8 + distillruns: 0 + wood: 1 + age: 2 + color: BRIGHT_GREY + difficulty: 1 + alcohol: 5 + + bier: + name: Ranziges Bier/Bier/Feines Bier + ingredients: - Wheat/6 - cookingtime: 8 - distillruns: 0 - wood: 0 - age: 3 - color: ORANGE - difficulty: 1 - alcohol: 6 - 3: - name: Ranziges Dunkelbier/Dunkelbier/Feines Dunkelbier - ingredients: + cookingtime: 8 + distillruns: 0 + wood: 0 + age: 3 + color: ORANGE + difficulty: 1 + lore: + - +++ &8Das perlt + alcohol: 6 + + dunkelbier: + name: Ranziges Dunkelbier/Dunkelbier/Feines Dunkelbier + ingredients: - Wheat/6 - cookingtime: 8 - distillruns: 0 - wood: 4 - age: 8 - color: BLACK - difficulty: 2 - alcohol: 7 - 4: - name: Scheußlicher Met/Met/&6Goldener Met - ingredients: + cookingtime: 8 + distillruns: 0 + wood: 4 + age: 8 + color: BLACK + difficulty: 2 + alcohol: 7 + + met: + name: Scheußlicher Met/Met/&6Goldener Met + ingredients: - Sugar_Cane/6 - cookingtime: 3 - distillruns: 0 - wood: 2 - age: 4 - color: ORANGE - difficulty: 2 - alcohol: 9 - 5: - name: Apfelmet/Süßer Apfelmet/&6Goldensüßer Apfelmet - ingredients: + cookingtime: 3 + distillruns: 0 + wood: 2 + age: 4 + color: ORANGE + difficulty: 2 + alcohol: 9 + lore: + - +++ Hat einen goldenen Schein + + apfelmet: + name: Apfelmet/Süßer Apfelmet/&6Goldensüßer Apfelmet + ingredients: - Sugar_Cane/6 - Apple/2 - cookingtime: 4 - distillruns: 0 - wood: 2 - age: 4 - color: ORANGE - difficulty: 4 - alcohol: 12 - effects: + cookingtime: 4 + distillruns: 0 + wood: 2 + age: 4 + color: ORANGE + difficulty: 4 + alcohol: 12 + lore: + - + Ist da wirklich Apfel drin? + - ++ Schmeckt nach süßem Apfel + - +++ Hat eine wunderbare Apfelnote + effects: - WATER_BREATHING/1-2/150 - 6: - name: Bitterer Rum/Würziger Rum/&6Goldener Rum - ingredients: + + rum: + name: Bitterer Rum/Würziger Rum/&6Goldener Rum + ingredients: - Sugar_Cane/14 - cookingtime: 5 - distillruns: 2 - distilltime: 30 - wood: 2 - age: 14 - color: DARK_RED - difficulty: 6 - alcohol: 30 - effects: + cookingtime: 5 + distillruns: 2 + distilltime: 30 + wood: 2 + age: 14 + color: DARK_RED + difficulty: 6 + alcohol: 30 + effects: - FIRE_RESISTANCE/1/20-100 - POISON/1-0/30-0 - 7: - name: Abgeranzter Vodka/Vodka/Russischer Vodka - ingredients: + + vodka: + name: Abgeranzter Vodka/Vodka/Russischer Vodka + ingredients: - Potato/10 - cookingtime: 15 - distillruns: 3 - age: 0 - color: BRIGHT_GREY - difficulty: 4 - alcohol: 20 - effects: + cookingtime: 15 + distillruns: 3 + age: 0 + color: BRIGHT_GREY + difficulty: 4 + alcohol: 20 + lore: + - + &8Fast nicht trinkbar + effects: - WEAKNESS/15 - POISON/10 - 8: - name: minderwertiger Absinth/Absinth/Starker Absinth - ingredients: + + absinth: + name: minderwertiger Absinth/Absinth/Starker Absinth + ingredients: - Grass/15 - cookingtime: 3 - distillruns: 6 - distilltime: 80 - color: GREEN - difficulty: 8 - alcohol: 45 - effects: + cookingtime: 3 + distillruns: 6 + distilltime: 80 + color: GREEN + difficulty: 8 + alcohol: 45 + effects: - POISON/20-30 - 9: - name: Kartoffelsuppe - ingredients: + + kartoffelsuppe: + name: Kartoffelsuppe + ingredients: - Potato/5 - Grass/3 - cookingtime: 3 - color: PINK - difficulty: 1 - effects: + cookingtime: 3 + color: PINK + difficulty: 1 + effects: - HEAL/0-1 - 10: - name: Fader Kaffee/Kaffee/Starker Kaffee - ingredients: + + kaffee: + name: Fader Kaffee/Kaffee/Starker Kaffee + ingredients: - Cocoa_Beans/12 - Milk_Bucket/2 - cookingtime: 2 - color: BLACK - difficulty: 3 - effects: + cookingtime: 2 + color: BLACK + difficulty: 3 + lore: + &8Bestimmt schon eine Woche alt + effects: - REGENERATION/1/2-5 - SPEED/1/30-140 @@ -243,27 +403,13 @@ recipes: # Der Serveradmin kann neue Rezepte hinzufügen und bestehende ändern, um das Abschauen aus der Standardconfig zu verhindern. -# cooked: ALLE möglichen Zutaten und die nach dem Gähren daraus entstehenden Tranknamen: -# [Beispiel] MATERIAL: Name nach Gähren - -cooked: - Wheat: Getreideferment - Sugar_Cane: Zuckersud - Apple: Apfelmost - Potato: Kartoffelmaische - Grass: Kräuterbrühe - Red_Mushroom: Pilzsud - Cocoa_Beans: Farbige Brühe - Milk_Bucket: Milchiges Wasser - - - # -- Plugin Kompatiblität -- # Andere Plugins (wenn installiert) nach Rechten zum öffnen von Fässern checken [true] useWorldGuard: true useLWC: true useGriefPrevention: true +useGMInventories: true # Änderungen an Fassinventaren mit LogBlock aufzeichen [true] useLogBlock: true @@ -277,6 +423,9 @@ useLogBlock: true # Unten kann noch eingestellt werden wie und was verändert wird enableChatDistortion: true +# In den Serverlog loggen was der Spieler tatsächlich geschrieben hat, bevor seine Worte verändert wurden [false] +logRealChat: false + # Text nach den angegebenen Kommandos wird bei Trunkenheit ebenfalls Verändert (Liste) [- /gl] distortCommands: - /gl diff --git a/resources/config/v13/en/config.yml b/resources/config/v13/en/config.yml index cc68467..92b636b 100644 --- a/resources/config/v13/en/config.yml +++ b/resources/config/v13/en/config.yml @@ -3,7 +3,6 @@ # -- Settings -- # Defaults are written in [] -# Deleting of single settings disables them # Languagefile to be used (found in plugins/Brewery/languages) language: en @@ -41,8 +40,8 @@ pukeDespawntime: 60 # Consumable Item/strength. Decreases the alcohol level by when consumed. (list) drainItems: -- Bread/4 -- Milk_Bucket/2 + - Bread/4 + - Milk_Bucket/2 # Time (in days) that drunkeness-data stays in memory after a player goes offline, to apply hangover etc. [7] hangoverDays: 7 @@ -51,12 +50,26 @@ hangoverDays: 7 colorInBarrels: true colorInBrewer: true +# Always show the 1-5 stars on the item depending on the quality. If false, they will only appear when brewing [true] +alwaysShowQuality: true + +# Always show the alcohol content on the item. If false, it will only show in the brewing stand [false] +alwaysShowAlc: false + # If a Large Barrel can be opened by clicking on any of its blocks, not just Spigot or Sign. This is always true for Small Barrels. [true] openLargeBarrelEverywhere: true # How many Brewery drinks can be put into the Minecraft barrels [6] maxBrewsInMCBarrels: 6 +# The used Ingredients and other brewing-data is saved to all Brewery Items. To prevent +# hacked clients from reading what exactly was used to brew an item, the data can be encoded/scrambled. +# This is a fast process to stop players from hacking out recipes, once they get hold of a brew. +# Only drawback: brew items can only be used on another server with the same encodeKey. +# So enable this if you want to make recipe cheating harder, but don't share any brews by world download, schematics, or other means. [false] +enableEncode: false +encodeKey: 0 + # Enable checking for Updates, Checks the curseforge api for updates to Brewery [true] # If an Update is found a Message is logged on Server-start and displayed to OPs joining the game updateCheck: true @@ -64,8 +77,117 @@ updateCheck: true # Autosave interval in minutes [3] autosave: 3 +# Show debug messages in log [false] +debug: false + # Config Version -version: '1.8' +version: '2.0' + + + +# -- Define custom items -- +# The defined id can then be used in recipes + +# matchAny: true if it is already enough if one of the info matches +# material: Which type the item has to be +# name: Which name the item has to be (Formatting codes possible: such as &6) +# lore: What has to be in the lore of the item + +customItems: + ex-item: + # A Barrier item called Wall and has the given line in its lore + material: Barrier + name: 'Wall' + lore: + - '&7Very well protected' + + ex-item2: + # Using matchAny only one of the following has to match. + # In this case on of the door types, or an item called Beechwood Door, or an item with 'A door' in its lore + matchAny: true + material: + - Acacia_Door + - Oak_Door + - Spruce_Door + name: + - 'Beechwood Door' + lore: + - 'A door' + + rasp: + name: '&cRaspberry' + + +# -- Ingredients in the Cauldron -- +# Which Ingredients are accepted by the Cauldron and the base potion resulting from them + + # name: Name of the base potion coming out of the Cauldron (Formatting codes possible: such as &6) + # ingredients: List of 'material/amount' + # With an item in your hand, use /brew ItemName to get its material for use in a recipe + # (Item-ids instead of material are not supported by bukkit anymore and will not work) + # A list of materials can be found here: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html + # color: Color of the potion from a cauldron. + # Usable Colors: DARK_RED, RED, BRIGHT_RED, ORANGE, PINK, BLUE, CYAN, WATER, GREEN, BLACK, GREY, BRIGHT_GREY + # Or RGB colors (hex: for example '99FF33') (with '') (search for "HTML color" on the internet) + # lore: List of additional text on the base potion. (Formatting codes possible: such as &6) + +cauldron: + ex: + name: Example + ingredients: + - Bedrock/2 + - Diamond + color: BLACK + lore: + - An example for a Base Potion + - This is how it comes out of a Cauldron + + # -- One Ingredient: -- + wheat: + name: Fermented wheat + ingredients: Wheat + + sugarcane: + name: Sugar brew + ingredients: Sugar_Cane + color: 'f1ffad' # yellowish green + + apple: + name: Apple cider + ingredients: Apple + + potato: + name: Potatomash + ingredients: Potato + + grass: + name: Boiled herbs + ingredients: Grass + color: '99ff66' # bright green + + rmushroom: + name: Mushroom brew + ingredients: Red_Mushroom + color: 'ff5c33' # amber red + + cocoa: + name: Chocolately brew + ingredients: Cocoa_Beans + color: '804600' # mocca + + milk: + name: Milky water + ingredients: Milk_Bucket + color: BRIGHT_GREY + + # -- Multiple Ingredients: -- + apfelmet_base: + name: Apple-Sugar brew + ingredients: + - Sugar_Cane/3 + - Apple + color: 'e1ff4d' # greenish yellow + # -- Recipes for Potions -- @@ -75,6 +197,8 @@ version: '1.8' # With an item in your hand, use /brew ItemName to get its material for use in a recipe # (Item-ids instead of material are not supported by bukkit anymore and will not work) # A list of materials can be found here: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html +# Plugin items with 'plugin:id' (Currently supporting ExoticGarden, Slimefun, MMOItems, Brewery) +# Or a custom item defined above # cookingtime: Time in real minutes ingredients have to boil # distillruns: How often it has to be distilled for full alcohol (0=without distilling) # distilltime: How long (in seconds) one distill-run takes (0=Default time of 40 sec) MC Default would be 20 sec @@ -86,6 +210,12 @@ version: '1.8' # Or RGB colors (hex: for example '99FF33') (with '') (search for "HTML color" on the internet) # difficulty: 1-10 accuracy needed to get good quality (1 = unaccurate/easy, 10 = very precise/hard) # alcohol: Absolute amount of alcohol 0-100 in a perfect potion (will be added directly to the player, where 100 means fainting) +# lore: List of additional text on the finished brew. (Formatting codes possible: such as &6) +# Specific lore for quality possible, using + bad, ++ normal, +++ good, added to the front of the line. +# servercommands: List of Commands executed by the Server when drinking the brew +# playercommands: List of Commands executed by the Player when drinking the brew +# drinkmessage: Chat-message to the Player when drinking the Brew +# drinktitle: Title on Screen to the Player when drinking the Brew # effects: List of effect/level/duration Special potion-effect when drinking, duration in sek. # Possible Effects: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/potion/PotionEffectType.html # Level or Duration ranges may be specified with a "-", ex. 'SPEED/1-2/30-40' = lvl 1 and 30 sec at worst and lvl 2 and 40 sec at best @@ -93,145 +223,181 @@ version: '1.8' # Highest possible Duration: 1638 sec. Instant Effects dont need any duration specified. recipes: -# Example Recipe with every possible entry first: - 0: - name: Bad Example/Example/Good Example - ingredients: - - Sugar_Cane/5 + # Example Recipe with every possible entry first: + ex: + name: Bad Example/Example/Good Example + ingredients: - Diamond/1 - - Cocoa_Beans/20 - Spruce_Planks/8 - Bedrock/1 - cookingtime: 3 - distillruns: 2 - distilltime: 60 - wood: 4 - age: 11 - color: DARK_RED - difficulty: 3 - alcohol: 23 - effects: + - Brewery:Wheatbeer/2 +# - ExoticGarden:Grape/3 + - ex-item/4 + cookingtime: 3 + distillruns: 2 + distilltime: 60 + wood: 4 + age: 11 + color: DARK_RED + difficulty: 3 + alcohol: 23 + lore: + - This is an examble brew + - ++Just a normal Example + - This text would be on the brew + - + Smells disgusting + - ++ Smells alright + - +++ Smells really good + servercommands: + - weather clear + playercommands: + - homes + drinkmessage: Tastes good + drinktitle: Warms you from inside + effects: - FIRE_RESISTANCE/20 - HEAL/1 - WEAKNESS/2-3/50-60 - POISON/1-0/20-0 - 1: - name: Skunky Wheatbeer/Wheatbeer/Fine Wheatbeer - ingredients: + + wheatbeer: + name: Skunky Wheatbeer/Wheatbeer/Fine Wheatbeer + ingredients: - Wheat/3 - cookingtime: 8 - distillruns: 0 - wood: 1 - age: 2 - color: BRIGHT_GREY - difficulty: 1 - alcohol: 5 - 2: - name: Skunky Beer/Beer/Fine Beer - ingredients: + cookingtime: 8 + distillruns: 0 + wood: 1 + age: 2 + color: BRIGHT_GREY + difficulty: 1 + alcohol: 5 + + beer: + name: Skunky Beer/Beer/Fine Beer + ingredients: - Wheat/6 - cookingtime: 8 - distillruns: 0 - wood: 0 - age: 3 - color: ORANGE - difficulty: 1 - alcohol: 6 - 3: - name: Skunky Darkbeer/Darkbeer/Fine Darkbeer - ingredients: + cookingtime: 8 + distillruns: 0 + wood: 0 + age: 3 + color: ORANGE + difficulty: 1 + lore: + - +++ &8Crisp taste + alcohol: 6 + + darkbeer: + name: Skunky Darkbeer/Darkbeer/Fine Darkbeer + ingredients: - Wheat/6 - cookingtime: 8 - distillruns: 0 - wood: 4 - age: 8 - color: BLACK - difficulty: 2 - alcohol: 7 - 4: - name: Awkward Mead/Mead/&6Golden Mead - ingredients: + cookingtime: 8 + distillruns: 0 + wood: 4 + age: 8 + color: BLACK + difficulty: 2 + lore: + - +++ &8Roasted taste + alcohol: 7 + + mead: + name: Awkward Mead/Mead/&6Golden Mead + ingredients: - Sugar_Cane/6 - cookingtime: 3 - distillruns: 0 - wood: 2 - age: 4 - color: ORANGE - difficulty: 2 - alcohol: 9 - 5: - name: Apple Mead/Sweet Apple Mead/&6Sweet Golden Apple Mead - ingredients: + cookingtime: 3 + distillruns: 0 + wood: 2 + age: 4 + color: ORANGE + difficulty: 2 + lore: + - +++ Has a golden shine + alcohol: 9 + + ap_mead: + name: Apple Mead/Sweet Apple Mead/&6Sweet Golden Apple Mead + ingredients: - Sugar_Cane/6 - Apple/2 - cookingtime: 4 - distillruns: 0 - wood: 2 - age: 4 - color: ORANGE - difficulty: 4 - alcohol: 12 - effects: + cookingtime: 4 + distillruns: 0 + wood: 2 + age: 4 + color: ORANGE + difficulty: 4 + alcohol: 12 + lore: + - +Is there any Apple in this? + - ++Refreshing taste of Apple + - +++Sweetest hint of Apple + effects: - WATER_BREATHING/1-2/150 - 6: - name: Bitter Rum/Spicy Rum/&6Golden Rum - ingredients: + + rum: + name: Bitter Rum/Spicy Rum/&6Golden Rum + ingredients: - Sugar_Cane/14 - cookingtime: 5 - distillruns: 2 - distilltime: 30 - wood: 2 - age: 14 - color: DARK_RED - difficulty: 6 - alcohol: 30 - effects: + cookingtime: 5 + distillruns: 2 + distilltime: 30 + wood: 2 + age: 14 + color: DARK_RED + difficulty: 6 + alcohol: 30 + effects: - FIRE_RESISTANCE/1/20-100 - POISON/1-0/30-0 - 7: - name: Lousy Vodka/Vodka/Russian Vodka - ingredients: + + vodka: + name: Lousy Vodka/Vodka/Russian Vodka + ingredients: - Potato/10 - cookingtime: 15 - distillruns: 3 - age: 0 - color: BRIGHT_GREY - difficulty: 4 - alcohol: 20 - effects: + cookingtime: 15 + distillruns: 3 + age: 0 + color: BRIGHT_GREY + difficulty: 4 + alcohol: 20 + lore: +&8Almost undrinkable + effects: - WEAKNESS/15 - POISON/10 - 8: - name: Poor Absinthe/Absinthe/Strong Absinthe - ingredients: + + absinthe: + name: Poor Absinthe/Absinthe/Strong Absinthe + ingredients: - Grass/15 - cookingtime: 3 - distillruns: 6 - distilltime: 80 - color: GREEN - difficulty: 8 - alcohol: 45 - effects: + cookingtime: 3 + distillruns: 6 + distilltime: 80 + color: GREEN + difficulty: 8 + alcohol: 45 + effects: - POISON/20-30 - 9: - name: Potato soup - ingredients: + + potato_soup: + name: Potato soup + ingredients: - Potato/5 - Grass/3 - cookingtime: 3 - color: PINK - difficulty: 1 - effects: + cookingtime: 3 + color: PINK + difficulty: 1 + effects: - HEAL/0-1 - 10: - name: Stale Coffee/Coffee/Strong Coffee - ingredients: + + coffee: + name: Stale Coffee/Coffee/Strong Coffee + ingredients: - Cocoa_Beans/12 - Milk_Bucket/2 - cookingtime: 2 - color: BLACK - difficulty: 3 - effects: + cookingtime: 2 + color: BLACK + difficulty: 3 + lore: + &8Probably a week old + effects: - REGENERATION/1/2-5 - SPEED/1/30-140 @@ -240,28 +406,13 @@ recipes: # It is up to the Serveradmin to change and add Recipes, so players cannot cheat from the default config. - -# cooked: EVERY possible ingredient and the names for the originating potions after fermenting: -# [Example] MATERIAL: Name after cooking - -cooked: - Wheat: Fermented wheat - Sugar_Cane: Sugar brew - Apple: Apple cider - Potato: Potatomash - Grass: Boiled herbs - Red_Mushroom: Mushroom brew - Cocoa_Beans: Colored brew - Milk_Bucket: Milky water - - - # -- Plugin Compatibility -- # Enable checking of other Plugins (if installed) for Barrel Permissions [true] useWorldGuard: true useLWC: true useGriefPrevention: true +useGMInventories: true # Enable the Logging of Barrel Inventories to LogBlock [true] useLogBlock: true @@ -269,8 +420,7 @@ useLogBlock: true # -- Chat Distortion Settings -- -# If written Chat is distorted when the Player is Drunk, -# so that it looks like drunk writing +# If written Chat is distorted when the Player is Drunk, so that it looks like drunk writing # How much the chat is distorted depends on how drunk the Player is # Below are settings for what and how changes in chat occur enableChatDistortion: true diff --git a/resources/config/v13/fr/config.yml b/resources/config/v13/fr/config.yml index cc000f0..f622d0b 100644 --- a/resources/config/v13/fr/config.yml +++ b/resources/config/v13/fr/config.yml @@ -1,9 +1,9 @@ # config for Brewery.jar +# Quelques traductions en français ont été faites avec DeepL # -- Paramètres -- # Les paramètres par défaut sont entre [] -# Supprimer un paramètre le désactive # Fichier de langage utilisé (trouvable dans plugins/Brewery/languages) language: fr @@ -41,8 +41,8 @@ pukeDespawntime: 60 # Consommables Objet/Force. Réduit le montant d'alcool par lors de la consommation. (list) drainItems: -- Bread/4 -- Milk_Bucket/2 + - Bread/4 + - Milk_Bucket/2 # Temps (en jours) pour que les données d'ivresse restent sauvergardées lorsque le joueur est déconnecté, pour appliquer les effets. [7] hangoverDays: 7 @@ -51,12 +51,26 @@ hangoverDays: 7 colorInBarrels: true colorInBrewer: true +# Toujours montrer les 1-5 étoiles sur les objets en fonction de leur qualité. S'ils sont faux, ils n'apparaîtront que lors de l'infusion. [true] +alwaysShowQuality: true + +# Toujours indiquer la teneur en alcool sur les objets. S'il est false, il n'apparaîtra que dans le stand de brassage. [false] +alwaysShowAlc: false + # Si le grand tonneau peut être ouvert en cliquant sur n'importe quel bloc, non seulement le robinet ou le panneau. Toujours "true" pour les petits tonneaux. [true] openLargeBarrelEverywhere: true -# How many Brewery drinks can be put into the Minecraft barrels [6] +# Combien de boissons de brasserie peuvent être mises dans les barils Minecraft [6] maxBrewsInMCBarrels: 6 +# Les ingrédients et autres données de brassage utilisés sont sauvegardés dans tous les articles de brasserie. [false] +# Pour empêcher les clients piratés de lire exactement ce qui a été utilisé pour infuser un élément, les données peuvent être encodées/brouillées. +# Il s'agit d'un processus rapide pour empêcher les joueurs de pirater des recettes, une fois qu'ils mettent la main sur une bière. +# Seul inconvénient: Les boissons brassicoles ne peuvent être utilisés que sur un autre serveur avec la même clé de chiffrement. +# Activez cette option si vous voulez rendre la tricherie des recettes plus difficile, mais ne partagez pas les infusions par téléchargement mondial, schémas ou autres moyens. +enableEncode: false +encodeKey: 0 + # Enable checking for Updates, Checks the curseforge api for updates to Brewery [true] # If an Update is found a Message is logged on Server-start and displayed to OPs joining the game updateCheck: true @@ -64,29 +78,147 @@ updateCheck: true # Intervale de la sauvegarde automatique en minutes [3] autosave: 3 +# Show debug messages in log [false] +debug: false + # Version de configuration -version: '1.8' +version: '2.0' + + + +# -- Définir des objets personnalisés -- +# L'id défini peut ensuite être utilisé dans les recettes + +# matchAny: si c'est déjà assez si l'une des infos correspond +# material: le type d'article à utiliser +# name: Quel nom l'article doit porter (codes de formatage possibles : tels que &6) +# lore: Ce qui doit être dans la lore de l'objet + +customItems: + ex-item: + # Un objet Barrière appelé "Wall" et qui a la ligne donnée dans sa lore + material: Barrier + name: 'Wall' + lore: + - '&7Very well protected' + + ex-item2: + # En utilisant matchAny, un seul des éléments suivants doit correspondre. + # Dans ce cas, l'un des types de porte, ou un article appelé "Beechwood Door", ou un objet avec "A door" dans sa lore. + matchAny: true + material: + - Acacia_Door + - Oak_Door + - Spruce_Door + name: + - 'Beechwood Door' + lore: + - 'A door' + + rasp: + name: '&cRaspberry' + + +# -- Ingrédients dans le chaudron -- +# Quels sont les ingrédients acceptés par le chaudron et la potion de base qui en résulte + + # name: Nom de la potion de base qui sort du chaudron (codes de formatage possibles : tels que &6) + # ingredients: Liste des 'matériaux/montant' + # Avec un objet en main, utilisez /brew ItemName pour obtenir son matériau pour une recette de cuisine + # (Les id d'objets à la place des matériaux sont obsolètes pour bukkit) + # Une liste des matériaux peuvent-être trouvés ici: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html + # color : Couleur de la potion provenant d'un chaudron. + # Couleurs disponibles : DARK_RED, RED, BRIGHT_RED, ORANGE, PINK, BLUE, CYAN, WATER, GREEN, BLACK, GREY, BRIGHT_GREY + # (Dans l'ordre : Rouge foncé, Rouge, Rouge clair, Orange, Rose, Bleu, Cyan, Eau, Vert, Noir, Gris, Gris clair) + # Ou couleurs RGB (hex: par exemple '99FF33') (avec '') (recherche de "HTML color" sur internet) + # lore: Liste de texte supplémentaire sur la potion de base. (Formatting codes possible: such as &6) + +cauldron: + ex: + name: Exemple + ingredients: + - Bedrock/2 + - Diamond + color: BLACK + lore: + - Un exemple pour une potion de base + - Voici comment il sort d'un chaudron + + # -- Un ingrédient: -- + wheat: + name: Blé fermenté + ingredients: Wheat + + sugarcane: + name: Sucre fermenté + ingredients: Sugar_Cane + color: 'f1ffad' # yellowish green + + apple: + name: Cidre de pommes + ingredients: Apple + + potato: + name: Purée de Pommes de Terre + ingredients: Potato + + grass: + name: Herbes bouillies + ingredients: Grass + color: '99ff66' # bright green + + rmushroom: + name: Champignons fermentés + ingredients: Red_Mushroom + color: 'ff5c33' # amber red + + cocoa: + name: Infusion au chocolat + ingredients: Cocoa_Beans + color: '804600' # mocca + + milk: + name: Eau laiteuse + ingredients: Milk_Bucket + color: BRIGHT_GREY + + # -- Ingrédients multiples: -- + apfelmet_base: + name: Brassée pomme-sucre + ingredients: + - Sugar_Cane/3 + - Apple + color: 'e1ff4d' # greenish yellow # -- Recette pour les boissons -- # name: Différents noms pour la mauvaise/moyen/bonne qualité (Les codes de mise en forme sont pris en charge: comme par exemple &6 pour la couleur Or.) # ingredients: Liste des 'matériaux/montant' -# With an item in your hand, use /brew ItemName to get its material for use in a recipe +# Avec un objet en main, utilisez /brew ItemName pour obtenir son matériau pour une recette de cuisine # (Les id d'objets à la place des matériaux sont obsolètes pour bukkit) # Ex: 'Sugar_Cane' # Une liste des matériaux peuvent-être trouvés ici: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html +# Plugin items avec 'plugin:id' (Actuellement supporté ExoticGarden, Slimefun, MMOItems, Brewery) +# Ou un élément personnalisé défini ci-dessus # cookingtime: Temps en minutes réelles durant lesquelles les ingrédients devront bouillir # distillruns: Combien de fois le breuvage devra être distillé pour un alcool de qualité (0=Ne pas distiller) -# distilltime: How long (in seconds) one distill-run takes (0=Default time of 40 sec) MC Default would be 20 sec +# distilltime: Combien de temps (en secondes) dure une distillation (0=Temps par défaut de 40 secondes) MC Par défaut serait de 20 secondes # wood: Type de bois du baril 0=aucun 1=Bouleau 2=Chêne 3=Jungle 4=Pin 5=Acacia 6=Chêne Noir -# The Minecraft barrel is made of oak +# Le tonneau Minecraft est en chêne. # age: Temps en jours de Minecraft, la potion devra être âgée dans un baril. 0=Pas besoin d'âge # color: Couleur de la potion après distillation/avoir laissé vieillir. -# Couleurs disponibles: DARK_RED, RED, BRIGHT_RED, ORANGE, PINK, BLUE, CYAN, WATER, GREEN, BLACK, GREY, BRIGHT_GREY (Dans l'ordre : Rouge foncé, Rouge, Rouge clair, Orange, Rose, Bleu, Cyan, Eau, Vert, Noir, Gris, Gris clair) -# Or RGB colors (hex: for example '99FF33') (with '') (search for "HTML color" on the internet) +# Couleurs disponibles: DARK_RED, RED, BRIGHT_RED, ORANGE, PINK, BLUE, CYAN, WATER, GREEN, BLACK, GREY, BRIGHT_GREY +# (Dans l'ordre : Rouge foncé, Rouge, Rouge clair, Orange, Rose, Bleu, Cyan, Eau, Vert, Noir, Gris, Gris clair) +# Ou couleurs RGB (hex: par exemple '99FF33') (avec '') (recherche de "HTML color" sur internet) # difficulty: 1-10 précision nécessaire pour obtenir une bonne qualité (1 = imprécis/facile, 10 = très précis/difficile) # alcohol: Le montant d'alcool absolu dans une boisson parfaite (cela sera ajouté directement au joueur, où 100% entraînera l'évanouissement), un degré d'alcooléisme en fait +# lore: Liste des textes supplémentaires sur le breuvage fini. (Codes de formatage possibles : tels que &6) +# Texte spécifique de qualité possible, en utilisant + mauvais, ++ normal, +++ bon, ajouté à l'avant de la ligne. +# servercommands: Liste des commandes exécutées par le serveur lors de la consommation de la potion +# playercommands: Liste des commandes exécutées par le joueur lors de la consommation de la potion +# drinkmessage: Chat-message au joueur lorsqu'il boit la potion +# drinktitle: Titre à l'écran du joueur lorsqu'il boit la potion # effects: Liste des effets/durée en secondes lors de la consommation. # Effets posssible: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/potion/PotionEffectType.html # POUR LES EFFETS EN FONCTIONS DE LA QUALITE : Les Niveaux (I ou II) ou les Intervalles de durées d'effets doivent être spécifiés avec un "-". @@ -98,144 +230,168 @@ version: '1.8' recipes: # Exemple de recette avec tous les paramètres possibles : - 0: - name: Mauvais Exemple/Exemple/Bonne Exemple - ingredients: - - Sugar_Cane/5 + 0: + name: Mauvais Exemple/Exemple/Bonne Exemple + ingredients: - Diamond/1 - - Cocoa_Beans/20 - Spruce_Planks/8 - Bedrock/1 - cookingtime: 3 - distillruns: 2 - distilltime: 60 - wood: 4 - age: 11 - color: DARK_RED - difficulty: 3 - alcohol: 23 - effects: + - Brewery:Bière Blanche/2 +# - ExoticGarden:Grape/3 + - ex-item/4 + cookingtime: 3 + distillruns: 2 + distilltime: 60 + wood: 4 + age: 11 + color: DARK_RED + difficulty: 3 + alcohol: 23 + lore: + - C'est un breuvage d'exemple + - ++Juste un exemple normal + - Ce texte serait sur le breuvage + - + Ça sent dégueulasse. + - ++ Ça sent bon + - +++ Ça sent vraiment bon + servercommands: + - weather clear + playercommands: + - homes + drinkmessage: C'est bon au goût + drinktitle: vous réchauffe à l'intérieur + effects: - FIRE_RESISTANCE/20 - HEAL/1 - WEAKNESS/2-3/50-60 - POISON/1-0/20-0 - 1: - name: Bière Blanche Fade/Bière Blanche/Bonne Bière Blanche - ingredients: + + 1: + name: Bière Blanche Fade/Bière Blanche/Bonne Bière Blanche + ingredients: - Wheat/3 - cookingtime: 8 - distillruns: 0 - wood: 1 - age: 2 - color: BRIGHT_GREY - difficulty: 1 - alcohol: 5 - 2: - name: Bière Fade/Bière/Bonne Bière - ingredients: + cookingtime: 8 + distillruns: 0 + wood: 1 + age: 2 + color: BRIGHT_GREY + difficulty: 1 + alcohol: 5 + + 2: + name: Bière Fade/Bière/Bonne Bière + ingredients: - Wheat/6 - cookingtime: 8 - distillruns: 0 - wood: 0 - age: 3 - color: ORANGE - difficulty: 1 - alcohol: 6 - 3: - name: Bière Brune Fade/Bière Brune/Bonne Bière Brune - ingredients: + cookingtime: 8 + distillruns: 0 + wood: 0 + age: 3 + color: ORANGE + difficulty: 1 + alcohol: 6 + + 3: + name: Bière Brune Fade/Bière Brune/Bonne Bière Brune + ingredients: - Wheat/6 - cookingtime: 8 - distillruns: 0 - wood: 4 - age: 8 - color: BLACK - difficulty: 2 - alcohol: 7 - 4: - name: Hydromel Bizarre/Hydromel/&6Hydromel Doré - ingredients: + cookingtime: 8 + distillruns: 0 + wood: 4 + age: 8 + color: BLACK + difficulty: 2 + alcohol: 7 + + 4: + name: Hydromel Bizarre/Hydromel/&6Hydromel Doré + ingredients: - Sugar_Cane/6 - cookingtime: 3 - distillruns: 0 - wood: 2 - age: 4 - color: ORANGE - difficulty: 2 - alcohol: 9 - 5: - name: Hydromel de Pommes/Doux Hydromel de Pommes/&6Doux Hydromel de Pommes Dorées - ingredients: + cookingtime: 3 + distillruns: 0 + wood: 2 + age: 4 + color: ORANGE + difficulty: 2 + alcohol: 9 + + 5: + name: Hydromel de Pommes/Doux Hydromel de Pommes/&6Doux Hydromel de Pommes Dorées + ingredients: - Sugar_Cane/6 - Apple/2 - cookingtime: 4 - distillruns: 0 - wood: 2 - age: 4 - color: ORANGE - difficulty: 4 - alcohol: 12 - effects: + cookingtime: 4 + distillruns: 0 + wood: 2 + age: 4 + color: ORANGE + difficulty: 4 + alcohol: 12 + effects: - WATER_BREATHING/1-2/150 - 6: - name: Rhum Amer/Rhum Epicé/&6Rhum Doré - ingredients: + + 6: + name: Rhum Amer/Rhum Epicé/&6Rhum Doré + ingredients: - Sugar_Cane/14 - cookingtime: 5 - distillruns: 2 - distilltime: 30 - wood: 2 - age: 14 - color: DARK_RED - difficulty: 6 - alcohol: 30 - effects: + cookingtime: 5 + distillruns: 2 + distilltime: 30 + wood: 2 + age: 14 + color: DARK_RED + difficulty: 6 + alcohol: 30 + effects: - FIRE_RESISTANCE/1/20-100 - POISON/1-0/30-0 - 7: - name: Vodka Sale/Vodka/Vodka Russe - ingredients: + + 7: + name: Vodka Sale/Vodka/Vodka Russe + ingredients: - Potato/10 - cookingtime: 15 - distillruns: 3 - age: 0 - color: BRIGHT_GREY - difficulty: 4 - alcohol: 20 - effects: + cookingtime: 15 + distillruns: 3 + age: 0 + color: BRIGHT_GREY + difficulty: 4 + alcohol: 20 + effects: - WEAKNESS/15 - POISON/10 - 8: - name: Absinthe pauvre/Absinthe/Absinthe forte - ingredients: + + 8: + name: Absinthe pauvre/Absinthe/Absinthe forte + ingredients: - Grass/15 - cookingtime: 3 - distillruns: 6 - distilltime: 80 - color: GREEN - difficulty: 8 - alcohol: 45 - effects: + cookingtime: 3 + distillruns: 6 + distilltime: 80 + color: GREEN + difficulty: 8 + alcohol: 45 + effects: - POISON/20-30 - 9: - name: Potato soup - ingredients: + + 9: + name: Potato soup + ingredients: - Potato/5 - Grass/3 - cookingtime: 3 - color: PINK - difficulty: 1 - effects: + cookingtime: 3 + color: PINK + difficulty: 1 + effects: - HEAL/0-1 - 10: - name: Café fétide/Café/Café fort - ingredients: + + 10: + name: Café fétide/Café/Café fort + ingredients: - Cocoa_Beans/12 - Milk_Bucket/2 - cookingtime: 2 - color: BLACK - difficulty: 3 - effects: + cookingtime: 2 + color: BLACK + difficulty: 3 + effects: - REGENERATION/1/2-5 - SPEED/1/30-140 @@ -244,28 +400,13 @@ recipes: # Ce sera aux Admins des serveurs de changer et d'ajouter les recettes, ainsi les joueurs ne pourront pas tricher avec les configuration de base. - -# cooked: CHAQUE ingrédient possible avec le nom donné après la fermentation (la cuisson): -# [Exemple] MATERIEL: Nom après la cuisson - -cooked: - Wheat: Blé fermenté - Sugar_Cane: Sucre fermenté - Apple: Cidre de pommes - Potato: Purée de Pommes de Terre - Grass: Herbes bouillies - Red_Mushroom: Champignons fermentés - Cocoa_Beans: Fermentation colorée - Milk_Bucket: Eau laiteuse - - - # -- Compatibilité entre Plugins -- # Activer la vérification des autres plugins (si installés) pour les permissions des tonneaux. [true] useWorldGuard: true useLWC: true useGriefPrevention: true +useGMInventories: true # Activer l'historique du contenu des tonneaux avec LogBlock [true] useLogBlock: true @@ -273,10 +414,9 @@ useLogBlock: true # -- Paramètres de la distorsion du Chat -- -# If written Chat is distorted when the Player is Drunk, -# so that it looks like drunk writing -# How much the chat is distorted depends on how drunk the Player is -# Below are settings for what and how changes in chat occur +# Si le Chat écrit est déformé quand le joueur est ivre, de sorte qu'il ressemble à un chat bourré en train d'écrire +# Le degré de distorsion du chat dépend de l'état d'ébriété du joueur +# Ci-dessous sont les paramètres pour ce qui et comment les changements dans le chat se produisent enableChatDistortion: true # Ecrire dans les "logs" du serveur ce que le joueur devrait dire, à la place de la distorsion. [false] diff --git a/resources/config/v13/it/config.yml b/resources/config/v13/it/config.yml index 55649f8..c94df82 100644 --- a/resources/config/v13/it/config.yml +++ b/resources/config/v13/it/config.yml @@ -3,7 +3,6 @@ # -- Opzioni -- # I valori di default sono scritti fra [] -# Cancellare una voce la disabilita # Lingua da usare (fra quelle in plugins/Brewery/languages) language: it @@ -41,8 +40,8 @@ pukeDespawntime: 60 # Oggetto consumabile/forza. Questi oggetti se consumati calano il livello di alcool (della "forza" che avevi impsotato) (list) drainItems: -- Bread/4 -- Milk_Bucket/2 + - Bread/4 + - Milk_Bucket/2 # Tempo in giorni che la sbronza resta in memoria dopo che il giocatore va offline, cioè il tempo per cui i postumi della sbornia durano. [7] hangoverDays: 7 @@ -51,12 +50,26 @@ hangoverDays: 7 colorInBarrels: true colorInBrewer: true +# Always show the 1-5 stars on the item depending on the quality. If false, they will only appear when brewing [true] +alwaysShowQuality: true + +# Always show the alcohol content on the item. If false, it will only show in the brewing stand [false] +alwaysShowAlc: false + # Se un barile grande può essere aperto cliccandoci sopra, non solo sul cartello e sulla staccionata. Questo è sempre true per i barili piccoli. [true] openLargeBarrelEverywhere: true # How many Brewery drinks can be put into the Minecraft barrels [6] maxBrewsInMCBarrels: 6 +# The used Ingredients and other brewing-data is saved to all Brewery Items. To prevent +# hacked clients from reading what exactly was used to brew an item, the data can be encoded/scrambled. +# This is a fast process to stop players from hacking out recipes, once they get hold of a brew. +# Only drawback: brew items can only be used on another server with the same encodeKey. +# So enable this if you want to make recipe cheating harder, but don't share any brews by world download, schematics, or other means. [false] +enableEncode: false +encodeKey: 0 + # Abilita il controllo degli aggiornamenti, controlla l'API di CurseForge per eventuali aggiornamenti di Brewery [true] # Se quando un aggiornamento viene trovato un messaggio è loggato e mostrato agli OPs quando entrano in gioco. updateCheck: true @@ -64,9 +77,116 @@ updateCheck: true # Intervallo di autosalvataggio in minuti [3] autosave: 3 -# Versione del config -version: '1.8' +# Show debug messages in log [false] +debug: false +# Versione del config +version: '2.0' + + + +# -- Define custom items -- +# The defined id can then be used in recipes + +# matchAny: true if it is already enough if one of the info matches +# material: Which type the item has to be +# name: Which name the item has to be (Formatting codes possible: such as &6) +# lore: What has to be in the lore of the item + +customItems: + ex-item: + # A Barrier item called Wall and has the given line in its lore + material: Barrier + name: 'Wall' + lore: + - '&7Very well protected' + + ex-item2: + # Using matchAny only one of the following has to match. + # In this case on of the door types, or an item called Beechwood Door, or an item with 'A door' in its lore + matchAny: true + material: + - Acacia_Door + - Oak_Door + - Spruce_Door + name: + - 'Beechwood Door' + lore: + - 'A door' + + rasp: + name: '&cRaspberry' + + +# -- Ingredients in the Cauldron -- +# Which Ingredients are accepted by the Cauldron and the base potion resulting from them + + # name: Name of the base potion coming out of the Cauldron (Formatting codes possible: such as &6) + # ingredients: List of 'material/amount' + # With an item in your hand, use /brew ItemName to get its material for use in a recipe + # (Item-ids instead of material are not supported by bukkit anymore and will not work) + # A list of materials can be found here: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html + # color: Color of the potion from a cauldron. + # Usable Colors: DARK_RED, RED, BRIGHT_RED, ORANGE, PINK, BLUE, CYAN, WATER, GREEN, BLACK, GREY, BRIGHT_GREY + # Or RGB colors (hex: for example '99FF33') (with '') (search for "HTML color" on the internet) + # lore: List of additional text on the base potion. (Formatting codes possible: such as &6) + +cauldron: + ex: + name: Example + ingredients: + - Bedrock/2 + - Diamond + color: BLACK + lore: + - An example for a Base Potion + - This is how it comes out of a Cauldron + + # -- One Ingredient: -- + wheat: + name: Frumento fermentato + ingredients: Wheat + + sugarcane: + name: Miscela zuccherata + ingredients: Sugar_Cane + color: 'f1ffad' # yellowish green + + apple: + name: Sidro di mele + ingredients: Apple + + potato: + name: Purè di patate + ingredients: Potato + + grass: + name: Erbe bollite + ingredients: Grass + color: '99ff66' # bright green + + rmushroom: + name: Miscela ai funghi + ingredients: Red_Mushroom + color: 'ff5c33' # amber red + + cocoa: + name: Miscela colorata + ingredients: Cocoa_Beans + color: '804600' # mocca + + milk: + name: Acqua lattea + ingredients: Milk_Bucket + color: BRIGHT_GREY + + # -- Multiple Ingredients: -- + apfelmet_base: + name: Apple-Sugar brew + ingredients: + - Sugar_Cane/3 + - Apple + color: 'e1ff4d' # greenish yellow # -- Ricette per pozioni -- @@ -75,6 +195,8 @@ version: '1.8' # With an item in your hand, use /brew ItemName to get its material for use in a recipe # (Gli id invece dei materiali sono "disapprovati" da Bukkit) # Una lista di materiali può essere trovata qui: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html +# Plugin items with 'plugin:id' (Currently supporting ExoticGarden, Slimefun, MMOItems, Brewery) +# Or a custom item defined above # cookingtime: Tempo in minuti richiesto dagli ingredienti per bollire # distillruns: Quanto spesso deve essere distillato per ottenere la versione perfetta con il volume alcolico impostato (0=non serve distillare). # distilltime: How long (in seconds) one distill-run takes (0=Default time of 40 sec) MC Default would be 20 sec @@ -86,6 +208,12 @@ version: '1.8' # Or RGB colors (hex: for example '99FF33') (with '') (search for "HTML color" on the internet) # difficoltà: Precisione richiesta per avere la migliore qualità da 1 a 10(1 = spreciso/più facile, 10 = molto preciso/più difficile) # alcohol: Volume alcolico da 0 a 100 nella versione perfetta (sarà aggiunta direttamente al giocatore, dove 100 è la quantità massima di alcohol assorbibile. +# lore: List of additional text on the finished brew. (Formatting codes possible: such as &6) +# Specific lore for quality possible, using + bad, ++ normal, +++ good, added to the front of the line. +# servercommands: List of Commands executed by the Server when drinking the brew +# playercommands: List of Commands executed by the Player when drinking the brew +# drinkmessage: Chat-message to the Player when drinking the Brew +# drinktitle: Title on Screen to the Player when drinking the Brew # effects: Eventuali effetti come quelli delle pozioni nel formato di effetto/livello/durata. # Lista di effetti possibili: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/potion/PotionEffectType.html # Intervalli di livelli o durate possono essere specificati con un "-", per esempio 'SPEED/1-2/30-40'. Ciò significa nel peggior caso livello 1 e 30 secondi di durata e livello 2 e 40 secondi nel migliore. @@ -93,145 +221,169 @@ version: '1.8' # La durata massima possibile è 1638 secondi. Gli effetti instantaner non hanno bisogno che la durata sia specificata. recipes: -# Ricetta di esempio con ogni opzione possibile: - 0: - name: Cattivo esempio/Esempio/Buon esempio - ingredients: - - Sugar_Cane/5 + # Ricetta di esempio con ogni opzione possibile: + 0: + name: Cattivo esempio/Esempio/Buon esempio + ingredients: - Diamond/1 - - Cocoa_Beans/20 - Spruce_Planks/8 - Bedrock/1 - cookingtime: 3 - distillruns: 2 - distilltime: 60 - wood: 4 - age: 11 - color: DARK_RED - difficulty: 3 - alcohol: 23 - effects: + - Brewery:Wheatbeer/2 + # - ExoticGarden:Grape/3 + - ex-item/4 + cookingtime: 3 + distillruns: 2 + distilltime: 60 + wood: 4 + age: 11 + color: DARK_RED + difficulty: 3 + alcohol: 23 + lore: + - This is an examble brew + - ++Just a normal Example + - This text would be on the brew + - + Smells disgusting + - ++ Smells alright + - +++ Smells really good + servercommands: + - weather clear + playercommands: + - homes + drinkmessage: Tastes good + drinktitle: Warms you from inside + effects: - FIRE_RESISTANCE/20 - HEAL/1 - WEAKNESS/2-3/50-60 - POISON/1-0/20-0 - 1: - name: Birra di frumento puzzolente/Birra di frumento/Birra di frumento pregiata - ingredients: + + 1: + name: Birra di frumento puzzolente/Birra di frumento/Birra di frumento pregiata + ingredients: - Wheat/3 - cookingtime: 8 - distillruns: 0 - wood: 1 - age: 2 - color: BRIGHT_GREY - difficulty: 1 - alcohol: 5 - 2: - name: Birra puzzolente/Birra/Birra pregiata - ingredients: + cookingtime: 8 + distillruns: 0 + wood: 1 + age: 2 + color: BRIGHT_GREY + difficulty: 1 + alcohol: 5 + + 2: + name: Birra puzzolente/Birra/Birra pregiata + ingredients: - Wheat/6 - cookingtime: 8 - distillruns: 0 - wood: 0 - age: 3 - color: ORANGE - difficulty: 1 - alcohol: 6 - 3: - name: Birra scura puzzolente/Birra scura/Birra scura pregiata - ingredients: + cookingtime: 8 + distillruns: 0 + wood: 0 + age: 3 + color: ORANGE + difficulty: 1 + alcohol: 6 + + 3: + name: Birra scura puzzolente/Birra scura/Birra scura pregiata + ingredients: - Wheat/6 - cookingtime: 8 - distillruns: 0 - wood: 4 - age: 8 - color: BLACK - difficulty: 2 - alcohol: 7 - 4: - name: Idromele scarso/Idromele/&6Idromele dorato - ingredients: + cookingtime: 8 + distillruns: 0 + wood: 4 + age: 8 + color: BLACK + difficulty: 2 + alcohol: 7 + + 4: + name: Idromele scarso/Idromele/&6Idromele dorato + ingredients: - Sugar_Cane/6 - cookingtime: 3 - distillruns: 0 - wood: 2 - age: 4 - color: ORANGE - difficulty: 2 - alcohol: 9 - 5: - name: Idromele di mele/Idromele di mele dolci/&6Idromele di mele dolci dorato - ingredients: + cookingtime: 3 + distillruns: 0 + wood: 2 + age: 4 + color: ORANGE + difficulty: 2 + alcohol: 9 + + 5: + name: Idromele di mele/Idromele di mele dolci/&6Idromele di mele dolci dorato + ingredients: - Sugar_Cane/6 - Apple/2 - cookingtime: 4 - distillruns: 0 - wood: 2 - age: 4 - color: ORANGE - difficulty: 4 - alcohol: 12 - effects: + cookingtime: 4 + distillruns: 0 + wood: 2 + age: 4 + color: ORANGE + difficulty: 4 + alcohol: 12 + effects: - WATER_BREATHING/1-2/150 - 6: - name: Rum amaro/Rum speziato/&6Rum dorato - ingredients: + + 6: + name: Rum amaro/Rum speziato/&6Rum dorato + ingredients: - Sugar_Cane/14 - cookingtime: 5 - distillruns: 2 - distilltime: 30 - wood: 2 - age: 14 - color: DARK_RED - difficulty: 6 - alcohol: 30 - effects: + cookingtime: 5 + distillruns: 2 + distilltime: 30 + wood: 2 + age: 14 + color: DARK_RED + difficulty: 6 + alcohol: 30 + effects: - FIRE_RESISTANCE/1/20-100 - POISON/1-0/30-0 - 7: - name: Vodka schifosa/Vodka/Vodka russa - ingredients: + + 7: + name: Vodka schifosa/Vodka/Vodka russa + ingredients: - Potato/10 - cookingtime: 15 - distillruns: 3 - age: 0 - color: BRIGHT_GREY - difficulty: 4 - alcohol: 20 - effects: + cookingtime: 15 + distillruns: 3 + age: 0 + color: BRIGHT_GREY + difficulty: 4 + alcohol: 20 + effects: - WEAKNESS/15 - POISON/10 - 8: - name: Assenzio scarso/Assenzio/Assenzio forte - ingredients: + + 8: + name: Assenzio scarso/Assenzio/Assenzio forte + ingredients: - Grass/15 - cookingtime: 3 - distillruns: 6 - distilltime: 80 - color: GREEN - difficulty: 8 - alcohol: 45 - effects: + cookingtime: 3 + distillruns: 6 + distilltime: 80 + color: GREEN + difficulty: 8 + alcohol: 45 + effects: - POISON/20-30 - 9: - name: Zuppa di patate - ingredients: + + 9: + name: Zuppa di patate + ingredients: - Potato/5 - Grass/3 - cookingtime: 3 - color: PINK - difficulty: 1 - effects: + cookingtime: 3 + color: PINK + difficulty: 1 + effects: - HEAL/0-1 - 10: - name: Caffè stantio/Caffè/Caffè forte - ingredients: + + 10: + name: Caffè stantio/Caffè/Caffè forte + ingredients: - Cocoa_Beans,3/12 - Milk_Bucket/2 - cookingtime: 2 - color: BLACK - difficulty: 3 - effects: + cookingtime: 2 + color: BLACK + difficulty: 3 + effects: - REGENERATION/1/2-5 - SPEED/1/30-140 @@ -241,27 +393,13 @@ recipes: -# cooked: OGNI possibile ingrediente e i nomi per la pozione originatasi dopo la fermentazione. -# (Esempio) MATERIALE: Nome dopo la cottura - -cooked: - Wheat: Frumento fermentato - Sugar_Cane: Miscela zuccherata - Apple: Sidro di mele - Potato: Purè di patate - Grass: Erbe bollite - Red_Mushroom: Miscela ai funghi - Cocoa_Beans: Miscela colorata - Milk_Bucket: Acqua lattea - - - # -- Compatibilità con altri plugin -- # Abilita il controllo della presenza di altri plugin per i permessi relativi ai barili[true] useWorldGuard: true useLWC: true useGriefPrevention: true +useGMInventories: true # Abilita il logging degli inventari dei barili [true] useLogBlock: true diff --git a/resources/config/v13/zh/config.yml b/resources/config/v13/zh/config.yml index ad157f0..3703208 100644 --- a/resources/config/v13/zh/config.yml +++ b/resources/config/v13/zh/config.yml @@ -52,12 +52,26 @@ hangoverDays: 7 colorInBarrels: true colorInBrewer: true +# Always show the 1-5 stars on the item depending on the quality. If false, they will only appear when brewing [true] +alwaysShowQuality: true + +# Always show the alcohol content on the item. If false, it will only show in the brewing stand [false] +alwaysShowAlc: false + # 大型熟成木桶可以通过右击桶身任意一个位置来打开, 而不需要专门右击其龙头. 这一操作对小型酒桶总为真.[true] openLargeBarrelEverywhere: false # MC自带的桶内可以存放多少饮品 [6] maxBrewsInMCBarrels: 6 +# The used Ingredients and other brewing-data is saved to all Brewery Items. To prevent +# hacked clients from reading what exactly was used to brew an item, the data can be encoded/scrambled. +# This is a fast process to stop players from hacking out recipes, once they get hold of a brew. +# Only drawback: brew items can only be used on another server with the same encodeKey. +# So enable this if you want to make recipe cheating harder, but don't share any brews by world download, schematics, or other means. [false] +enableEncode: false +encodeKey: 0 + # 是否检查更新.[true] # 若有更新, 服务端后台与上线时的管理员会收到通知. updateCheck: true @@ -65,8 +79,117 @@ updateCheck: true # 自动保存时间间隔, 单位:分钟.[3] autosave: 3 +# Show debug messages in log [false] +debug: false + # 配置文件版本 -version: '1.8' +version: '2.0' + + + +# -- Define custom items -- +# The defined id can then be used in recipes + +# matchAny: true if it is already enough if one of the info matches +# material: Which type the item has to be +# name: Which name the item has to be (Formatting codes possible: such as &6) +# lore: What has to be in the lore of the item + +customItems: + ex-item: + # A Barrier item called Wall and has the given line in its lore + material: Barrier + name: 'Wall' + lore: + - '&7Very well protected' + + ex-item2: + # Using matchAny only one of the following has to match. + # In this case on of the door types, or an item called Beechwood Door, or an item with 'A door' in its lore + matchAny: true + material: + - Acacia_Door + - Oak_Door + - Spruce_Door + name: + - 'Beechwood Door' + lore: + - 'A door' + + rasp: + name: '&cRaspberry' + + +# -- Ingredients in the Cauldron -- +# Which Ingredients are accepted by the Cauldron and the base potion resulting from them + + # name: Name of the base potion coming out of the Cauldron (Formatting codes possible: such as &6) + # ingredients: List of 'material/amount' + # With an item in your hand, use /brew ItemName to get its material for use in a recipe + # (Item-ids instead of material are not supported by bukkit anymore and will not work) + # A list of materials can be found here: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html + # color: Color of the potion from a cauldron. + # Usable Colors: DARK_RED, RED, BRIGHT_RED, ORANGE, PINK, BLUE, CYAN, WATER, GREEN, BLACK, GREY, BRIGHT_GREY + # Or RGB colors (hex: for example '99FF33') (with '') (search for "HTML color" on the internet) + # lore: List of additional text on the base potion. (Formatting codes possible: such as &6) + +cauldron: + ex: + name: Example + ingredients: + - Bedrock/2 + - Diamond + color: BLACK + lore: + - An example for a Base Potion + - This is how it comes out of a Cauldron + + # -- One Ingredient: -- + wheat: + name: 发酵麦汁 + ingredients: Wheat + + sugarcane: + name: 糖浆 + ingredients: Sugar_Cane + color: 'f1ffad' # yellowish green + + apple: + name: 苹果汁 + ingredients: Apple + + potato: + name: 土豆泥 + ingredients: Potato + + grass: + name: 蒸煮过的药草 + ingredients: Grass + color: '99ff66' # bright green + + rmushroom: + name: 蘑菇酿 + ingredients: Red_Mushroom + color: 'ff5c33' # amber red + + cocoa: + name: 上了色的水 + ingredients: Cocoa_Beans + color: '804600' # mocca + + milk: + name: 乳浊液体 + ingredients: Milk_Bucket + color: BRIGHT_GREY + + # -- Multiple Ingredients: -- + apfelmet_base: + name: Apple-Sugar brew + ingredients: + - Sugar_Cane/3 + - Apple + color: 'e1ff4d' # greenish yellow + # -- 饮品配方列表 -- @@ -76,6 +199,8 @@ version: '1.8' # 手中持有物品的时候, 可以使用/brew ItemName来获取其物品id, 便于增改配方 # (请勿使用物品ID, 物品ID已被弃用, 使用材料名称是唯一支持的做法.) # 材料名称列表详见此处: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html (可能需要挂梯子进行浏览) +# Plugin items with 'plugin:id' (Currently supporting ExoticGarden, Slimefun, MMOItems, Brewery) +# Or a custom item defined above # cookingtime: 原料需要在炼药锅内烹制发酵的时间, 单位为现实分钟. # distillruns: 饮品需要在酿造台上蒸馏的次数(0=无需蒸馏). # distilltime: 每次蒸馏所需要耗费的时间(0=默认值, 即40秒), MC默认则为20秒/ @@ -88,6 +213,12 @@ version: '1.8' # 也可以使用RGB颜色(十六进制, 如'99FF33', 必须包含两个单引号)(在线搜索"HTML 颜色"或者其他颜色库即刻获得你需要的颜色的十六进制表示) # difficulty: 酿制难度, 1 = 不需要非常精确的操作就可以制出优质饮品, 10 = 需要非常精确的操作才可以制出优质饮品. # alcohol: 完美质量的饮品所含有的酒精度(百分数, 会被直接添加到玩家身上, 100意为直接昏迷) +# lore: List of additional text on the finished brew. (Formatting codes possible: such as &6) +# Specific lore for quality possible, using + bad, ++ normal, +++ good, added to the front of the line. +# servercommands: List of Commands executed by the Server when drinking the brew +# playercommands: List of Commands executed by the Player when drinking the brew +# drinkmessage: Chat-message to the Player when drinking the Brew +# drinktitle: Title on Screen to the Player when drinking the Brew # effects: 饮用后能够获得的药效的列表. 格式为"药效/强度/时长(秒)". # 药效列表: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/potion/PotionEffectType.html # 药效强度与时长可以用"-"划分区间, 如: "SPEED/1-2/30-40" = 最劣质情况下的饮品会给予速度1三十秒, 最优质则会给予速度2四十秒. @@ -95,145 +226,170 @@ version: '1.8' # 时长极限为1638秒. 药效强度极限为255级. 瞬间型药效无需附加时长. recipes: -# 例: - 0: - name: 劣质的 范例饮品/范例饮品/优质的 范例饮品 - ingredients: - - Sugar_Cane/5 + # 例: + + 0: + name: 劣质的 范例饮品/范例饮品/优质的 范例饮品 + ingredients: - Diamond/1 - - Cocoa_Beans/20 - Spruce_Planks/8 - Bedrock/1 - cookingtime: 3 - distillruns: 2 - distilltime: 60 - wood: 4 - age: 11 - color: DARK_RED - difficulty: 3 - alcohol: 23 - effects: + - Brewery:Wheatbeer/2 +# - ExoticGarden:Grape/3 + - ex-item/4 + cookingtime: 3 + distillruns: 2 + distilltime: 60 + wood: 4 + age: 11 + color: DARK_RED + difficulty: 3 + alcohol: 23 + lore: + - This is an examble brew + - ++Just a normal Example + - This text would be on the brew + - + Smells disgusting + - ++ Smells alright + - +++ Smells really good + servercommands: + - weather clear + playercommands: + - homes + drinkmessage: Tastes good + drinktitle: Warms you from inside + effects: - FIRE_RESISTANCE/20 - HEAL/1 - WEAKNESS/2-3/50-60 - POISON/1-0/20-0 - 1: - name: 劣质麦啤/麦啤/优质麦啤 - ingredients: + + 1: + name: 劣质麦啤/麦啤/优质麦啤 + ingredients: - Wheat/3 - cookingtime: 8 - distillruns: 0 - wood: 1 - age: 2 - color: BRIGHT_GREY - difficulty: 1 - alcohol: 5 - 2: - name: 劣质啤酒/啤酒/鲜啤 - ingredients: + cookingtime: 8 + distillruns: 0 + wood: 1 + age: 2 + color: BRIGHT_GREY + difficulty: 1 + alcohol: 5 + + 2: + name: 劣质啤酒/啤酒/鲜啤 + ingredients: - Wheat/6 - cookingtime: 8 - distillruns: 0 - wood: 0 - age: 3 - color: ORANGE - difficulty: 1 - alcohol: 6 - 3: - name: 劣质黑啤/黑啤/精制黑啤 - ingredients: + cookingtime: 8 + distillruns: 0 + wood: 0 + age: 3 + color: ORANGE + difficulty: 1 + alcohol: 6 + + 3: + name: 劣质黑啤/黑啤/精制黑啤 + ingredients: - Wheat/6 - cookingtime: 8 - distillruns: 0 - wood: 4 - age: 8 - color: BLACK - difficulty: 2 - alcohol: 7 - 4: - name: 粗制蜜酒/蜜酒/黄金蜜酒 - ingredients: + cookingtime: 8 + distillruns: 0 + wood: 4 + age: 8 + color: BLACK + difficulty: 2 + alcohol: 7 + + 4: + name: 粗制蜜酒/蜜酒/黄金蜜酒 + ingredients: - Sugar_Cane/6 - cookingtime: 3 - distillruns: 0 - wood: 2 - age: 4 - color: ORANGE - difficulty: 2 - alcohol: 9 - 5: - name: 苹果蜜酒/甜苹果蜜酒/甘醇黄金苹果蜜酒 - ingredients: + cookingtime: 3 + distillruns: 0 + wood: 2 + age: 4 + color: ORANGE + difficulty: 2 + alcohol: 9 + + 5: + name: 苹果蜜酒/甜苹果蜜酒/甘醇黄金苹果蜜酒 + ingredients: - Sugar_Cane/6 - Apple/2 - cookingtime: 4 - distillruns: 0 - wood: 2 - age: 4 - color: ORANGE - difficulty: 4 - alcohol: 12 - effects: + cookingtime: 4 + distillruns: 0 + wood: 2 + age: 4 + color: ORANGE + difficulty: 4 + alcohol: 12 + effects: - WATER_BREATHING/1-2/150 - 6: - name: 苦涩的朗姆/辛辣的朗姆/金品朗姆 - ingredients: + + 6: + name: 苦涩的朗姆/辛辣的朗姆/金品朗姆 + ingredients: - Sugar_Cane/14 - cookingtime: 5 - distillruns: 2 - distilltime: 30 - wood: 2 - age: 14 - color: DARK_RED - difficulty: 6 - alcohol: 30 - effects: + cookingtime: 5 + distillruns: 2 + distilltime: 30 + wood: 2 + age: 14 + color: DARK_RED + difficulty: 6 + alcohol: 30 + effects: - FIRE_RESISTANCE/1/20-100 - POISON/1-0/30-0 - 7: - name: 劣质伏特加/伏特加/纯正俄式风味伏特加 - ingredients: + + 7: + name: 劣质伏特加/伏特加/纯正俄式风味伏特加 + ingredients: - Potato/10 - cookingtime: 15 - distillruns: 3 - age: 0 - color: BRIGHT_GREY - difficulty: 4 - alcohol: 20 - effects: + cookingtime: 15 + distillruns: 3 + age: 0 + color: BRIGHT_GREY + difficulty: 4 + alcohol: 20 + effects: - WEAKNESS/15 - POISON/10 - 8: - name: 劣质苦艾酒/苦艾酒/劲猛苦艾酒 - ingredients: + + 8: + name: 劣质苦艾酒/苦艾酒/劲猛苦艾酒 + ingredients: - Grass/15 - cookingtime: 3 - distillruns: 6 - distilltime: 80 - color: GREEN - difficulty: 8 - alcohol: 45 - effects: + cookingtime: 3 + distillruns: 6 + distilltime: 80 + color: GREEN + difficulty: 8 + alcohol: 45 + effects: - POISON/20-30 - 9: - name: 土豆汤 - ingredients: + + 9: + name: 土豆汤 + ingredients: - Potato/5 - Grass/3 - cookingtime: 3 - color: PINK - difficulty: 1 - effects: + cookingtime: 3 + color: PINK + difficulty: 1 + effects: - HEAL/0-1 - 10: - name: 平淡的咖啡/咖啡/浓苦黑咖 - ingredients: + + 10: + name: 平淡的咖啡/咖啡/浓苦黑咖 + ingredients: - Cocoa_Beans/12 - Milk_Bucket/2 - cookingtime: 2 - color: BLACK - difficulty: 3 - effects: + cookingtime: 2 + color: BLACK + difficulty: 3 + effects: - REGENERATION/1/2-5 - SPEED/1/30-140 @@ -242,30 +398,13 @@ recipes: # 插件的本意是让玩家自行实验饮品制造配方, 所以未来的新配方由服管决定是否加入. - - -# cooked: 炼药锅中烹制发酵过后的液体名称, 由一个饮品中的第一个原料决定. -# [例] 原料名称: 烹制后的名称 - -cooked: - Wheat: 发酵麦汁 - Sugar_Cane: 糖浆 - Apple: 苹果汁 - Potato: 土豆泥 - Grass: 蒸煮过的药草 - Red_Mushroom: 蘑菇酿 - Cocoa_Beans: 上了色的水 - Milk_Bucket: 乳浊液体 - - - - # -- 插件兼容性 -- # 检查插件以进行权限配置[true] useWorldGuard: true useLWC: true useGriefPrevention: true +useGMInventories: true # 对LogBlock启用方块数据录入[true] useLogBlock: true diff --git a/resources/languages/de.yml b/resources/languages/de.yml index 991e8d3..b4f009a 100644 --- a/resources/languages/de.yml +++ b/resources/languages/de.yml @@ -1,19 +1,20 @@ # Brew Brew_-times: -fach -Brew_BadPotion: Verdorbenes Getränk +Brew_BadPotion: Verdorbenes Gebräu Brew_BarrelRiped: Fassgereift -Brew_DistillUndefined: Undefinierbares Destillat +Brew_DistillUndefined: Graues Destillat Brew_Distilled: Destilliert Brew_HundredsOfYears: Hunderte Jahre Brew_Ingredients: Zutaten Brew_MinutePluralPostfix: n Brew_OneYear: Ein Jahr Brew_ThickBrew: Schlammiger Sud -Brew_Undefined: Undefinierbarer Sud +Brew_Undefined: Kesselsud Brew_Woodtype: Holzart Brew_Years: Jahre Brew_fermented: gegärt Brew_minute: minute +Brew_Alc: Alc &v1ml # CMD CMD_Copy_Error: '&6&v1 &cTränke haben nicht mehr in das Inventar gepasst' @@ -22,34 +23,24 @@ CMD_Info_NotDrunk: '&v1 ist nicht betrunken' CMD_Player: '&a&v1 ist nun &6&v2% &abetrunken, mit einer Qualität von &6&v3' CMD_Player_Error: '&cDie Qualität muss zwischen 1 und 10 liegen!' CMD_Reload: '&aConfig wurde neu eingelesen' +CMD_Created: '&aTrank erstellt' CMD_Configname: '&aName für Config ist: &f&v1' CMD_Configname_Error: '&cDu hast kein Item in der Hand' CMD_UnLabel: '&aDas Label wurde entfernt' -CMD_Persistent: '&aTrankdaten sind nun permanent und unveränderlich und so kann der Trank nun wie jedes andere Item kopiert werden' -CMD_PersistRemove: '&cPermanente Tränke können nicht aus der Datenbank gelöscht werden, evtl. Kopien würden sonst unbrauchbar werden!' -CMD_UnPersist: '&aTrankdaten nicht mehr permanent und unveränderlich. &ePotentielle Kopien dieses Trankes, die nicht mit "/brew copy" gemacht wurden, könnten nun unbrauchbar werden!' -CMD_CopyNotPersistent: '&eDiese Kopien dieses Trankes werden nicht die permanenten und unveränderlichen Trankdaten haben!' CMD_Static: '&aTrank ist nun unveränderlich und kann nicht weiter gereift oder destilliert werden.' CMD_NonStatic: '&eTrank ist wieder veränderlich und kann normal gereift oder destilliert werden' # Error +Error_ConfigUpdate: 'Unbekannte Brewery Config Version: v&v1, Config wurde nicht geupdated!' Error_ItemNotPotion: '&cDas Item in deiner Hand konnte nicht als Trank identifiziert werden' +Error_NoBarrelAccess: '&cDu hast keine Rechte dieses Fass zu öffnen!' Error_NoBrewName: '&cKein Rezept mit Namen: "&v1&c" gefunden!' +Error_NoPermissions: '&cDu hast keine Rechte dies zu tun!' Error_PlayerCommand: '&cDieser Befehl kann nur als Spieler ausgeführt werden' Error_Recipeload: '&cEs konnten nicht alle Rezepte wiederhergesellt werden: Siehe Serverlog!' Error_ShowHelp: 'Benutze &6/brew help &fum die Hilfe anzuzeigen' Error_UnknownCommand: Unbekannter Befehl -Error_ConfigUpdate: 'Unbekannte Brewery Config Version: v&v1, Config wurde nicht geupdated!' -Error_PersistStatic: '&cTränke mit permanenten Trankdaten sind immer unveränderlich!' - -# Permission -Error_NoPermissions: '&cDu hast keine Rechte dies zu tun!' -Error_NoBarrelAccess: '&cDu hast keine Rechte dieses Fass zu öffnen!' -Perms_NoBarrelCreate: '&cDu hast keine Rechte Fässer zu erstellen!' -Perms_NoSmallBarrelCreate: '&cDu kast keine Rechte kleine Fässer zu erstellen!' -Perms_NoBigBarrelCreate: '&cDu kast keine Rechte große Fässer zu erstellen!' -Perms_NoCauldronInsert: '&cDu hast keine Rechte Zutaten in Bottiche zu tun!' -Perms_NoCauldronFill: '&cDu hast keine Rechte Flaschen von diesem Bottich abzufüllen!' +Error_YmlRead: 'config.yml konnte nicht gelesen werden, ist die Datei im korrekten yml-Format (korrekte Leerzeichen usw.)?' # Etc Etc_Page: Seite @@ -72,10 +63,16 @@ Help_WakeupCheck: '&6/brew Wakeup Check &9Teleportiert zu allen Aufwachpunkten' Help_WakeupCheckSpecific: '&6/brew Wakeup Check &9Teleportiert zu einem Aufwachpunkt' Help_WakeupList: '&6/brew Wakeup List [Welt]&9 Listet die Aufwachpunkte einer Welt auf' Help_WakeupRemove: '&6/brew Wakeup Remove &9Entfernt einen Aufwachpunkt' -Help_Persist: '&6/brew Persist &9Trankdaten permanent machen -> Kopierbar durch andere Plugins' Help_Static: '&6/brew Static &9Trank unveränderlich machen -> Kein weiteres reifen oder destillieren möglich' Help_Create: '&6/brew Create [Qualität] [Spieler] &9Erstellt einen Trank mit optionaler Qualität (1-10)' +# Permission +Perms_NoBarrelCreate: '&cDu hast keine Rechte Fässer zu erstellen!' +Perms_NoSmallBarrelCreate: '&cDu kast keine Rechte kleine Fässer zu erstellen!' +Perms_NoBigBarrelCreate: '&cDu kast keine Rechte große Fässer zu erstellen!' +Perms_NoCauldronInsert: '&cDu hast keine Rechte Zutaten in Bottiche zu tun!' +Perms_NoCauldronFill: '&cDu hast keine Rechte Flaschen von diesem Bottich abzufüllen!' + # Player Player_BarrelCreated: Fass erfolgreich erstellt Player_BarrelFull: '&cHier passen nicht mehr Getränke hinein' diff --git a/resources/languages/en.yml b/resources/languages/en.yml index 84db0f4..b328e82 100644 --- a/resources/languages/en.yml +++ b/resources/languages/en.yml @@ -2,35 +2,33 @@ Brew_-times: -times Brew_BadPotion: Ruined Potion Brew_BarrelRiped: Barrel aged -Brew_DistillUndefined: Indefinable Distillate +Brew_DistillUndefined: Murky Distillate Brew_Distilled: Distilled Brew_HundredsOfYears: Hundreds of Years Brew_Ingredients: Ingredients Brew_MinutePluralPostfix: s Brew_OneYear: One Year Brew_ThickBrew: Muddy Brew -Brew_Undefined: Indefinable Brew +Brew_Undefined: Cauldron Brew Brew_Woodtype: Woodtype Brew_Years: Years Brew_fermented: fermented Brew_minute: minute +Brew_Alc: Alc &v1ml # CMD -CMD_CopyNotPersistent: '&eThese copies of this Brew will not be persistent or static!' CMD_Copy_Error: '&6&v1 &cPotions did not fit into your inventory' CMD_Info_Drunk: '&v1 is &6&v2% &fdrunk, with a quality of &6&v3' CMD_Info_NotDrunk: '&v1 is not drunk' CMD_NonStatic: '&ePotion is not static anymore and will normally age in barrels.' -CMD_PersistRemove: '&cPersistent Brews cannot be removed from the Database. It would render any copies of them useless!' -CMD_Persistent: '&aPotion is now Persistent and Static and may now be copied like any other item. You can remove the persistence with the same command.' CMD_Player: '&a&v1 is now &6&v2% &adrunk, with a quality of &6&v3' CMD_Player_Error: '&cThe quality has to be between 1 and 10!' CMD_Reload: '&aConfig was successfully reloaded' +CMD_Created: '&aBrew Created' CMD_Configname: '&aName for the Config is: &f&v1' CMD_Configname_Error: '&cCould not find item in your hand' CMD_Static: '&aPotion is now static and will not change in barrels or brewing stands.' CMD_UnLabel: '&aLabel removed!' -CMD_UnPersist: '&aPersistence and static Removed. &eEvery Potential copy NOT made with ''/brew copy'' could become useless now!' # Error Error_ConfigUpdate: 'Unknown Brewery config version: v&v1, config was not updated!' @@ -38,11 +36,11 @@ Error_ItemNotPotion: '&cThe item in your hand could not be identified as a potio Error_NoBarrelAccess: '&cYou don''t have permissions to access this barrel!' Error_NoBrewName: '&cNo Recipe with Name: ''&v1&c'' found!' Error_NoPermissions: '&cYou don''t have permissions to do this!' -Error_PersistStatic: '&cPersistent potions are always static!' Error_PlayerCommand: '&cThis command can only be executed as a player!' Error_Recipeload: '&cNot all recipes could be restored: More information in the server log!' Error_ShowHelp: Use &6/brew help &fto display the help Error_UnknownCommand: Unknown Command +Error_YmlRead: 'Could not read file config.yml, please make sure the file is in valid yml format (correct spaces etc.)' # Etc Etc_Barrel: Barrel @@ -56,7 +54,6 @@ Help_Delete: '&6/brew delete &9Deletes the potion in your hand' Help_Help: '&6/brew help [Page] &9Shows a specific help-page' Help_Info: '&6/brew info&9 Displays your current Drunkeness and Quality' Help_InfoOther: '&6/brew info [Player]&9 Displays the current Drunkeness and Quality of [Player]' -Help_Persist: '&6/brew persist &9Make Brew persistent -> copyable by any plugin and technique' Help_Player: '&6/brew <%Drunkeness> [Quality]&9 Sets Drunkeness (and Quality) of a Player' Help_Reload: '&6/brew reload &9Reload config' Help_Configname: '&6/brew ItemName &9Display name of item in hand for the config' diff --git a/resources/languages/fr.yml b/resources/languages/fr.yml index 22fe764..8a5ecb1 100644 --- a/resources/languages/fr.yml +++ b/resources/languages/fr.yml @@ -5,7 +5,7 @@ Brew_BarrelRiped: Baril âgé Brew_DistillUndefined: Distillation indéfinie Brew_Distilled: Distillé Brew_HundredsOfYears: Centaines d´années -Brew_Ingredients: Ingredients +Brew_Ingredients: Ingrédients Brew_MinutePluralPostfix: s Brew_OneYear: Un an Brew_ThickBrew: Breuvage boueux @@ -14,23 +14,21 @@ Brew_Woodtype: Type de bois Brew_Years: Années Brew_fermented: fermenté Brew_minute: minute +Brew_Alc: Alc &v1ml # CMD -CMD_CopyNotPersistent: '&eLes copies de cette boisson ne seront pas persistantes ou statiques!' CMD_Copy_Error: '&6&v1 &cCes potions ne rentrent pas dans votre inventaire.' CMD_Info_Drunk: '&v1 est &6&v2% &fivre, avec une qualité de &6&v3' CMD_Info_NotDrunk: '&v1 nest pas ivre' CMD_NonStatic: '&eLa boisson n´est plus statique et vieillira normalement dans les barils.' -CMD_PersistRemove: '&cLes boissons persisitantes ne peuvent pas être supprimées de la base de donnée. Cela rendrait toute copie inutile!' -CMD_Persistent: '&aLa boisson est maintenant Persistante et Statique et peut être copiée comme n´importe quel autre objet. Vous pouvez enlever la persistance avec la même commande.' CMD_Player: '&a&v1 est maintenant &6&v2% &aivre, avec une qualité de &6&v3' CMD_Player_Error: '&cLa qualité doit être comprise entre 1 et 10 !' CMD_Reload: '&aLa config a été reload avec succès.' +CMD_Created: '&aBrew créé' CMD_Configname: '&aName for the Config is: &f&v1' -CMD_Configname_Error: '&cCould not find item in your hand' +CMD_Configname_Error: '&cN´a pas pu trouver d´objet dans votre main' CMD_Static: '&aLa boisson est maintenant statique et ne changera pas dans les barils ou les stands d´alchimie.' CMD_UnLabel: '&aLabel supprimé!' -CMD_UnPersist: '&aPersistance et staticité supprimées. &eChaque copie potentionelle NON crée avec /brew copy deviendra inutile maintenant!' # Erreurs Error_ConfigUpdate: 'La version de la configuration de Brewery est inconnue: v&v1, la config n´a pas été mise à jour !' @@ -38,11 +36,11 @@ Error_ItemNotPotion: '&cL´item dans votre main n´a pas pu être identifié com Error_NoBarrelAccess: '&cVous n´avez pas la permission d´acceder à ce baril !' Error_NoBrewName: '&cAucune recette avec le nom: &v1&c n´a été trouvée!' Error_NoPermissions: '&cVous ne pouvez pas faire ça !' -Error_PersistStatic: '&cLes boissons persistantes sont toujours statiques!' Error_PlayerCommand: '&cCette commande ne peut être executée que par un joueur !' Error_Recipeload: '&cToutes les recettes n´ont pu être restaurées: Plus d´informations dans les logs du serveur !' Error_ShowHelp: Utilisez &6/brew help &fpour regarder l´aide Error_UnknownCommand: Commande inconnue +Error_YmlRead: 'Could not read file config.yml, please make sure the file is in valid yml format (correct spaces etc.)' # Etc Etc_Barrel: Baril @@ -56,7 +54,6 @@ Help_Delete: '&6/brew delete &9Supprime la potion qui est dans votre main' Help_Help: '&6/brew help [Page] &9Affiche une page spécifique de l´aide' Help_Info: '&6/brew info&9 Affiche votre ivresse actuelle ainsi que sa qualité' Help_InfoOther: '&6/brew info [Player]&9 Affiche l´ivresse actuelle de , cette commande affiche aussi sa qualité.' -Help_Persist: '&6/brew persist &9Crée une boisson persistante -> peut être copiée par n´importe quel plugin et technique' Help_Player: '&6/brew <%Drunkeness> [Quality]&9 Définit l´ivresse (et la qualité) du joueur' Help_Reload: '&6/brew reload &9Reload la config' Help_Configname: '&6/brew ItemName &9Display name of item in hand for the config' diff --git a/resources/languages/it.yml b/resources/languages/it.yml index 952fe33..524571b 100755 --- a/resources/languages/it.yml +++ b/resources/languages/it.yml @@ -14,23 +14,21 @@ Brew_Woodtype: Tipo di legno Brew_Years: Anni Brew_fermented: fermentata Brew_minute: minuto +Brew_Alc: Alc &v1ml # Comandi -CMD_CopyNotPersistent: '&eLe copie di questa miscela non saranno né persistenti né statiche!' CMD_Copy_Error: '&6&v1 &cLe copie di questa pozione non stavano nell''inventario.' CMD_Info_Drunk: '&v1 è &6&v2% &fsbronzo, con una qualità di &6&v3' CMD_Info_NotDrunk: '&v1 non è sbronzo' CMD_NonStatic: '&eLa pozione non è più statica ed invecchierà normalmente nei barili' -CMD_PersistRemove: '&cLe miscele persistenti non possono essere rimosse dal Database. Renderebbe le loro copie inutili!' -CMD_Persistent: '&aLa pozione è ora persistente e statica e può essere copiata come qualunque altro oggetto. Puoi rimuovere la persistenza con lo stesso comando.' CMD_Player: '&a&v1 è ora &6&v2% &asbronzo, con una qualità di &6&v3' CMD_Player_Error: '&cLa qualità deve essere fra 1 e 10!' CMD_Reload: '&aLa configurazione è stata ricaricata con successo' +CMD_Created: '&aBrew Created' CMD_Configname: '&aName for the Config is: &f&v1' CMD_Configname_Error: '&cCould not find item in your hand' CMD_Static: '&aLa pozione è ora statica e non cambierà né nei barili né negli alambicchi.' CMD_UnLabel: '&aEtichetta rimossa!' -CMD_UnPersist: '&aPersistenza e staticità rimosse. &eOgni copia non fatta con ''/brew copy'' potrebbe diventare inutile ora!' # Errori Error_ConfigUpdate: 'Versione della configurazione di Brewery sconosciuta: v&v1, la configurazione non è stato aggiornata!' @@ -38,11 +36,11 @@ Error_ItemNotPotion: '&cL''oggetto nella tua mano non è una pozione!' Error_NoBarrelAccess: '&cNon hai il permesso di aprire questo barile!' Error_NoBrewName: '&cNon è stata trovata nessuna ricetta chiamata ''&v1&c''!' Error_NoPermissions: '&cNon hai il permesso di farlo!' -Error_PersistStatic: '&cLe pozioni persistenti sono sempre statiche!' Error_PlayerCommand: '&cQuesto comando può essere eseguito solo da un giocatore!' Error_Recipeload: '&cNon è stato possibile recuperare tutte le ricette: ulteriori informazioni nel file log!' Error_ShowHelp: Usa &6/brew help &fper visualizzare l''aiuto Error_UnknownCommand: Comando sconosciuto +Error_YmlRead: 'Could not read file config.yml, please make sure the file is in valid yml format (correct spaces etc.)' # Varie Etc_Barrel: Barile @@ -56,7 +54,6 @@ Help_Delete: '&6/brew delete &9Elimina la pozione nella tua mano' Help_Help: '&6/brew help &9Mostra una specifica pagina dell''aiuto' Help_Info: '&6/brew info&9 Mostra il tuo livello di sbronza attuale e la qualità' Help_InfoOther: '&6/brew info [Giocatore]&9 Mostra l''attuale livello di sbronza e la qualità di [Giocatore]' -Help_Persist: '&6/brew persist &9Rendi una miscela persistente quindi copiabile normalmente in ogni modo e da ogni plugin.' Help_Player: '&6/brew <%Sbronza> [Qualità]&9 Imposta livello di sbronza (e qualità) di un giocatore.' Help_Reload: '&6/brew reload &9Ricarica la configurazione' Help_Configname: '&6/brew ItemName &9Display name of item in hand for the config' diff --git a/resources/languages/tw.yml b/resources/languages/tw.yml index 7dfd0f7..39c5298 100644 --- a/resources/languages/tw.yml +++ b/resources/languages/tw.yml @@ -14,23 +14,21 @@ Brew_Woodtype: 木材質 Brew_Years: 年 Brew_fermented: 發酵 Brew_minute: 分鐘 +Brew_Alc: Alc &v1ml # CMD -CMD_CopyNotPersistent: '&e這個釀造的副本不會持久或靜態!' CMD_Copy_Error: '&6&v1 &c藥水不適合你的庫存' CMD_Info_Drunk: '&v1 是 &6&v2% &f醉酒,酒的品質為 &6&v3' CMD_Info_NotDrunk: '&v1 沒有醉酒' CMD_NonStatic: '&e藥水不再是靜止的,會在桶木中老化.' -CMD_PersistRemove: '&c無法從數據庫中刪除持久性釀造.它會使任何副本無用!' -CMD_Persistent: '&a藥水現在是持久性和靜態,現在可以像任何其他項一樣被複製.您可以使用相同的命令刪除持久性.' CMD_Player: '&a&v1 現在是 &6&v2% &a醉酒,酒的品質為 &6&v3' CMD_Player_Error: '&c酒的品質必須在1到10之間!' CMD_Reload: '&a設定檔已成功重新加載' +CMD_Created: '&aBrew Created' CMD_Configname: '&aName for the Config is: &f&v1' CMD_Configname_Error: '&cCould not find item in your hand' CMD_Static: '&a藥水現在是靜止的,不會在桶或釀造台上變化.' CMD_UnLabel: '&a標籤已刪除!' -CMD_UnPersist: '&a持久性和靜態刪除.&e每個隱藏的副本都不用''/brew copy'',現在可能變得無用了!' # Error Error_ConfigUpdate: '未知 釀酒 設定檔版本: v&v1,設定檔沒有更新!' @@ -38,11 +36,11 @@ Error_ItemNotPotion: '&c你手上的物品不能確定為藥水' Error_NoBarrelAccess: '&c你沒有權限存取!' Error_NoBrewName: '&c沒有這個配方: ''&v1&c'' 找到!' Error_NoPermissions: '&c你沒有權限這樣做!' -Error_PersistStatic: '&c持久藥水總是靜止的!' Error_PlayerCommand: '&c這個指令只能由玩家執行!' Error_Recipeload: '&c並非所有配方都可以有用:服務器日誌中的更多信息!' Error_ShowHelp: 使用 &6/brew help &f顯示幫助 Error_UnknownCommand: 未知的指令 +Error_YmlRead: 'Could not read file config.yml, please make sure the file is in valid yml format (correct spaces etc.)' # Etc Etc_Barrel: 釀造桶 @@ -56,7 +54,6 @@ Help_Delete: '&6/brew delete &9刪除你手中的藥水' Help_Help: '&6/brew help [頁數] &9顯示幫助的頁面' Help_Info: '&6/brew info&9 顯示您當前的醉酒和酒的品質' Help_InfoOther: '&6/brew info [玩家]&9 顯示 [玩家] 當前醉酒程度和酒的品質程度' -Help_Persist: '&6/brew persist &9使釀造持久化 - >可通過任何插件和技術進行複制' Help_Player: '&6/brew <玩家> <%醉酒> [數量]&9 設置玩家的醉酒度(和酒的品質)' Help_Reload: '&6/brew reload &9重新加載設定檔' Help_Configname: '&6/brew ItemName &9Display name of item in hand for the config' diff --git a/resources/languages/zh.yml b/resources/languages/zh.yml index 41b8fdb..c64a3cb 100644 --- a/resources/languages/zh.yml +++ b/resources/languages/zh.yml @@ -14,23 +14,21 @@ Brew_Woodtype: 木头种类 Brew_Years: 年 Brew_fermented: 炖煮发酵 Brew_minute: 分钟 +Brew_Alc: Alc &v1ml # CMD -CMD_CopyNotPersistent: '&e复制饮品的数据并未静滞或固定! 请多加注意!' CMD_Copy_Error: '&6&v1&r, &c你的背包塞不下了.' CMD_Info_Drunk: '&v1醉酒程度为&6&v2%&f, 醉酒质量为&6&v3.' CMD_Info_NotDrunk: '&v1没醉.' CMD_NonStatic: '&e饮品已被取消静滞, 现在可以正常地进行蒸馏或进行木桶熟成了.' -CMD_PersistRemove: '&c当前饮品的数据已被固定, 无法从数据库中移除, 否则将使得任何该饮品的复制品无效!' -CMD_Persistent: '&a当前饮品的数据已被固定. 现在你可以正常地对该饮品进行复制. 再执行一次该命令将会撤销固定.' CMD_Player: '&a&v1醉酒程度为&6&v2%&f, 醉酒质量为&6&v3.' CMD_Player_Error: '&c醉酒质量必须在1到10之间.' CMD_Reload: '&a配置文件重载成功.' +CMD_Created: '&aBrew Created' CMD_Configname: '配置文件中&aName的名字是: &f&v1' CMD_Configname_Error: '&c无法找到物品名称' CMD_Static: '&a饮品已被静滞, 现在其无法被蒸馏或进行木桶熟成.' CMD_UnLabel: '&a标签已被去除!' -CMD_UnPersist: '&a当前饮品的数据已被撤销固定, 通过非指令方式(/brew copy)复制的饮品将可能无效!' # Error Error_ConfigUpdate: '未知的配置版本:v&v1, 插件配置未更新!' @@ -38,11 +36,11 @@ Error_ItemNotPotion: '&c你手上的物品并未成功地被认定为饮品!' Error_NoBarrelAccess: '&c你没有权限与该木桶互动!' Error_NoBrewName: '&c未找到名字为''&v1&c''的饮品!' Error_NoPermissions: '&c你没有权限这样做.' -Error_PersistStatic: '&c数据固定的饮品一定是静滞化饮品!' Error_PlayerCommand: '&c该命令必须由玩家执行.' Error_Recipeload: '&c加载饮品配方时出现问题, 请查看控制台以获得详细信息!' Error_ShowHelp: 使用&6/brew help&f以查看帮助 Error_UnknownCommand: 未知命令. +Error_YmlRead: 'Could not read file config.yml, please make sure the file is in valid yml format (correct spaces etc.)' # Etc Etc_Barrel: 木桶 @@ -56,7 +54,6 @@ Help_Delete: '&6/brew delete &9删除手中的饮品.' Help_Help: '&6/brew help [页数] &9显示某一页的帮助.' Help_Info: '&6/brew info &9显示你目前的醉酒程度与质量, 醉酒质量将会影响宿醉程度.' Help_InfoOther: '&6/brew info [指定玩家] &9显示指定玩家目前的醉酒程度与质量.' -Help_Persist: '&6/brew persist &9将手中的饮品数据固定在服务端, 这将允许其被任何手段复制并饮用而不影响原饮品本体.' Help_Player: '&6/brew <目标玩家> <%醉酒程度> [醉酒质量] &9设定目标玩家的醉酒程度与质量.' Help_Reload: '&6/brew reload &9重载插件配置, 可用以重载饮品配方.' Help_Configname: '&6/brew ItemName &9显示手中物品的配置名称' diff --git a/resources/plugin.yml b/resources/plugin.yml index f08207a..312b86c 100644 --- a/resources/plugin.yml +++ b/resources/plugin.yml @@ -1,5 +1,5 @@ name: Brewery -version: 1.8.2 +version: 1.8.3 main: com.dre.brewery.P softdepend: [LWC, LogBlock, WorldGuard, GriefPrevention, Vault, Citadel] authors: [Milan Albrecht, Frank Baumann, ProgrammerDan, Daniel Saukel] @@ -41,7 +41,6 @@ permissions: brewery.cmd.create: true brewery.cmd.copy: true brewery.cmd.delete: true - brewery.cmd.persist: true brewery.cmd.static: true brewery.cmd.reload: true # * @@ -69,8 +68,6 @@ permissions: description: Copy Potions brewery.cmd.delete: description: Delete Potions - brewery.cmd.persist: - description: Make Potions Persistent brewery.cmd.static: description: Make Potions Static brewery.cmd.reload: diff --git a/src/com/dre/brewery/BCauldron.java b/src/com/dre/brewery/BCauldron.java index a5dc21f..f798934 100644 --- a/src/com/dre/brewery/BCauldron.java +++ b/src/com/dre/brewery/BCauldron.java @@ -1,28 +1,42 @@ package com.dre.brewery; -import java.util.concurrent.CopyOnWriteArrayList; - -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.Levelled; -import org.bukkit.entity.Player; +import com.dre.brewery.api.events.IngedientAddEvent; +import com.dre.brewery.recipe.BCauldronRecipe; +import com.dre.brewery.recipe.RecipeItem; +import com.dre.brewery.utility.BUtil; +import com.dre.brewery.utility.LegacyUtil; +import org.bukkit.Effect; +import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; -import org.bukkit.inventory.ItemStack; -import org.bukkit.Effect; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.Levelled; import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.UUID; public class BCauldron { - public static CopyOnWriteArrayList bcauldrons = new CopyOnWriteArrayList<>(); + public static final byte EMPTY = 0, SOME = 1, FULL = 2; + private static Set plInteracted = new HashSet<>(); // Interact Event helper + public static Map bcauldrons = new HashMap<>(); // All active cauldrons. Mapped to their block for fast retrieve private BIngredients ingredients = new BIngredients(); - private Block block; + private final Block block; private int state = 1; - private boolean someRemoved = false; + private boolean changed = false; - public BCauldron(Block block, ItemStack ingredient) { + public BCauldron(Block block) { this.block = block; - add(ingredient); - bcauldrons.add(this); + bcauldrons.put(block, this); } // loading from file @@ -30,29 +44,30 @@ public class BCauldron { this.block = block; this.state = state; this.ingredients = ingredients; - bcauldrons.add(this); + bcauldrons.put(block, this); } public void onUpdate() { // Check if fire still alive - if (!Util.isChunkLoaded(block) || LegacyUtil.isFireForCauldron(block.getRelative(BlockFace.DOWN))) { + if (!BUtil.isChunkLoaded(block) || LegacyUtil.isFireForCauldron(block.getRelative(BlockFace.DOWN))) { // add a minute to cooking time state++; - if (someRemoved) { - ingredients = ingredients.clone(); - someRemoved = false; + if (changed) { + ingredients = ingredients.copy(); + changed = false; } } } // add an ingredient to the cauldron - public void add(ItemStack ingredient) { - if (someRemoved) { - ingredients = ingredients.clone(); - someRemoved = false; + public void add(ItemStack ingredient, RecipeItem rItem) { + if (ingredient == null || ingredient.getType() == Material.AIR) return; + if (changed) { + ingredients = ingredients.copy(); + changed = false; } - ingredient = new ItemStack(ingredient.getType(), 1, ingredient.getDurability()); - ingredients.add(ingredient); + + ingredients.add(ingredient, rItem); block.getWorld().playEffect(block.getLocation(), Effect.EXTINGUISH, 0); if (state > 1) { state--; @@ -60,88 +75,97 @@ public class BCauldron { } // get cauldron by Block + @Nullable public static BCauldron get(Block block) { - for (BCauldron bcauldron : bcauldrons) { - if (bcauldron.block.equals(block)) { - return bcauldron; - } - } - return null; + return bcauldrons.get(block); } // get cauldron from block and add given ingredient - public static boolean ingredientAdd(Block block, ItemStack ingredient) { + // Calls the IngredientAddEvent and may be cancelled or changed + public static boolean ingredientAdd(Block block, ItemStack ingredient, Player player) { // if not empty - if (LegacyUtil.getFillLevel(block) != 0) { + if (LegacyUtil.getFillLevel(block) != EMPTY) { + + if (!BCauldronRecipe.acceptedMaterials.contains(ingredient.getType()) && !ingredient.hasItemMeta()) { + // Extremely fast way to check for most items + return false; + } + // If the Item is on the list, or customized, we have to do more checks + RecipeItem rItem = RecipeItem.getMatchingRecipeItem(ingredient, false); + if (rItem == null) { + return false; + } + BCauldron bcauldron = get(block); - if (bcauldron != null) { - bcauldron.add(ingredient); - return true; + if (bcauldron == null) { + bcauldron = new BCauldron(block); + } + + IngedientAddEvent event = new IngedientAddEvent(player, block, bcauldron, ingredient.clone(), rItem); + P.p.getServer().getPluginManager().callEvent(event); + if (!event.isCancelled()) { + bcauldron.add(event.getIngredient(), event.getRecipeItem()); + //P.p.debugLog("Cauldron add: t2 " + ((t2 - t1) / 1000) + " t3: " + ((t3 - t2) / 1000) + " t4: " + ((t4 - t3) / 1000) + " t5: " + ((t5 - t4) / 1000) + "µs"); + return event.willTakeItem(); } else { - new BCauldron(block, ingredient); - return true; + return false; } } return false; } // fills players bottle with cooked brew - public static boolean fill(Player player, Block block) { - BCauldron bcauldron = get(block); - if (bcauldron != null) { - if (!player.hasPermission("brewery.cauldron.fill")) { - P.p.msg(player, P.p.languageReader.get("Perms_NoCauldronFill")); - return true; + public boolean fill(Player player, Block block) { + if (!player.hasPermission("brewery.cauldron.fill")) { + P.p.msg(player, P.p.languageReader.get("Perms_NoCauldronFill")); + return true; + } + ItemStack potion = ingredients.cook(state); + if (potion == null) return false; + + if (P.use1_13) { + BlockData data = block.getBlockData(); + Levelled cauldron = ((Levelled) data); + if (cauldron.getLevel() <= 0) { + bcauldrons.remove(block); + return false; } - ItemStack potion = bcauldron.ingredients.cook(bcauldron.state); - if (potion != null) { + cauldron.setLevel(cauldron.getLevel() - 1); + // Update the new Level to the Block + // We have to use the BlockData variable "data" here instead of the casted "cauldron" + // otherwise < 1.13 crashes on plugin load for not finding the BlockData Class + block.setBlockData(data); - if (P.use1_13) { - BlockData data = block.getBlockData(); - Levelled cauldron = ((Levelled) data); - if (cauldron.getLevel() <= 0) { - bcauldrons.remove(bcauldron); - return false; - } - cauldron.setLevel(cauldron.getLevel() - 1); - // Update the new Level to the Block - // We have to use the BlockData variable "data" here instead of the casted "cauldron" - // otherwise < 1.13 crashes on plugin load for not finding the BlockData Class - block.setBlockData(data); + if (cauldron.getLevel() <= 0) { + bcauldrons.remove(block); + } else { + changed = true; + } - if (cauldron.getLevel() <= 0) { - bcauldrons.remove(bcauldron); - } else { - bcauldron.someRemoved = true; - } + } else { + byte data = block.getData(); + if (data > 3) { + data = 3; + } else if (data <= 0) { + bcauldrons.remove(block); + return false; + } + data -= 1; + LegacyUtil.setData(block, data); - } else { - byte data = block.getData(); - if (data > 3) { - data = 3; - } else if (data <= 0) { - bcauldrons.remove(bcauldron); - return false; - } - data -= 1; - LegacyUtil.setData(block, data); - - if (data == 0) { - bcauldrons.remove(bcauldron); - } else { - bcauldron.someRemoved = true; - } - } - // Bukkit Bug, inventory not updating while in event so this - // will delay the give - // but could also just use deprecated updateInventory() - giveItem(player, potion); - // player.getInventory().addItem(potion); - // player.getInventory().updateInventory(); - return true; + if (data == 0) { + bcauldrons.remove(block); + } else { + changed = true; } } - return false; + // Bukkit Bug, inventory not updating while in event so this + // will delay the give + // but could also just use deprecated updateInventory() + giveItem(player, potion); + // player.getInventory().addItem(potion); + // player.getInventory().updateInventory(); + return true; } // prints the current cooking time to the player @@ -160,37 +184,147 @@ public class BCauldron { } } - // reset to normal cauldron - public static void remove(Block block) { - if (LegacyUtil.getFillLevel(block) != 0) { - BCauldron bcauldron = get(block); - if (bcauldron != null) { - bcauldrons.remove(bcauldron); + public static void clickCauldron(PlayerInteractEvent event) { + Material materialInHand = event.getMaterial(); + ItemStack item = event.getItem(); + Player player = event.getPlayer(); + Block clickedBlock = event.getClickedBlock(); + assert clickedBlock != null; + + if (materialInHand == null || materialInHand == Material.AIR || materialInHand == Material.BUCKET) { + return; + + } else if (materialInHand == LegacyUtil.CLOCK) { + printTime(player, clickedBlock); + return; + + // fill a glass bottle with potion + } else if (materialInHand == Material.GLASS_BOTTLE) { + assert item != null; + if (player.getInventory().firstEmpty() != -1 || item.getAmount() == 1) { + BCauldron bcauldron = get(clickedBlock); + if (bcauldron != null) { + if (bcauldron.fill(player, clickedBlock)) { + event.setCancelled(true); + if (player.hasPermission("brewery.cauldron.fill")) { + if (item.getAmount() > 1) { + item.setAmount(item.getAmount() - 1); + } else { + setItemInHand(event, Material.AIR, false); + } + } + } + } + } else { + event.setCancelled(true); + } + return; + + // reset cauldron when refilling to prevent unlimited source of potions + } else if (materialInHand == Material.WATER_BUCKET) { + if (!P.use1_9) { + // We catch >=1.9 cases in the Cauldron Listener + if (LegacyUtil.getFillLevel(clickedBlock) == 1) { + // will only remove when existing + BCauldron.remove(clickedBlock); + } + } + return; + } + + // Check if fire alive below cauldron when adding ingredients + Block down = clickedBlock.getRelative(BlockFace.DOWN); + if (LegacyUtil.isFireForCauldron(down)) { + + event.setCancelled(true); + boolean handSwap = false; + + // Interact event is called twice!!!?? in 1.9, once for each hand. + // Certain Items in Hand cause one of them to be cancelled or not called at all sometimes. + // We mark if a player had the event for the main hand + // If not, we handle the main hand in the event for the off hand + if (P.use1_9) { + if (event.getHand() == EquipmentSlot.HAND) { + final UUID id = player.getUniqueId(); + plInteracted.add(id); + P.p.getServer().getScheduler().runTask(P.p, () -> plInteracted.remove(id)); + } else if (event.getHand() == EquipmentSlot.OFF_HAND) { + if (!plInteracted.remove(player.getUniqueId())) { + item = player.getInventory().getItemInMainHand(); + if (item != null && item.getType() != Material.AIR) { + materialInHand = item.getType(); + handSwap = true; + } else { + item = event.getItem(); + } + } + } + } + if (item == null) return; + + if (!player.hasPermission("brewery.cauldron.insert")) { + P.p.msg(player, P.p.languageReader.get("Perms_NoCauldronInsert")); + return; + } + if (ingredientAdd(clickedBlock, item, player)) { + boolean isBucket = item.getType().equals(Material.WATER_BUCKET) + || item.getType().equals(Material.LAVA_BUCKET) + || item.getType().equals(Material.MILK_BUCKET); + if (item.getAmount() > 1) { + item.setAmount(item.getAmount() - 1); + + if (isBucket) { + giveItem(player, new ItemStack(Material.BUCKET)); + } + } else { + if (isBucket) { + setItemInHand(event, Material.BUCKET, handSwap); + } else { + setItemInHand(event, Material.AIR, handSwap); + } + } } } } + @SuppressWarnings("deprecation") + public static void setItemInHand(PlayerInteractEvent event, Material mat, boolean swapped) { + if (P.use1_9) { + if ((event.getHand() == EquipmentSlot.OFF_HAND) != swapped) { + event.getPlayer().getInventory().setItemInOffHand(new ItemStack(mat)); + } else { + event.getPlayer().getInventory().setItemInMainHand(new ItemStack(mat)); + } + } else { + event.getPlayer().setItemInHand(new ItemStack(mat)); + } + } + + // reset to normal cauldron + public static boolean remove(Block block) { + if (LegacyUtil.getFillLevel(block) != EMPTY) { + return bcauldrons.remove(block) != null; + } + return false; + } + // unloads cauldrons that are in a unloading world // as they were written to file just before, this is safe to do public static void onUnload(String name) { - for (BCauldron bcauldron : bcauldrons) { - if (bcauldron.block.getWorld().getName().equals(name)) { - bcauldrons.remove(bcauldron); - } - } + bcauldrons.keySet().removeIf(block -> block.getWorld().getName().equals(name)); } public static void save(ConfigurationSection config, ConfigurationSection oldData) { - Util.createWorldSections(config); + BUtil.createWorldSections(config); if (!bcauldrons.isEmpty()) { int id = 0; - for (BCauldron cauldron : bcauldrons) { + for (BCauldron cauldron : bcauldrons.values()) { String worldName = cauldron.block.getWorld().getName(); String prefix; if (worldName.startsWith("DXL_")) { - prefix = Util.getDxlName(worldName) + "." + id; + prefix = BUtil.getDxlName(worldName) + "." + id; } else { prefix = cauldron.block.getWorld().getUID().toString() + "." + id; } @@ -216,11 +350,7 @@ public class BCauldron { // bukkit bug not updating the inventory while executing event, have to // schedule the give public static void giveItem(final Player player, final ItemStack item) { - P.p.getServer().getScheduler().runTaskLater(P.p, new Runnable() { - public void run() { - player.getInventory().addItem(item); - } - }, 1L); + P.p.getServer().getScheduler().runTaskLater(P.p, () -> player.getInventory().addItem(item), 1L); } } diff --git a/src/com/dre/brewery/BDistiller.java b/src/com/dre/brewery/BDistiller.java new file mode 100644 index 0000000..da7e2ba --- /dev/null +++ b/src/com/dre/brewery/BDistiller.java @@ -0,0 +1,242 @@ +package com.dre.brewery; + +import com.dre.brewery.lore.BrewLore; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.block.BrewingStand; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.BrewerInventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.PotionMeta; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.HashMap; +import java.util.Map; + +/** + * Updated for 1.9 to replicate the "Brewing" process for distilling. + * Because of how metadata has changed, the brewer no longer triggers as previously described. + * So, I've added some event tracking and manual forcing of the brewing "animation" if the + * set of ingredients in the brewer can be distilled. + * Nothing here should interfere with vanilla brewing. + * + * @author ProgrammerDan (1.9 distillation update only) + */ +public class BDistiller { + + private static final int DISTILLTIME = 400; + private static Map trackedDistillers = new HashMap<>(); + + private int taskId; + private int runTime = -1; + private int brewTime = -1; + private Block standBlock; + private int fuel; + + public BDistiller(Block standBlock, int fuel) { + this.standBlock = standBlock; + this.fuel = fuel; + } + + public void cancelDistill() { + Bukkit.getScheduler().cancelTask(taskId); // cancel prior + } + + public void start() { + taskId = new DistillRunnable().runTaskTimer(P.p, 2L, 1L).getTaskId(); + } + + public static void distillerClick(InventoryClickEvent event) { + BrewerInventory standInv = (BrewerInventory) event.getInventory(); + final Block standBlock = standInv.getHolder().getBlock(); + + // If we were already tracking the brewer, cancel any ongoing event due to the click. + BDistiller distiller = trackedDistillers.get(standBlock); + if (distiller != null) { + distiller.cancelDistill(); + standInv.getHolder().setBrewingTime(0); // Fixes brewing continuing without fuel for normal potions + standInv.getHolder().update(); + } + final int fuel = standInv.getHolder().getFuelLevel(); + + // Now check if we should bother to track it. + distiller = new BDistiller(standBlock, fuel); + trackedDistillers.put(standBlock, distiller); + distiller.start(); + } + + public static boolean isTrackingDistiller(Block block) { + return trackedDistillers.containsKey(block); + } + + // Returns a Brew or null for every Slot in the BrewerInventory + public static Brew[] getDistillContents(BrewerInventory inv) { + ItemStack item; + Brew[] contents = new Brew[3]; + for (int slot = 0; slot < 3; slot++) { + item = inv.getItem(slot); + if (item != null) { + contents[slot] = Brew.get(item); + } + } + return contents; + } + + public static void checkContents(BrewerInventory inv, Brew[] contents) { + ItemStack item; + for (int slot = 0; slot < 3; slot++) { + if (contents[slot] != null) { + item = inv.getItem(slot); + if (item == null || !Brew.isBrew(item)) { + contents[slot] = null; + } + } + } + } + + public static byte hasBrew(BrewerInventory brewer, Brew[] contents) { + ItemStack item = brewer.getItem(3); // ingredient + boolean glowstone = (item != null && Material.GLOWSTONE_DUST == item.getType()); // need dust in the top slot. + byte customFound = 0; + for (Brew brew : contents) { + if (brew != null) { + if (!glowstone) { + return 1; + } + if (brew.canDistill()) { + return 2; + } else { + customFound = 1; + } + } + } + return customFound; + } + + public static boolean runDistill(BrewerInventory inv, Brew[] contents) { + boolean custom = false; + for (int slot = 0; slot < 3; slot++) { + if (contents[slot] == null) continue; + if (contents[slot].canDistill()) { + // is further distillable + custom = true; + } else { + contents[slot] = null; + } + } + if (custom) { + Brew.distillAll(inv, contents); + return true; + } + return false; + } + + public static int getLongestDistillTime(Brew[] contents) { + int bestTime = 0; + int time; + for (int slot = 0; slot < 3; slot++) { + if (contents[slot] == null) continue; + time = contents[slot].getDistillTimeNextRun(); + if (time == 0) { + // Undefined Potion needs 40 seconds + time = 800; + } + if (time > bestTime) { + bestTime = time; + } + } + if (bestTime > 0) { + return bestTime; + } + return 800; + } + + public static void showAlc(BrewerInventory inv, Brew[] contents) { + for (int slot = 0; slot < 3; slot++) { + if (contents[slot] != null) { + // Show Alc in lore + ItemStack item = inv.getItem(slot); + PotionMeta meta = (PotionMeta) item.getItemMeta(); + BrewLore brewLore = new BrewLore(contents[slot], meta); + brewLore.updateAlc(true); + brewLore.write(); + item.setItemMeta(meta); + } + } + } + + public class DistillRunnable extends BukkitRunnable { + Brew[] contents = null; + + @Override + public void run() { + BlockState now = standBlock.getState(); + if (now instanceof BrewingStand) { + BrewingStand stand = (BrewingStand) now; + if (brewTime == -1) { // only check at the beginning (and end) for distillables + BrewerInventory inventory = stand.getInventory(); + if (contents == null) { + contents = getDistillContents(inventory); + } else { + checkContents(inventory, contents); + } + switch (hasBrew(inventory, contents)) { + case 1: + // Custom potion but not for distilling. Stop any brewing and cancel this task + if (stand.getBrewingTime() > 0) { + if (P.use1_11) { + // The trick below doesnt work in 1.11, but we dont need it anymore + // This should only happen with older Brews that have been made with the old Potion Color System + stand.setBrewingTime(Short.MAX_VALUE); + } else { + // Brewing time is sent and stored as short + // This sends a negative short value to the Client + // In the client the Brewer will look like it is not doing anything + stand.setBrewingTime(Short.MAX_VALUE << 1); + } + stand.setFuelLevel(fuel); + stand.update(); + } + case 0: + // No custom potion, cancel and ignore + this.cancel(); + trackedDistillers.remove(standBlock); + showAlc(inventory, contents); + P.p.debugLog("nothing to distill"); + return; + default: + runTime = getLongestDistillTime(contents); + brewTime = runTime; + P.p.debugLog("using brewtime: " + runTime); + + } + } + + brewTime--; // count down. + stand.setBrewingTime((int) ((float) brewTime / ((float) runTime / (float) DISTILLTIME)) + 1); + + if (brewTime <= 1) { // Done! + checkContents(stand.getInventory(), contents); + stand.setBrewingTime(0); + stand.update(); + if (!runDistill(stand.getInventory(), contents)) { + this.cancel(); + trackedDistillers.remove(standBlock); + P.p.debugLog("All done distilling"); + } else { + brewTime = -1; // go again. + P.p.debugLog("Can distill more! Continuing."); + } + } else { + stand.update(); + } + } else { + this.cancel(); + trackedDistillers.remove(standBlock); + P.p.debugLog("The block was replaced; not a brewing stand."); + } + } + } +} diff --git a/src/com/dre/brewery/BIngredients.java b/src/com/dre/brewery/BIngredients.java index 1859824..f5e59ec 100644 --- a/src/com/dre/brewery/BIngredients.java +++ b/src/com/dre/brewery/BIngredients.java @@ -1,130 +1,192 @@ package com.dre.brewery; +import com.dre.brewery.api.events.brew.BrewModifyEvent; +import com.dre.brewery.lore.Base91EncoderStream; +import com.dre.brewery.lore.BrewLore; +import com.dre.brewery.recipe.BCauldronRecipe; +import com.dre.brewery.recipe.BRecipe; +import com.dre.brewery.recipe.Ingredient; +import com.dre.brewery.recipe.ItemLoader; +import com.dre.brewery.recipe.RecipeItem; +import com.dre.brewery.recipe.PotionColor; import org.bukkit.Material; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.PotionMeta; -import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +/** + * Represents ingredients in Cauldron, Brew + */ public class BIngredients { - public static Set possibleIngredients = new HashSet<>(); - public static ArrayList recipes = new ArrayList<>(); - public static Map cookedNames = new HashMap<>(); - private static int lastId = 0; + private static int lastId = 0; // Legacy - private int id; - private ArrayList ingredients = new ArrayList<>(); - private Map materials = new HashMap<>(); // Merged List Of ingredients that doesnt consider Durability + private int id; // Legacy + private List ingredients = new ArrayList<>(); private int cookedTime; - // Represents ingredients in Cauldron, Brew - // Init a new BIngredients + /** + * Init a new BIngredients + */ public BIngredients() { - this.id = lastId; - lastId++; + //this.id = lastId; + //lastId++; } - // Load from File - public BIngredients(ArrayList ingredients, int cookedTime) { + /** + * Load from File + */ + public BIngredients(List ingredients, int cookedTime) { this.ingredients = ingredients; this.cookedTime = cookedTime; - this.id = lastId; - lastId++; + //this.id = lastId; + //lastId++; + } - for (ItemStack item : ingredients) { - addMaterial(item); + /** + * Load from legacy Brew section + */ + public BIngredients(List ingredients, int cookedTime, boolean legacy) { + this(ingredients, cookedTime); + if (legacy) { + this.id = lastId; + lastId++; } } - // Add an ingredient to this + /** + * Force add an ingredient to this. + *

Will not check if item is acceptable + * + * @param ingredient the item to add + */ public void add(ItemStack ingredient) { - addMaterial(ingredient); - for (ItemStack item : ingredients) { - if (item.isSimilar(ingredient)) { - item.setAmount(item.getAmount() + ingredient.getAmount()); + for (Ingredient existing : ingredients) { + if (existing.matches(ingredient)) { + existing.setAmount(existing.getAmount() + 1); return; } } - ingredients.add(ingredient); + + Ingredient ing = RecipeItem.getMatchingRecipeItem(ingredient, true).toIngredient(ingredient); + ing.setAmount(1); + ingredients.add(ing); } - private void addMaterial(ItemStack ingredient) { - if (materials.containsKey(ingredient.getType())) { - int newAmount = materials.get(ingredient.getType()) + ingredient.getAmount(); - materials.put(ingredient.getType(), newAmount); - } else { - materials.put(ingredient.getType(), ingredient.getAmount()); + /** + * Add an ingredient to this with corresponding RecipeItem + * + * @param ingredient the item to add + * @param rItem the RecipeItem that matches the ingredient + */ + public void add(ItemStack ingredient, RecipeItem rItem) { + Ingredient ingredientItem = rItem.toIngredient(ingredient); + for (Ingredient existing : ingredients) { + if (existing.isSimilar(ingredientItem)) { + existing.setAmount(existing.getAmount() + 1); + return; + } } + ingredientItem.setAmount(1); + ingredients.add(ingredientItem); } - // returns an Potion item with cooked ingredients + /** + * returns an Potion item with cooked ingredients + */ public ItemStack cook(int state) { ItemStack potion = new ItemStack(Material.POTION); PotionMeta potionMeta = (PotionMeta) potion.getItemMeta(); + assert potionMeta != null; // cookedTime is always time in minutes, state may differ with number of ticks cookedTime = state; String cookedName = null; BRecipe cookRecipe = getCookRecipe(); + Brew brew; - int uid = Brew.generateUID(); + //int uid = Brew.generateUID(); if (cookRecipe != null) { // Potion is best with cooking only int quality = (int) Math.round((getIngredientQuality(cookRecipe) + getCookingQuality(cookRecipe, false)) / 2.0); - P.p.debugLog("cooked potion has Quality: " + quality); - Brew brew = new Brew(uid, quality, cookRecipe, this); - Brew.addOrReplaceEffects(potionMeta, brew.getEffects(), brew.getQuality()); + int alc = (int) Math.round(cookRecipe.getAlcohol() * ((float) quality / 10.0f)); + P.p.debugLog("cooked potion has Quality: " + quality + ", Alc: " + alc); + brew = new Brew(quality, alc, cookRecipe, this); + BrewLore lore = new BrewLore(brew, potionMeta); + lore.updateQualityStars(false); + lore.updateCustomLore(); + lore.updateAlc(false); + lore.addOrReplaceEffects(brew.getEffects(), brew.getQuality()); + lore.write(); cookedName = cookRecipe.getName(quality); - Brew.PotionColor.fromString(cookRecipe.getColor()).colorBrew(potionMeta, potion, false); + cookRecipe.getColor().colorBrew(potionMeta, potion, false); } else { // new base potion - new Brew(uid, this); + brew = new Brew(this); if (state <= 1) { cookedName = P.p.languageReader.get("Brew_ThickBrew"); - Brew.PotionColor.BLUE.colorBrew(potionMeta, potion, false); + PotionColor.BLUE.colorBrew(potionMeta, potion, false); } else { - for (Material ingredient : materials.keySet()) { - if (cookedNames.containsKey(ingredient)) { - // if more than half of the ingredients is of one kind - if (materials.get(ingredient) > (getIngredientsCount() / 2)) { - cookedName = cookedNames.get(ingredient); - Brew.PotionColor.CYAN.colorBrew(potionMeta, potion, true); - } + BCauldronRecipe cauldronRecipe = getCauldronRecipe(); + if (cauldronRecipe != null) { + P.p.debugLog("Found Cauldron Recipe: " + cauldronRecipe.getName()); + cookedName = cauldronRecipe.getName(); + if (cauldronRecipe.getLore() != null) { + BrewLore lore = new BrewLore(brew, potionMeta); + lore.addCauldronLore(cauldronRecipe.getLore()); + lore.write(); } + cauldronRecipe.getColor().colorBrew(potionMeta, potion, true); } } } if (cookedName == null) { // if no name could be found cookedName = P.p.languageReader.get("Brew_Undefined"); - Brew.PotionColor.CYAN.colorBrew(potionMeta, potion, true); + PotionColor.CYAN.colorBrew(potionMeta, potion, true); } potionMeta.setDisplayName(P.p.color("&f" + cookedName)); - if (!P.use1_14) { + //if (!P.use1_14) { // Before 1.14 the effects duration would strangely be only a quarter of what we tell it to be // This is due to the Duration Modifier, that is removed in 1.14 - uid *= 4; - } + // uid *= 4; + //} // This effect stores the UID in its Duration - potionMeta.addCustomEffect((PotionEffectType.REGENERATION).createEffect(uid, 0), true); + //potionMeta.addCustomEffect((PotionEffectType.REGENERATION).createEffect((uid * 4), 0), true); + + brew.touch(); + BrewModifyEvent modifyEvent = new BrewModifyEvent(brew, potionMeta, BrewModifyEvent.Type.FILL); + P.p.getServer().getPluginManager().callEvent(modifyEvent); + if (modifyEvent.isCancelled()) { + return null; + } + brew.save(potionMeta); potion.setItemMeta(potionMeta); + P.p.metricsForCreate(false); return potion; } - // returns amount of ingredients - private int getIngredientsCount() { + /** + * returns amount of ingredients + */ + public int getIngredientsCount() { int count = 0; - for (ItemStack item : ingredients) { - count += item.getAmount(); + for (Ingredient ing : ingredients) { + count += ing.getAmount(); } return count; } @@ -137,8 +199,9 @@ public class BIngredients { return cookedTime; } - // best recipe for current state of potion, STILL not always returns the - // correct one... + /** + * best recipe for current state of potion, STILL not always returns the correct one... + */ public BRecipe getBestRecipe(float wood, float time, boolean distilled) { float quality = 0; int ingredientQuality; @@ -146,7 +209,7 @@ public class BIngredients { int woodQuality; int ageQuality; BRecipe bestRecipe = null; - for (BRecipe recipe : recipes) { + for (BRecipe recipe : BRecipe.getAllRecipes()) { ingredientQuality = getIngredientQuality(recipe); cookingQuality = getCookingQuality(recipe, distilled); @@ -179,8 +242,9 @@ public class BIngredients { return bestRecipe; } - // returns recipe that is cooking only and matches the ingredients and - // cooking time + /** + * returns recipe that is cooking only and matches the ingredients and cooking time + */ public BRecipe getCookRecipe() { BRecipe bestRecipe = getBestRecipe(0, 0, false); @@ -193,9 +257,31 @@ public class BIngredients { return null; } - // returns the currently best matching recipe for distilling for the - // ingredients and cooking time - public BRecipe getdistillRecipe(float wood, float time) { + /** + * Get Cauldron Recipe that matches the contents of the cauldron + */ + @Nullable + public BCauldronRecipe getCauldronRecipe() { + BCauldronRecipe best = null; + float bestMatch = 0; + float match; + for (BCauldronRecipe recipe : BCauldronRecipe.getAllRecipes()) { + match = recipe.getIngredientMatch(ingredients); + if (match >= 10) { + return recipe; + } + if (match > bestMatch) { + best = recipe; + bestMatch = match; + } + } + return best; + } + + /** + * returns the currently best matching recipe for distilling for the ingredients and cooking time + */ + public BRecipe getDistillRecipe(float wood, float time) { BRecipe bestRecipe = getBestRecipe(wood, time, true); // Check if best recipe needs to be destilled @@ -207,8 +293,9 @@ public class BIngredients { return null; } - // returns currently best matching recipe for ingredients, cooking- and - // ageingtime + /** + * returns currently best matching recipe for ingredients, cooking- and ageingtime + */ public BRecipe getAgeRecipe(float wood, float time, boolean distilled) { BRecipe bestRecipe = getBestRecipe(wood, time, distilled); @@ -220,8 +307,9 @@ public class BIngredients { return null; } - // returns the quality of the ingredients conditioning given recipe, -1 if - // no recipe is near them + /** + * returns the quality of the ingredients conditioning given recipe, -1 if no recipe is near them + */ public int getIngredientQuality(BRecipe recipe) { float quality = 10; int count; @@ -230,20 +318,9 @@ public class BIngredients { // when ingredients are not complete return -1; } - ArrayList mergedChecked = new ArrayList<>(); - for (ItemStack ingredient : ingredients) { - if (mergedChecked.contains(ingredient.getType())) { - // This ingredient type was already checked as part of a merged material - continue; - } + for (Ingredient ingredient : ingredients) { int amountInRecipe = recipe.amountOf(ingredient); - // If we dont consider durability for this ingredient, check the merged material - if (recipe.hasExactData(ingredient)) { - count = ingredient.getAmount(); - } else { - mergedChecked.add(ingredient.getType()); - count = materials.get(ingredient.getType()); - } + count = ingredient.getAmount(); if (amountInRecipe == 0) { // this ingredient doesnt belong into the recipe if (count > (getIngredientsCount() / 2)) { @@ -270,7 +347,9 @@ public class BIngredients { return -1; } - // returns the quality regarding the cooking-time conditioning given Recipe + /** + * returns the quality regarding the cooking-time conditioning given Recipe + */ public int getCookingQuality(BRecipe recipe, boolean distilled) { if (!recipe.needsDistilling() == distilled) { return -1; @@ -286,15 +365,19 @@ public class BIngredients { return -1; } - // returns pseudo quality of distilling. 0 if doesnt match the need of the recipes distilling - public int getDistillQuality(BRecipe recipe, int distillRuns) { + /** + * returns pseudo quality of distilling. 0 if doesnt match the need of the recipes distilling + */ + public int getDistillQuality(BRecipe recipe, byte distillRuns) { if (recipe.needsDistilling() != distillRuns > 0) { return 0; } return 10 - Math.abs(recipe.getDistillRuns() - distillRuns); } - // returns the quality regarding the barrel wood conditioning given Recipe + /** + * returns the quality regarding the barrel wood conditioning given Recipe + */ public int getWoodQuality(BRecipe recipe, float wood) { if (recipe.getWood() == 0) { // type of wood doesnt matter @@ -305,24 +388,100 @@ public class BIngredients { return Math.max(quality, 0); } - // returns the quality regarding the ageing time conditioning given Recipe + /** + * returns the quality regarding the ageing time conditioning given Recipe + */ public int getAgeQuality(BRecipe recipe, float time) { int quality = 10 - Math.round(Math.abs(time - recipe.getAge()) * ((float) recipe.getDifficulty() / 2)); return Math.max(quality, 0); } + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof BIngredients)) return false; + BIngredients other = ((BIngredients) obj); + return cookedTime == other.cookedTime && + ingredients.equals(other.ingredients); + } + // Creates a copy ingredients - public BIngredients clone() { + public BIngredients copy() { BIngredients copy = new BIngredients(); copy.ingredients.addAll(ingredients); - copy.materials.putAll(materials); copy.cookedTime = cookedTime; return copy; } + @Override + public String toString() { + return "BIngredients{" + + "cookedTime=" + cookedTime + + ", total ingredients: " + getIngredientsCount() + '}'; + } + + /*public void testStore(DataOutputStream out) throws IOException { + out.writeInt(cookedTime); + out.writeByte(ingredients.size()); + for (ItemStack item : ingredients) { + out.writeUTF(item.getType().name()); + out.writeShort(item.getDurability()); + out.writeShort(item.getAmount()); + } + } + + public void testLoad(DataInputStream in) throws IOException { + if (in.readInt() != cookedTime) { + P.p.log("cookedtime wrong"); + } + if (in.readUnsignedByte() != ingredients.size()) { + P.p.log("size wrong"); + return; + } + for (ItemStack item : ingredients) { + if (!in.readUTF().equals(item.getType().name())) { + P.p.log("name wrong"); + } + if (in.readShort() != item.getDurability()) { + P.p.log("dur wrong"); + } + if (in.readShort() != item.getAmount()) { + P.p.log("amount wrong"); + } + } + }*/ + + public void save(DataOutputStream out) throws IOException { + out.writeInt(cookedTime); + out.writeByte(ingredients.size()); + for (Ingredient ing : ingredients) { + ing.saveTo(out); + out.writeShort(Math.min(ing.getAmount(), Short.MAX_VALUE)); + } + } + + public static BIngredients load(DataInputStream in, short dataVersion) throws IOException { + int cookedTime = in.readInt(); + byte size = in.readByte(); + List ing = new ArrayList<>(size); + for (; size > 0; size--) { + ItemLoader itemLoader = new ItemLoader(dataVersion, in, in.readUTF()); + if (Ingredient.LOADERS.containsKey(itemLoader.getSaveID())) { + Ingredient loaded = Ingredient.LOADERS.get(itemLoader.getSaveID()).apply(itemLoader); + int amount = in.readShort(); + if (loaded != null) { + loaded.setAmount(amount); + ing.add(loaded); + } + } + } + return new BIngredients(ing, cookedTime); + } + // saves data into main Ingredient section. Returns the save id - public int save(ConfigurationSection config) { + // Only needed for legacy potions + public int saveLegacy(ConfigurationSection config) { String path = "Ingredients." + id; if (cookedTime != 0) { config.set(path + ".cookedTime", cookedTime); @@ -332,13 +491,26 @@ public class BIngredients { } //convert the ingredient Material to String - public Map serializeIngredients() { + /*public Map serializeIngredients() { Map mats = new HashMap<>(); for (ItemStack item : ingredients) { String mat = item.getType().name() + "," + item.getDurability(); mats.put(mat, item.getAmount()); } return mats; + }*/ + + // Serialize Ingredients to String for storing in yml, ie for Cauldrons + public String serializeIngredients() { + ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); + try (DataOutputStream out = new DataOutputStream(new Base91EncoderStream(byteStream))) { + out.writeByte(Brew.SAVE_VER); + save(out); + } catch (IOException e) { + e.printStackTrace(); + return ""; + } + return byteStream.toString(); } } diff --git a/src/com/dre/brewery/BPlayer.java b/src/com/dre/brewery/BPlayer.java index 14111b4..289fd7c 100644 --- a/src/com/dre/brewery/BPlayer.java +++ b/src/com/dre/brewery/BPlayer.java @@ -1,5 +1,12 @@ package com.dre.brewery; +import com.dre.brewery.api.events.PlayerEffectEvent; +import com.dre.brewery.api.events.PlayerPukeEvent; +import com.dre.brewery.api.events.PlayerPushEvent; +import com.dre.brewery.api.events.brew.BrewDrinkEvent; +import com.dre.brewery.filedata.BConfig; +import com.dre.brewery.recipe.BEffect; +import com.dre.brewery.utility.BUtil; import org.apache.commons.lang.mutable.MutableInt; import org.bukkit.Location; import org.bukkit.Material; @@ -10,8 +17,11 @@ import org.bukkit.entity.Item; import org.bukkit.entity.Player; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import org.bukkit.util.Vector; +import org.jetbrains.annotations.Nullable; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; @@ -24,20 +34,9 @@ public class BPlayer { private static int taskId; private static boolean modAge = true; private static Random pukeRand; - private static Method gh; + private static Method itemHandle; private static Field age; - // Settings - public static Map drainItems = new HashMap<>();// DrainItem Material and Strength - public static Material pukeItem; - public static int pukeDespawntime; - public static int hangoverTime; - public static boolean overdrinkKick; - public static boolean enableHome; - public static boolean enableLoginDisallow; - public static boolean enablePuke; - public static String homeType; - private int quality = 0;// = quality of drunkeness * drunkeness private int drunkeness = 0;// = amount of drunkeness private int offlineDrunk = 0;// drunkeness when gone offline @@ -55,14 +54,16 @@ public class BPlayer { players.put(name, this); } + @Nullable public static BPlayer get(Player player) { if (!players.isEmpty()) { - return players.get(Util.playerString(player)); + return players.get(BUtil.playerString(player)); } return null; } // This method may be slow and should not be used if not needed + @Nullable public static BPlayer getByName(String playerName) { if (P.useUUID) { for (Map.Entry entry : players.entrySet()) { @@ -105,18 +106,18 @@ public class BPlayer { } public static boolean hasPlayer(Player player) { - return players.containsKey(Util.playerString(player)); + return players.containsKey(BUtil.playerString(player)); } // Create a new BPlayer and add it to the list public static BPlayer addPlayer(Player player) { BPlayer bPlayer = new BPlayer(); - players.put(Util.playerString(player), bPlayer); + players.put(BUtil.playerString(player), bPlayer); return bPlayer; } public static void remove(Player player) { - players.remove(Util.playerString(player)); + players.remove(BUtil.playerString(player)); } public static int numDrunkPlayers() { @@ -124,9 +125,10 @@ public class BPlayer { } public void remove() { - for (Map.Entry entry : players.entrySet()) { + for (Iterator> iterator = players.entrySet().iterator(); iterator.hasNext(); ) { + Map.Entry entry = iterator.next(); if (entry.getValue() == this) { - players.remove(entry.getKey()); + iterator.remove(); return; } } @@ -137,39 +139,58 @@ public class BPlayer { } // Drink a brew and apply effects, etc. - public static void drink(Brew brew, Player player) { - int brewAlc = brew.calcAlcohol(); - if (brewAlc == 0) { - //no alcohol so we dont need to add a BPlayer - addBrewEffects(brew, player); - return; - } + public static boolean drink(Brew brew, ItemMeta meta, Player player) { BPlayer bPlayer = get(player); if (bPlayer == null) { bPlayer = addPlayer(player); } + BrewDrinkEvent drinkEvent = new BrewDrinkEvent(brew, meta, player, bPlayer); + P.p.getServer().getPluginManager().callEvent(drinkEvent); + if (drinkEvent.isCancelled()) { + if (bPlayer.drunkeness <= 0) { + bPlayer.remove(); + } + return false; + } + + if (brew.hasRecipe()) { + brew.getCurrentRecipe().applyDrinkFeatures(player); + } + P.p.metricsForDrink(brew); + + int brewAlc = drinkEvent.getAddedAlcohol(); + int quality = drinkEvent.getQuality(); + List effects = getBrewEffects(brew.getEffects(), quality); + + if (brewAlc < 1) { + //no alcohol so we dont need to add a BPlayer + applyEffects(effects, player, PlayerEffectEvent.EffectType.DRINK); + if (bPlayer.drunkeness <= 0) { + bPlayer.remove(); + } + return true; + } + bPlayer.drunkeness += brewAlc; - if (brew.getQuality() > 0) { - bPlayer.quality += brew.getQuality() * brewAlc; + if (quality > 0) { + bPlayer.quality += quality * brewAlc; } else { bPlayer.quality += brewAlc; } + applyEffects(effects, player, PlayerEffectEvent.EffectType.DRINK); + applyEffects(getQualityEffects(quality, brewAlc), player, PlayerEffectEvent.EffectType.QUALITY); - if (bPlayer.drunkeness <= 100) { - - addBrewEffects(brew, player); - addQualityEffects(brew.getQuality(), brewAlc, player); - - } else { + if (bPlayer.drunkeness > 100) { bPlayer.drinkCap(player); } + return true; } // Player has drunken too much public void drinkCap(Player player) { quality = getQuality() * 100; drunkeness = 100; - if (overdrinkKick && !player.hasPermission("brewery.bypass.overdrink")) { + if (BConfig.overdrinkKick && !player.hasPermission("brewery.bypass.overdrink")) { P.p.getServer().getScheduler().scheduleSyncDelayedTask(P.p, () -> passOut(player), 1); } else { addPuke(player, 60 + (int) (Math.random() * 60.0)); @@ -187,7 +208,7 @@ public class BPlayer { // Eat something to drain the drunkeness public void drainByItem(Player player, Material mat) { - int strength = drainItems.get(mat); + int strength = BConfig.drainItems.get(mat); if (drain(player, strength)) { remove(player); } @@ -211,7 +232,7 @@ public class BPlayer { } quality = getQuality(); if (drunkeness <= -offlineDrunk) { - return drunkeness <= -hangoverTime; + return drunkeness <= -BConfig.hangoverTime; } } return false; @@ -244,6 +265,13 @@ public class BPlayer { push.setX(Math.random() - 0.5); push.setZ(Math.random() - 0.5); } + PlayerPushEvent pushEvent = new PlayerPushEvent(player, push, this); + P.p.getServer().getPluginManager().callEvent(pushEvent); + push = pushEvent.getPush(); + if (pushEvent.isCancelled() || push.lengthSquared() <= 0) { + time = -10; + return; + } player.setVelocity(push); } else if (time < 0 && time > -10) { // push him some more in the same direction @@ -272,7 +300,7 @@ public class BPlayer { if (drunkeness <= 70) { return 0; } - if (!enableLoginDisallow) { + if (!BConfig.enableLoginDisallow) { if (drunkeness <= 100) { return 0; } else { @@ -302,24 +330,20 @@ public class BPlayer { return; } // delayed login event as the player is not fully accessible pre login - P.p.getServer().getScheduler().runTaskLater(P.p, new Runnable() { - public void run() { - login(player); - } - }, 1L); + P.p.getServer().getScheduler().runTaskLater(P.p, () -> login(player), 1L); } // he may be having a hangover public void login(final Player player) { if (drunkeness < 10) { if (offlineDrunk > 60) { - if (enableHome && !player.hasPermission("brewery.bypass.teleport")) { + if (BConfig.enableHome && !player.hasPermission("brewery.bypass.teleport")) { goHome(player); } } hangoverEffects(player); // wird der spieler noch gebraucht? - players.remove(Util.playerString(player)); + players.remove(BUtil.playerString(player)); } else if (offlineDrunk - drunkeness >= 30) { Location randomLoc = Wakeup.getRandom(player.getLocation()); @@ -339,6 +363,7 @@ public class BPlayer { } public void goHome(final Player player) { + String homeType = BConfig.homeType; if (homeType != null) { Location home = null; if (homeType.equalsIgnoreCase("bed")) { @@ -365,11 +390,11 @@ public class BPlayer { public void drunkPuke(Player player) { if (drunkeness >= 80) { if (drunkeness >= 90) { - if (Math.random() < 0.15 - (getQuality() / 100)) { + if (Math.random() < 0.15f - (getQuality() / 100f)) { addPuke(player, 20 + (int) (Math.random() * 40)); } } else { - if (Math.random() < 0.08 - (getQuality() / 100)) { + if (Math.random() < 0.08f - (getQuality() / 100f)) { addPuke(player, 10 + (int) (Math.random() * 30)); } } @@ -378,18 +403,20 @@ public class BPlayer { // make a Player puke "count" items public static void addPuke(Player player, int count) { - if (!enablePuke) { + if (!BConfig.enablePuke) { + return; + } + + PlayerPukeEvent event = new PlayerPukeEvent(player, count); + P.p.getServer().getPluginManager().callEvent(event); + if (event.isCancelled() || event.getCount() < 1) { return; } if (pTasks.isEmpty()) { - taskId = P.p.getServer().getScheduler().scheduleSyncRepeatingTask(P.p, new Runnable() { - public void run() { - pukeTask(); - } - }, 1L, 1L); + taskId = P.p.getServer().getScheduler().scheduleSyncRepeatingTask(P.p, BPlayer::pukeTask, 1L, 1L); } - pTasks.put(player, new MutableInt(count)); + pTasks.put(player, new MutableInt(event.getCount())); } public static void pukeTask() { @@ -415,8 +442,8 @@ public class BPlayer { if (pukeRand == null) { pukeRand = new Random(); } - if (pukeItem == null || pukeItem == Material.AIR) { - pukeItem = Material.SOUL_SAND; + if (BConfig.pukeItem == null || BConfig.pukeItem == Material.AIR) { + BConfig.pukeItem = Material.SOUL_SAND; } Location loc = player.getLocation(); loc.setY(loc.getY() + 1.1); @@ -425,19 +452,20 @@ public class BPlayer { Vector direction = loc.getDirection(); direction.multiply(0.5); loc.add(direction); - Item item = player.getWorld().dropItem(loc, new ItemStack(pukeItem)); + Item item = player.getWorld().dropItem(loc, new ItemStack(BConfig.pukeItem)); item.setVelocity(direction); item.setPickupDelay(32767); // Item can never be picked up when pickup delay is 32767 //item.setTicksLived(6000 - pukeDespawntime); // Well this does not work... if (modAge) { + int pukeDespawntime = BConfig.pukeDespawntime; if (pukeDespawntime >= 5800) { return; } try { - if (gh == null) { - gh = Class.forName(P.p.getServer().getClass().getPackage().getName() + ".entity.CraftItem").getMethod("getHandle", (Class[]) null); + if (itemHandle == null) { + itemHandle = Class.forName(P.p.getServer().getClass().getPackage().getName() + ".entity.CraftItem").getMethod("getHandle", (Class[]) null); } - Object entityItem = gh.invoke(item, (Object[]) null); + Object entityItem = itemHandle.invoke(item, (Object[]) null); if (age == null) { age = entityItem.getClass().getDeclaredField("age"); age.setAccessible(true); @@ -459,13 +487,25 @@ public class BPlayer { e.printStackTrace(); } modAge = false; - P.p.errorLog("Failed to set Despawn Time on item " + pukeItem.name()); + P.p.errorLog("Failed to set Despawn Time on item " + BConfig.pukeItem.name()); } } // #### Effects #### + public static void applyEffects(List effects, Player player, PlayerEffectEvent.EffectType effectType) { + PlayerEffectEvent event = new PlayerEffectEvent(player, effectType, effects); + P.p.getServer().getPluginManager().callEvent(event); + effects = event.getEffects(); + if (event.isCancelled() || effects == null) { + return; + } + for (PotionEffect effect : effects) { + BUtil.reapplyPotionEffect(player, effect, true); + } + } + public void drunkEffects(Player player) { int duration = 10 - getQuality(); duration += drunkeness / 2; @@ -478,10 +518,22 @@ public class BPlayer { if (!P.use1_14) { duration *= 4; } - PotionEffectType.CONFUSION.createEffect(duration, 0).apply(player); + List l = new ArrayList<>(1); + l.add(PotionEffectType.CONFUSION.createEffect(duration, 0)); + + PlayerEffectEvent event = new PlayerEffectEvent(player, PlayerEffectEvent.EffectType.ALCOHOL, l); + P.p.getServer().getPluginManager().callEvent(event); + l = event.getEffects(); + if (event.isCancelled() || l == null) { + return; + } + for (PotionEffect effect : l) { + effect.apply(player); + } } - public static void addQualityEffects(int quality, int brewAlc, Player player) { + public static List getQualityEffects(int quality, int brewAlc) { + List out = new ArrayList<>(2); int duration = 7 - quality; if (quality == 0) { duration *= 125; @@ -497,7 +549,7 @@ public class BPlayer { duration *= 4; } if (duration > 0) { - Util.reapplyPotionEffect(player, PotionEffectType.POISON.createEffect(duration, 0), true); + out.add(PotionEffectType.POISON.createEffect(duration, 0)); } if (brewAlc > 10) { @@ -511,12 +563,39 @@ public class BPlayer { if (!P.use1_14) { duration *= 4; } - Util.reapplyPotionEffect(player, PotionEffectType.BLINDNESS.createEffect(duration, 0), true); + out.add(PotionEffectType.BLINDNESS.createEffect(duration, 0)); + } + return out; + } + + public static void addQualityEffects(int quality, int brewAlc, Player player) { + List list = getQualityEffects(quality, brewAlc); + PlayerEffectEvent event = new PlayerEffectEvent(player, PlayerEffectEvent.EffectType.QUALITY, list); + P.p.getServer().getPluginManager().callEvent(event); + list = event.getEffects(); + if (event.isCancelled() || list == null) { + return; + } + for (PotionEffect effect : list) { + BUtil.reapplyPotionEffect(player, effect, true); } } + public static List getBrewEffects(List effects, int quality) { + List out = new ArrayList<>(); + if (effects != null) { + for (BEffect effect : effects) { + PotionEffect e = effect.generateEffect(quality); + if (e != null) { + out.add(e); + } + } + } + return out; + } + public static void addBrewEffects(Brew brew, Player player) { - ArrayList effects = brew.getEffects(); + List effects = brew.getEffects(); if (effects != null) { for (BEffect effect : effects) { effect.apply(brew.getQuality(), player); @@ -531,8 +610,19 @@ public class BPlayer { } int amplifier = getHangoverQuality() / 3; - Util.reapplyPotionEffect(player, PotionEffectType.SLOW.createEffect(duration, amplifier), true); - Util.reapplyPotionEffect(player, PotionEffectType.HUNGER.createEffect(duration, amplifier), true); + List list = new ArrayList<>(2); + list.add(PotionEffectType.SLOW.createEffect(duration, amplifier)); + list.add(PotionEffectType.HUNGER.createEffect(duration, amplifier)); + + PlayerEffectEvent event = new PlayerEffectEvent(player, PlayerEffectEvent.EffectType.HANGOVER, list); + P.p.getServer().getPluginManager().callEvent(event); + list = event.getEffects(); + if (event.isCancelled() || list == null) { + return; + } + for (PotionEffect effect : list) { + BUtil.reapplyPotionEffect(player, effect, true); + } } @@ -545,12 +635,12 @@ public class BPlayer { if (bplayer.drunkeness > 30) { if (bplayer.offlineDrunk == 0) { - Player player = Util.getPlayerfromString(name); + Player player = BUtil.getPlayerfromString(name); if (player != null) { bplayer.drunkEffects(player); - if (enablePuke) { + if (BConfig.enablePuke) { bplayer.drunkPuke(player); } @@ -573,7 +663,7 @@ public class BPlayer { // Prevent 0 drunkeness soberPerMin++; } - if (bplayer.drain(Util.getPlayerfromString(name), soberPerMin)) { + if (bplayer.drain(BUtil.getPlayerfromString(name), soberPerMin)) { iter.remove(); } } diff --git a/src/com/dre/brewery/BRecipe.java b/src/com/dre/brewery/BRecipe.java deleted file mode 100644 index f0d997b..0000000 --- a/src/com/dre/brewery/BRecipe.java +++ /dev/null @@ -1,361 +0,0 @@ -package com.dre.brewery; - -import org.bukkit.Material; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.PotionMeta; -import org.bukkit.potion.PotionEffectType; - -import java.util.ArrayList; -import java.util.List; - -public class BRecipe { - - private String[] name; - private ArrayList ingredients = new ArrayList<>(); // material and amount - private int cookingTime; // time to cook in cauldron - private int distillruns; // runs through the brewer - private int distillTime; // time for one distill run in seconds - private byte wood; // type of wood the barrel has to consist of - private int age; // time in minecraft days for the potions to age in barrels - private String color; // color of the destilled/finished potion - private int difficulty; // difficulty to brew the potion, how exact the instruction has to be followed - private int alcohol; // Alcohol in perfect potion - private ArrayList effects = new ArrayList<>(); // Special Effects when drinking - - public BRecipe(ConfigurationSection configSectionRecipes, String recipeId) { - String nameList = configSectionRecipes.getString(recipeId + ".name"); - if (nameList != null) { - String[] name = nameList.split("/"); - if (name.length > 2) { - this.name = name; - } else { - this.name = new String[1]; - this.name[0] = name[0]; - } - } else { - return; - } - List ingredientsList = configSectionRecipes.getStringList(recipeId + ".ingredients"); - if (ingredientsList != null) { - for (String item : ingredientsList) { - String[] ingredParts = item.split("/"); - if (ingredParts.length == 2) { - String[] matParts; - if (ingredParts[0].contains(",")) { - matParts = ingredParts[0].split(","); - } else if (ingredParts[0].contains(":")) { - matParts = ingredParts[0].split(":"); - } else if (ingredParts[0].contains(";")) { - matParts = ingredParts[0].split(";"); - } else { - matParts = ingredParts[0].split("\\."); - } - Material mat = Material.matchMaterial(matParts[0]); - short durability = -1; - if (matParts.length == 2) { - durability = (short) P.p.parseInt(matParts[1]); - } - if (mat == null && P.p.hasVault) { - try { - net.milkbowl.vault.item.ItemInfo vaultItem = net.milkbowl.vault.item.Items.itemByString(matParts[0]); - if (vaultItem != null) { - mat = vaultItem.getType(); - if (durability == -1 && vaultItem.getSubTypeId() != 0) { - durability = vaultItem.getSubTypeId(); - } - if (mat.name().contains("LEAVES")) { - if (durability > 3) { - durability -= 4; // Vault has leaves with higher durability - } - } - } - } catch (Exception e) { - P.p.errorLog("Could not check vault for Item Name"); - e.printStackTrace(); - } - } - if (mat != null) { - ItemStack stack = new ItemStack(mat, P.p.parseInt(ingredParts[1]), durability); - this.ingredients.add(stack); - BIngredients.possibleIngredients.add(mat); - } else { - P.p.errorLog("Unknown Material: " + ingredParts[0]); - this.ingredients = null; - return; - } - } else { - return; - } - } - } - this.cookingTime = configSectionRecipes.getInt(recipeId + ".cookingtime", 1); - this.distillruns = configSectionRecipes.getInt(recipeId + ".distillruns", 0); - this.distillTime = configSectionRecipes.getInt(recipeId + ".distilltime", 0) * 20; - this.wood = (byte) configSectionRecipes.getInt(recipeId + ".wood", 0); - this.age = configSectionRecipes.getInt(recipeId + ".age", 0); - this.color = configSectionRecipes.getString(recipeId + ".color"); - this.difficulty = configSectionRecipes.getInt(recipeId + ".difficulty", 0); - this.alcohol = configSectionRecipes.getInt(recipeId + ".alcohol", 0); - - List effectStringList = configSectionRecipes.getStringList(recipeId + ".effects"); - if (effectStringList != null) { - for (String effectString : effectStringList) { - BEffect effect = new BEffect(effectString); - if (effect.isValid()) { - effects.add(effect); - } else { - P.p.errorLog("Error adding Effect to Recipe: " + getName(5)); - } - } - } - } - - // check every part of the recipe for validity - public boolean isValid() { - if (name == null || name.length < 1) { - P.p.errorLog("Recipe Name missing or invalid!"); - return false; - } - if (getName(5) == null || getName(5).length() < 1) { - P.p.errorLog("Recipe Name invalid"); - return false; - } - if (ingredients == null || ingredients.isEmpty()) { - P.p.errorLog("No ingredients could be loaded for Recipe: " + getName(5)); - return false; - } - if (cookingTime < 1) { - P.p.errorLog("Invalid cooking time '" + cookingTime + "' in Recipe: " + getName(5)); - return false; - } - if (distillruns < 0) { - P.p.errorLog("Invalid distillruns '" + distillruns + "' in Recipe: " + getName(5)); - return false; - } - if (distillTime < 0) { - P.p.errorLog("Invalid distilltime '" + distillTime + "' in Recipe: " + getName(5)); - return false; - } - if (wood < 0 || wood > 6) { - P.p.errorLog("Invalid wood type '" + wood + "' in Recipe: " + getName(5)); - return false; - } - if (age < 0) { - P.p.errorLog("Invalid age time '" + age + "' in Recipe: " + getName(5)); - return false; - } - String c = getColor(); - if (!c.equals("WATER") && Brew.PotionColor.fromString(c) == Brew.PotionColor.WATER) { - P.p.errorLog("Invalid Color '" + color + "' in Recipe: " + getName(5)); - return false; - } - if (difficulty < 0 || difficulty > 10) { - P.p.errorLog("Invalid difficulty '" + difficulty + "' in Recipe: " + getName(5)); - return false; - } - if (alcohol < 0) { - P.p.errorLog("Invalid alcohol '" + alcohol + "' in Recipe: " + getName(5)); - return false; - } - return true; - } - - // allowed deviation to the recipes count of ingredients at the given difficulty - public int allowedCountDiff(int count) { - if (count < 8) { - count = 8; - } - int allowedCountDiff = Math.round((float) ((11.0 - difficulty) * (count / 10.0))); - - if (allowedCountDiff == 0) { - return 1; - } - return allowedCountDiff; - } - - // allowed deviation to the recipes cooking-time at the given difficulty - public int allowedTimeDiff(int time) { - if (time < 8) { - time = 8; - } - int allowedTimeDiff = Math.round((float) ((11.0 - difficulty) * (time / 10.0))); - - if (allowedTimeDiff == 0) { - return 1; - } - return allowedTimeDiff; - } - - // difference between given and recipe-wanted woodtype - public float getWoodDiff(float wood) { - return Math.abs(wood - this.wood); - } - - public boolean isCookingOnly() { - return age == 0 && distillruns == 0; - } - - public boolean needsDistilling() { - return distillruns != 0; - } - - public boolean needsToAge() { - return age != 0; - } - - // true if given list misses an ingredient - public boolean isMissingIngredients(List list) { - if (list.size() < ingredients.size()) { - return true; - } - for (ItemStack ingredient : ingredients) { - boolean matches = false; - for (ItemStack used : list) { - if (ingredientsMatch(used, ingredient)) { - matches = true; - break; - } - } - if (!matches) { - return true; - } - } - return false; - } - - // Returns true if this ingredient cares about durability - public boolean hasExactData(ItemStack item) { - for (ItemStack ingredient : ingredients) { - if (ingredient.getType().equals(item.getType())) { - return ingredient.getDurability() != -1; - } - } - return true; - } - - // Returns true if this item matches the item from a recipe - public static boolean ingredientsMatch(ItemStack usedItem, ItemStack recipeItem) { - if (!recipeItem.getType().equals(usedItem.getType())) { - return false; - } - return recipeItem.getDurability() == -1 || recipeItem.getDurability() == usedItem.getDurability(); - } - - // Create a Potion from this Recipe with best values. Quality can be set, but will reset to 10 if put in a barrel - public ItemStack create(int quality) { - ItemStack potion = new ItemStack(Material.POTION); - PotionMeta potionMeta = (PotionMeta) potion.getItemMeta(); - - int uid = Brew.generateUID(); - - ArrayList list = new ArrayList<>(ingredients.size()); - for (ItemStack item : ingredients) { - if (item.getDurability() == -1) { - list.add(new ItemStack(item.getType(), item.getAmount())); - } else { - list.add(item.clone()); - } - } - - BIngredients bIngredients = new BIngredients(list, cookingTime); - - Brew brew = new Brew(uid, bIngredients, quality, distillruns, getAge(), wood, getName(5), false, false, true, 0); - - Brew.PotionColor.fromString(getColor()).colorBrew(potionMeta, potion, false); - potionMeta.setDisplayName(P.p.color("&f" + getName(quality))); - if (!P.use1_14) { - // Before 1.14 the effects duration would strangely be only a quarter of what we tell it to be - // This is due to the Duration Modifier, that is removed in 1.14 - uid *= 4; - } - // This effect stores the UID in its Duration - potionMeta.addCustomEffect((PotionEffectType.REGENERATION).createEffect(uid, 0), true); - - brew.convertLore(potionMeta, false); - Brew.addOrReplaceEffects(potionMeta, effects, quality); - brew.touch(); - - potion.setItemMeta(potionMeta); - return potion; - } - - - // Getter - - // how many of a specific ingredient in the recipe - public int amountOf(ItemStack item) { - for (ItemStack ingredient : ingredients) { - if (ingredientsMatch(item, ingredient)) { - return ingredient.getAmount(); - } - } - return 0; - } - - // name that fits the quality - public String getName(int quality) { - if (name.length > 2) { - if (quality <= 3) { - return name[0]; - } else if (quality <= 7) { - return name[1]; - } else { - return name[2]; - } - } else { - return name[0]; - } - } - - // If one of the quality names equalIgnoreCase given name - public boolean hasName(String name) { - for (String test : this.name) { - if (test.equalsIgnoreCase(name)) { - return true; - } - } - return false; - } - - public int getCookingTime() { - return cookingTime; - } - - public int getDistillRuns() { - return distillruns; - } - - public int getDistillTime() { - return distillTime; - } - - public String getColor() { - if (color != null) { - return color.toUpperCase(); - } - return "BLUE"; - } - - // get the woodtype - public byte getWood() { - return wood; - } - - public float getAge() { - return (float) age; - } - - public int getDifficulty() { - return difficulty; - } - - public int getAlcohol() { - return alcohol; - } - - public ArrayList getEffects() { - return effects; - } - -} diff --git a/src/com/dre/brewery/Barrel.java b/src/com/dre/brewery/Barrel.java index fb3d603..e94a680 100644 --- a/src/com/dre/brewery/Barrel.java +++ b/src/com/dre/brewery/Barrel.java @@ -1,11 +1,19 @@ package com.dre.brewery; -import com.dre.brewery.integration.CitadelBarrel; -import com.dre.brewery.integration.GriefPreventionBarrel; -import com.dre.brewery.integration.LWCBarrel; -import com.dre.brewery.integration.LogBlockBarrel; -import org.apache.commons.lang.ArrayUtils; +import com.dre.brewery.api.events.barrel.BarrelAccessEvent; +import com.dre.brewery.api.events.barrel.BarrelCreateEvent; +import com.dre.brewery.api.events.barrel.BarrelDestroyEvent; +import com.dre.brewery.api.events.barrel.BarrelRemoveEvent; +import com.dre.brewery.filedata.BConfig; +import com.dre.brewery.integration.barrel.LogBlockBarrel; +import com.dre.brewery.lore.BrewLore; +import com.dre.brewery.utility.BUtil; +import com.dre.brewery.utility.BoundingBox; +import com.dre.brewery.utility.LegacyUtil; +import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.SoundCategory; import org.bukkit.block.Block; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.HumanEntity; @@ -15,40 +23,47 @@ import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.PotionMeta; -import org.bukkit.plugin.Plugin; import org.bukkit.scheduler.BukkitRunnable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.concurrent.CopyOnWriteArrayList; +/** + * A Multi Block Barrel with Inventory + */ public class Barrel implements InventoryHolder { - public static CopyOnWriteArrayList barrels = new CopyOnWriteArrayList<>(); - private static int check = 0; + public static List barrels = new ArrayList<>(); + private static int check = 0; // Which Barrel was last checked - private Block spigot; - private int[] woodsloc = null; // location of wood Blocks - private int[] stairsloc = null; // location of stair Blocks - private byte signoffset; - private boolean checked; + private final Block spigot; + private final BarrelBody body; // The Blocks that make up a Barrel in the World + private boolean checked; // Checked by the random BarrelCheck routine private Inventory inventory; private float time; public Barrel(Block spigot, byte signoffset) { this.spigot = spigot; - this.signoffset = signoffset; + if (isLarge()) { + inventory = P.p.getServer().createInventory(this, 27, P.p.languageReader.get("Etc_Barrel")); + } else { + inventory = P.p.getServer().createInventory(this, 9, P.p.languageReader.get("Etc_Barrel")); + } + body = new BarrelBody(this, signoffset); } - // load from file - public Barrel(Block spigot, byte sign, String[] st, String[] wo, Map items, float time) { + /** + * load from file + */ + public Barrel(Block spigot, byte sign, BoundingBox bounds, Map items, float time) { this.spigot = spigot; - this.signoffset = sign; if (isLarge()) { - this.inventory = org.bukkit.Bukkit.createInventory(this, 27, P.p.languageReader.get("Etc_Barrel")); + this.inventory = P.p.getServer().createInventory(this, 27, P.p.languageReader.get("Etc_Barrel")); } else { - this.inventory = org.bukkit.Bukkit.createInventory(this, 9, P.p.languageReader.get("Etc_Barrel")); + this.inventory = P.p.getServer().createInventory(this, 9, P.p.languageReader.get("Etc_Barrel")); } if (items != null) { for (String slot : items.keySet()) { @@ -59,34 +74,7 @@ public class Barrel implements InventoryHolder { } this.time = time; - int i = 0; - if (wo.length > 1) { - woodsloc = new int[wo.length]; - for (String wos : wo) { - woodsloc[i] = P.p.parseInt(wos); - i++; - } - i = 0; - } - if (st.length > 1) { - stairsloc = new int[st.length]; - for (String sts : st) { - stairsloc[i] = P.p.parseInt(sts); - i++; - } - } - - if (woodsloc == null && stairsloc == null) { - // If loading from old data, or block locations are missing, regenerate them - // This will only be done in those extreme cases. - Block broken = getBrokenBlock(true); - if (broken != null) { - remove(broken, null); - return; - } - } - - barrels.add(this); + body = new BarrelBody(this, sign, bounds); } public static void onUpdate() { @@ -94,13 +82,21 @@ public class Barrel implements InventoryHolder { // Minecraft day is 20 min, so add 1/20 to the time every minute barrel.time += (1.0 / 20.0); } - if (check == 0 && barrels.size() > 0) { - Barrel random = barrels.get((int) Math.floor(Math.random() * barrels.size())); + int numBarrels = barrels.size(); + if (check == 0 && numBarrels > 0) { + Barrel random = barrels.get((int) Math.floor(Math.random() * numBarrels)); if (random != null) { // You have been selected for a random search // We want to check at least one barrel every time random.checked = false; } + if (numBarrels > 50) { + Barrel randomInTheBack = barrels.get(numBarrels - 1 - (int) (Math.random() * (numBarrels >>> 2))); + if (randomInTheBack != null) { + // Prioritize checking one of the less recently used barrels as well + randomInTheBack.checked = false; + } + } new BarrelCheck().runTaskTimer(P.p, 1, 1); } } @@ -118,154 +114,36 @@ public class Barrel implements InventoryHolder { } } - if (P.p.useWG) { - Plugin plugin = P.p.getServer().getPluginManager().getPlugin("WorldGuard"); - if (plugin != null) { - try { - if (!P.p.wg.checkAccess(player, spigot, plugin)) { - return false; - } - } catch (Throwable e) { - P.p.errorLog("Failed to Check WorldGuard for Barrel Open Permissions!"); - P.p.errorLog("Brewery was tested with version 5.8, 6.1 to 7.0 of WorldGuard!"); - P.p.errorLog("Disable the WorldGuard support in the config and do /brew reload"); - e.printStackTrace(); - if (player.hasPermission("brewery.admin") || player.hasPermission("brewery.mod")) { - P.p.msg(player, "&cWorldGuard check Error, Brewery was tested with up to v7.0 of Worldguard"); - P.p.msg(player, "&cSet &7useWorldGuard: false &cin the config and /brew reload"); - } else { - P.p.msg(player, "&cError opening Barrel, please report to an Admin!"); - } - return false; - } - } - } - - if (P.p.useGP) { - if (P.p.getServer().getPluginManager().isPluginEnabled("GriefPrevention")) { - try { - if (!GriefPreventionBarrel.checkAccess(player, spigot)) { - return false; - } - } catch (Throwable e) { - P.p.errorLog("Failed to Check GriefPrevention for Barrel Open Permissions!"); - P.p.errorLog("Brewery was tested with GriefPrevention v14.5 - v16.9"); - P.p.errorLog("Disable the GriefPrevention support in the config and do /brew reload"); - e.printStackTrace(); - if (player.hasPermission("brewery.admin") || player.hasPermission("brewery.mod")) { - P.p.msg(player, "&cGriefPrevention check Error, Brewery was tested with up to v16.9 of GriefPrevention"); - P.p.msg(player, "&cSet &7useGriefPrevention: false &cin the config and /brew reload"); - } else { - P.p.msg(player, "&cError opening Barrel, please report to an Admin!"); - } - return false; - } - } - } - - if (event != null && P.p.useLWC) { - Plugin plugin = P.p.getServer().getPluginManager().getPlugin("LWC"); - if (plugin != null) { - - // If the Clicked Block was the Sign, LWC already knows and we dont need to do anything here - if (!LegacyUtil.isSign(event.getClickedBlock().getType())) { - Block sign = getSignOfSpigot(); - // If the Barrel does not have a Sign, it cannot be locked - if (!sign.equals(event.getClickedBlock())) { - try { - return LWCBarrel.checkAccess(player, sign, event, plugin); - } catch (Throwable e) { - P.p.errorLog("Failed to Check LWC for Barrel Open Permissions!"); - P.p.errorLog("Brewery was tested with version 4.5.0 of LWC!"); - P.p.errorLog("Disable the LWC support in the config and do /brew reload"); - e.printStackTrace(); - if (player.hasPermission("brewery.admin") || player.hasPermission("brewery.mod")) { - P.p.msg(player, "&cLWC check Error, Brewery was tested with up to v4.5.0 of LWC"); - P.p.msg(player, "&cSet &7useLWC: false &cin the config and /brew reload"); - } else { - P.p.msg(player, "&cError opening Barrel, please report to an Admin!"); - } - return false; - } - } - } - } - } - - if (event != null && P.p.useCitadel) { - Plugin plugin = P.p.getServer().getPluginManager().getPlugin("Citadel"); - if (plugin != null) { - try { - if (LegacyUtil.isSign(event.getClickedBlock().getType())) { - return CitadelBarrel.checkAccess(player, getSignOfSpigot()); - } else { - return CitadelBarrel.checkAccess(player, spigot); - } - } catch (Throwable e) { - P.p.errorLog("Failed to Check Citadel for Container Access Permissions!"); - P.p.errorLog("Brewery was tested with version 3.9.1 of Citadel!"); - P.p.errorLog("Disable Citadel support in the config and do /brew reload"); - e.printStackTrace(); - if (player.hasPermission("brewery.admin") || player.hasPermission("brewery.mod")) { - P.p.msg(player, "&cCitadel check Error, Brewery was tested with up to v3.9.1 of Citadel"); - } else { - P.p.msg(player, "&cError opening Barrel, please report to an Admin!"); - } - return false; - } - } + // Call event + BarrelAccessEvent accessEvent = new BarrelAccessEvent(this, player, event.getClickedBlock()); + // Listened to by WGBarrel7, WGBarrelNew, WGBarrelOld, GriefPreventionBarrel (IntegrationListener) + P.p.getServer().getPluginManager().callEvent(accessEvent); + if (accessEvent.isCancelled()) { + return false; } return true; } - // Ask for permission to destroy barrel, remove protection if has - public boolean hasPermsDestroy(Player player) { - if (player == null) { - willDestroy(); - return true; - } - if (P.p.useLWC) { - try { - return LWCBarrel.checkDestroy(player, this); - } catch (Throwable e) { - P.p.errorLog("Failed to Check LWC for Barrel Break Permissions!"); - P.p.errorLog("Brewery was tested with version 4.5.0 of LWC!"); - P.p.errorLog("Disable the LWC support in the config and do /brew reload"); - e.printStackTrace(); - if (player.hasPermission("brewery.admin") || player.hasPermission("brewery.mod")) { - P.p.msg(player, "&cLWC check Error, Brewery was tested with up to v4.5.0 of LWC"); - P.p.msg(player, "&cSet &7useLWC: false &cin the config and /brew reload"); - } else { - P.p.msg(player, "&cError breaking Barrel, please report to an Admin!"); - } - return false; - } - } - - return true; + /** + * Ask for permission to destroy barrel + */ + public boolean hasPermsDestroy(Player player, Block block, BarrelDestroyEvent.Reason reason) { + // Listened to by LWCBarrel (IntegrationListener) + BarrelDestroyEvent destroyEvent = new BarrelDestroyEvent(this, block, reason, player); + P.p.getServer().getPluginManager().callEvent(destroyEvent); + return !destroyEvent.isCancelled(); } - // If something other than the Player is destroying the barrel, inform protection plugins - public void willDestroy() { - if (P.p.useLWC) { - try { - LWCBarrel.remove(this); - } catch (Throwable e) { - P.p.errorLog("Failed to Remove LWC Lock from Barrel!"); - P.p.errorLog("Brewery was tested with version 4.5.0 of LWC!"); - e.printStackTrace(); - } - } - } - - // player opens the barrel + /** + * player opens the barrel + */ public void open(Player player) { if (inventory == null) { if (isLarge()) { - inventory = org.bukkit.Bukkit.createInventory(this, 27, P.p.languageReader.get("Etc_Barrel")); + inventory = P.p.getServer().createInventory(this, 27, P.p.languageReader.get("Etc_Barrel")); } else { - inventory = org.bukkit.Bukkit.createInventory(this, 9, P.p.languageReader.get("Etc_Barrel")); + inventory = P.p.getServer().createInventory(this, 9, P.p.languageReader.get("Etc_Barrel")); } } else { if (time > 0) { @@ -273,7 +151,7 @@ public class Barrel implements InventoryHolder { if (inventory.getViewers().isEmpty()) { // if inventory contains potions if (inventory.contains(Material.POTION)) { - byte wood = getWood(); + byte wood = body.getWood(); long loadTime = System.nanoTime(); for (ItemStack item : inventory.getContents()) { if (item != null) { @@ -293,7 +171,7 @@ public class Barrel implements InventoryHolder { // reset barreltime, potions have new age time = 0; - if (P.p.useLB) { + if (BConfig.useLB) { try { LogBlockBarrel.openBarrel(player, inventory, spigot.getLocation()); } catch (Throwable e) { @@ -305,78 +183,78 @@ public class Barrel implements InventoryHolder { player.openInventory(inventory); } + public void playOpeningSound() { + float randPitch = (float) (Math.random() * 0.1); + Location location = getSpigot().getLocation(); + if (location.getWorld() == null) return; + if (isLarge()) { + location.getWorld().playSound(location, Sound.BLOCK_CHEST_OPEN, SoundCategory.BLOCKS, 0.4f, 0.55f + randPitch); + //getSpigot().getWorld().playSound(getSpigot().getLocation(), Sound.ITEM_BUCKET_EMPTY, SoundCategory.BLOCKS, 0.5f, 0.6f + randPitch); + location.getWorld().playSound(location, Sound.BLOCK_BREWING_STAND_BREW, SoundCategory.BLOCKS, 0.4f, 0.45f + randPitch); + } else { + location.getWorld().playSound(location, Sound.BLOCK_BARREL_OPEN, SoundCategory.BLOCKS, 0.5f, 0.8f + randPitch); + } + } + + public void playClosingSound() { + float randPitch = (float) (Math.random() * 0.1); + Location location = getSpigot().getLocation(); + if (location.getWorld() == null) return; + if (isLarge()) { + location.getWorld().playSound(location, Sound.BLOCK_BARREL_CLOSE, SoundCategory.BLOCKS, 0.5f, 0.5f + randPitch); + location.getWorld().playSound(location, Sound.ITEM_BUCKET_EMPTY, SoundCategory.BLOCKS, 0.2f, 0.6f + randPitch); + } else { + location.getWorld().playSound(location, Sound.BLOCK_BARREL_CLOSE, SoundCategory.BLOCKS, 0.5f, 0.8f + randPitch); + } + } + @Override + @NotNull public Inventory getInventory() { return inventory; } + @NotNull public Block getSpigot() { return spigot; } - // Returns true if this Block is part of this Barrel + @NotNull + public BarrelBody getBody() { + return body; + } + + public float getTime() { + return time; + } + + /** + * Returns true if this Block is part of this Barrel + */ public boolean hasBlock(Block block) { - if (block != null) { - if (LegacyUtil.isWoodPlanks(block.getType())) { - return hasWoodBlock(block); - } else if (LegacyUtil.isWoodStairs(block.getType())) { - return hasStairsBlock(block); - } - } - return false; + return body.hasBlock(block); } + /** + * @deprecated just use hasBlock + */ + @Deprecated public boolean hasWoodBlock(Block block) { - if (woodsloc != null) { - if (spigot.getWorld() != null && spigot.getWorld().equals(block.getWorld())) { - if (woodsloc.length > 2) { - int x = block.getX(); - if (Math.abs(x - woodsloc[0]) < 10) { - for (int i = 0; i < woodsloc.length - 2; i += 3) { - if (woodsloc[i] == x) { - if (woodsloc[i + 1] == block.getY()) { - if (woodsloc[i + 2] == block.getZ()) { - return true; - } - } - } - } - } - } - } - } - return false; + return body.hasBlock(block); } + /** + * @deprecated just use hasBlock + */ + @Deprecated public boolean hasStairsBlock(Block block) { - if (stairsloc != null) { - if (spigot.getWorld() != null && spigot.getWorld().equals(block.getWorld())) { - if (stairsloc.length > 2) { - int x = block.getX(); - if (Math.abs(x - stairsloc[0]) < 10) { - for (int i = 0; i < stairsloc.length - 2; i += 3) { - if (stairsloc[i] == x) { - if (stairsloc[i + 1] == block.getY()) { - if (stairsloc[i + 2] == block.getZ()) { - return true; - } - } - } - } - } - } - } - } - return false; + return body.hasBlock(block); } - // Returns true if the Offset of the clicked Sign matches the Barrel. - // This prevents adding another sign to the barrel and clicking that. - public boolean isSignOfBarrel(byte offset) { - return offset == 0 || signoffset == 0 || signoffset == offset; - } - - // Get the Barrel by Block, null if that block is not part of a barrel + /** + * Get the Barrel by Block, null if that block is not part of a barrel + */ + @Nullable public static Barrel get(Block block) { if (block == null) { return null; @@ -384,57 +262,73 @@ public class Barrel implements InventoryHolder { Material type = block.getType(); if (LegacyUtil.isFence(type) || LegacyUtil.isSign(type) ) { return getBySpigot(block); - } else if (LegacyUtil.isWoodPlanks(type) || LegacyUtil.isWoodStairs(type)) { + } else { return getByWood(block); } - return null; } - // Get the Barrel by Sign or Spigot (Fastest) + /** + * Get the Barrel by Sign or Spigot (Fastest) + */ + @Nullable public static Barrel getBySpigot(Block sign) { // convert spigot if neccessary - Block spigot = getSpigotOfSign(sign); + Block spigot = BarrelBody.getSpigotOfSign(sign); byte signoffset = 0; if (!spigot.equals(sign)) { signoffset = (byte) (sign.getY() - spigot.getY()); } + int i = 0; for (Barrel barrel : barrels) { - if (barrel.isSignOfBarrel(signoffset)) { + if (barrel.body.isSignOfBarrel(signoffset)) { if (barrel.spigot.equals(spigot)) { - if (barrel.signoffset == 0 && signoffset != 0) { + if (barrel.body.getSignoffset() == 0 && signoffset != 0) { // Barrel has no signOffset even though we clicked a sign, may be old - barrel.signoffset = signoffset; + barrel.body.setSignoffset(signoffset); } + moveMRU(i); return barrel; } } + i++; } return null; } - // Get the barrel by its corpus (Wood Planks, Stairs) + /** + * Get the barrel by its corpus (Wood Planks, Stairs) + */ + @Nullable public static Barrel getByWood(Block wood) { - if (LegacyUtil.isWoodPlanks(wood.getType())) { + if (LegacyUtil.isWoodPlanks(wood.getType()) || LegacyUtil.isWoodStairs(wood.getType())) { + int i = 0; for (Barrel barrel : barrels) { - if (barrel.hasWoodBlock(wood)) { - return barrel; - } - } - } else if (LegacyUtil.isWoodStairs(wood.getType())) { - for (Barrel barrel : Barrel.barrels) { - if (barrel.hasStairsBlock(wood)) { + if (barrel.getSpigot().getWorld().equals(wood.getWorld()) && barrel.body.getBounds().contains(wood)) { + moveMRU(i); return barrel; } + i++; } } return null; } - // creates a new Barrel out of a sign + // Move Barrel that was recently used more towards the front of the List + // Optimizes retrieve by Block over time + private static void moveMRU(int index) { + if (index > 0) { + // Swap entry at the index with the one next to it + barrels.set(index - 1, barrels.set(index, barrels.get(index - 1))); + } + } + + /** + * creates a new Barrel out of a sign + */ public static boolean create(Block sign, Player player) { - Block spigot = getSpigotOfSign(sign); + Block spigot = BarrelBody.getSpigotOfSign(sign); byte signoffset = 0; if (!spigot.equals(sign)) { @@ -444,7 +338,7 @@ public class Barrel implements InventoryHolder { Barrel barrel = getBySpigot(spigot); if (barrel == null) { barrel = new Barrel(spigot, signoffset); - if (barrel.getBrokenBlock(true) == null) { + if (barrel.body.getBrokenBlock(true) == null) { if (LegacyUtil.isSign(spigot.getType())) { if (!player.hasPermission("brewery.createbarrel.small")) { P.p.msg(player, P.p.languageReader.get("Perms_NoSmallBarrelCreate")); @@ -456,29 +350,43 @@ public class Barrel implements InventoryHolder { return false; } } - barrels.add(barrel); - return true; + BarrelCreateEvent createEvent = new BarrelCreateEvent(barrel, player); + P.p.getServer().getPluginManager().callEvent(createEvent); + if (!createEvent.isCancelled()) { + barrels.add(0, barrel); + return true; + } } } else { - if (barrel.signoffset == 0 && signoffset != 0) { - barrel.signoffset = signoffset; + if (barrel.body.getSignoffset() == 0 && signoffset != 0) { + barrel.body.setSignoffset(signoffset); return true; } } return false; } - // removes a barrel, throwing included potions to the ground - public void remove(Block broken, Player breaker) { + /** + * Removes a barrel, throwing included potions to the ground + * + * @param broken The Block that was broken + * @param breaker The Player that broke it, or null if not known + * @param dropItems If the items in the barrels inventory should drop to the ground + */ + public void remove(@Nullable Block broken, @Nullable Player breaker, boolean dropItems) { + BarrelRemoveEvent event = new BarrelRemoveEvent(this, dropItems); + // Listened to by LWCBarrel (IntegrationListener) + P.p.getServer().getPluginManager().callEvent(event); + if (inventory != null) { - List viewers = new ArrayList(inventory.getViewers()); + List viewers = new ArrayList<>(inventory.getViewers()); // Copy List to fix ConcModExc for (HumanEntity viewer : viewers) { viewer.closeInventory(); } ItemStack[] items = inventory.getContents(); inventory.clear(); - if (P.p.useLB && breaker != null) { + if (BConfig.useLB && breaker != null) { try { LogBlockBarrel.breakBarrel(breaker, items, spigot.getLocation()); } catch (Throwable e) { @@ -487,23 +395,27 @@ public class Barrel implements InventoryHolder { e.printStackTrace(); } } - for (ItemStack item : items) { - if (item != null) { - Brew brew = Brew.get(item); - if (brew != null) { - // Brew before throwing - brew.age(item, time, getWood()); - PotionMeta meta = (PotionMeta) item.getItemMeta(); - if (Brew.hasColorLore(meta)) { - brew.convertLore(meta, false); - item.setItemMeta(meta); + if (event.willDropItems()) { + for (ItemStack item : items) { + if (item != null) { + Brew brew = Brew.get(item); + if (brew != null) { + // Brew before throwing + brew.age(item, time, body.getWood()); + PotionMeta meta = (PotionMeta) item.getItemMeta(); + if (BrewLore.hasColorLore(meta)) { + BrewLore lore = new BrewLore(brew, meta); + lore.convertLore(false); + lore.write(); + item.setItemMeta(meta); + } + } + // "broken" is the block that destroyed, throw them there! + if (broken != null) { + broken.getWorld().dropItem(broken.getLocation(), item); + } else { + spigot.getWorld().dropItem(spigot.getLocation(), item); } - } - // "broken" is the block that destroyed, throw them there! - if (broken != null) { - broken.getWorld().dropItem(broken.getLocation(), item); - } else { - spigot.getWorld().dropItem(spigot.getLocation(), item); } } } @@ -512,23 +424,56 @@ public class Barrel implements InventoryHolder { barrels.remove(this); } - //unloads barrels that are in a unloading world + /** + * is this a Large barrel? + */ + public boolean isLarge() { + return !isSmall(); + } + + /** + * is this a Small barrel? + */ + public boolean isSmall() { + return LegacyUtil.isSign(spigot.getType()); + } + + /** + * returns the Sign of a large barrel, the spigot if there is none + */ + public Block getSignOfSpigot() { + return body.getSignOfSpigot(); + } + + /** + * returns the fence above/below a block, itself if there is none + */ + public static Block getSpigotOfSign(Block block) { + return BarrelBody.getSpigotOfSign(block); + } + + /** + * returns null if Barrel is correctly placed; the block that is missing when not. + *

The barrel needs to be formed correctly + * + * @param force to also check even if chunk is not loaded + */ + public Block getBrokenBlock(boolean force) { + return body.getBrokenBlock(force); + } + + /** + * unloads barrels that are in a unloading world + */ public static void onUnload(String name) { - for (Barrel barrel : barrels) { - if (barrel.spigot.getWorld().getName().equals(name)) { - barrels.remove(barrel); - } - } + barrels.removeIf(barrel -> barrel.spigot.getWorld().getName().equals(name)); } - // If the Sign of a Large Barrel gets destroyed, set signOffset to 0 - public void destroySign() { - signoffset = 0; - } - - // Saves all data + /** + * Saves all data + */ public static void save(ConfigurationSection config, ConfigurationSection oldData) { - Util.createWorldSections(config); + BUtil.createWorldSections(config); if (!barrels.isEmpty()) { int id = 0; @@ -538,7 +483,7 @@ public class Barrel implements InventoryHolder { String prefix; if (worldName.startsWith("DXL_")) { - prefix = Util.getDxlName(worldName) + "." + id; + prefix = BUtil.getDxlName(worldName) + "." + id; } else { prefix = barrel.spigot.getWorld().getUID().toString() + "." + id; } @@ -546,23 +491,8 @@ public class Barrel implements InventoryHolder { // block: x/y/z config.set(prefix + ".spigot", barrel.spigot.getX() + "/" + barrel.spigot.getY() + "/" + barrel.spigot.getZ()); - if (barrel.signoffset != 0) { - config.set(prefix + ".sign", barrel.signoffset); - } - if (barrel.stairsloc != null && barrel.stairsloc.length > 0) { - StringBuilder st = new StringBuilder(); - for (int i : barrel.stairsloc) { - st.append(i).append(","); - } - config.set(prefix + ".st", st.substring(0, st.length() - 1)); - } - if (barrel.woodsloc != null && barrel.woodsloc.length > 0) { - StringBuilder wo = new StringBuilder(); - for (int i : barrel.woodsloc) { - wo.append(i).append(","); - } - config.set(prefix + ".wo", wo.substring(0, wo.length() - 1)); - } + // save the body data into the section as well + barrel.body.save(config, prefix); if (barrel.inventory != null) { int slot = 0; @@ -599,280 +529,6 @@ public class Barrel implements InventoryHolder { } } - // direction of the barrel from the spigot - public static int getDirection(Block spigot) { - int direction = 0;// 1=x+ 2=x- 3=z+ 4=z- - Material type = spigot.getRelative(0, 0, 1).getType(); - if (LegacyUtil.isWoodPlanks(type) || LegacyUtil.isWoodStairs(type)) { - direction = 3; - } - type = spigot.getRelative(0, 0, -1).getType(); - if (LegacyUtil.isWoodPlanks(type) || LegacyUtil.isWoodStairs(type)) { - if (direction == 0) { - direction = 4; - } else { - return 0; - } - } - type = spigot.getRelative(1, 0, 0).getType(); - if (LegacyUtil.isWoodPlanks(type) || LegacyUtil.isWoodStairs(type)) { - if (direction == 0) { - direction = 1; - } else { - return 0; - } - } - type = spigot.getRelative(-1, 0, 0).getType(); - if (LegacyUtil.isWoodPlanks(type) || LegacyUtil.isWoodStairs(type)) { - if (direction == 0) { - direction = 2; - } else { - return 0; - } - } - return direction; - } - - // is this a Large barrel? - public boolean isLarge() { - return !LegacyUtil.isSign(spigot.getType()); - } - - // woodtype of the block the spigot is attached to - public byte getWood() { - Block wood; - switch (getDirection(spigot)) { // 1=x+ 2=x- 3=z+ 4=z- - case 0: - return 0; - case 1: - wood = spigot.getRelative(1, 0, 0); - break; - case 2: - wood = spigot.getRelative(-1, 0, 0); - break; - case 3: - wood = spigot.getRelative(0, 0, 1); - break; - default: - wood = spigot.getRelative(0, 0, -1); - } - try { - return LegacyUtil.getWoodType(wood); - } catch (NoSuchFieldError | NoClassDefFoundError noSuchFieldError) { - // Using older minecraft versions some fields and classes do not exist - return 0; - } - } - - // returns the Sign of a large barrel, the spigot if there is none - public Block getSignOfSpigot() { - if (signoffset != 0) { - if (LegacyUtil.isSign(spigot.getType())) { - return spigot; - } - - if (LegacyUtil.isSign(spigot.getRelative(0, signoffset, 0).getType())) { - return spigot.getRelative(0, signoffset, 0); - } else { - signoffset = 0; - } - } - return spigot; - } - - // returns the fence above/below a block, itself if there is none - public static Block getSpigotOfSign(Block block) { - - int y = -2; - while (y <= 1) { - // Fence and Netherfence - Block relative = block.getRelative(0, y, 0); - if (LegacyUtil.isFence(relative.getType())) { - return (relative); - } - y++; - } - return block; - } - - // returns null if Barrel is correctly placed; the block that is missing when not - // the barrel needs to be formed correctly - // flag force to also check if chunk is not loaded - public Block getBrokenBlock(boolean force) { - if (force || Util.isChunkLoaded(spigot)) { - spigot = getSpigotOfSign(spigot); - if (LegacyUtil.isSign(spigot.getType())) { - return checkSBarrel(); - } else { - return checkLBarrel(); - } - } - return null; - } - - public Block checkSBarrel() { - int direction = getDirection(spigot);// 1=x+ 2=x- 3=z+ 4=z- - if (direction == 0) { - return spigot; - } - int startX; - int startZ; - int endX; - int endZ; - - ArrayList stairs = new ArrayList<>(); - - if (direction == 1) { - startX = 1; - endX = startX + 1; - startZ = -1; - endZ = 0; - } else if (direction == 2) { - startX = -2; - endX = startX + 1; - startZ = 0; - endZ = 1; - } else if (direction == 3) { - startX = 0; - endX = 1; - startZ = 1; - endZ = startZ + 1; - } else { - startX = -1; - endX = 0; - startZ = -2; - endZ = startZ + 1; - } - - Material type; - int x = startX; - int y = 0; - int z = startZ; - while (y <= 1) { - while (x <= endX) { - while (z <= endZ) { - Block block = spigot.getRelative(x, y, z); - type = block.getType(); - - if (LegacyUtil.isWoodStairs(type)) { - if (y == 0) { - // stairs have to be upside down - if (!LegacyUtil.areStairsInverted(block)) { - return block; - } - } - stairs.add(block.getX()); - stairs.add(block.getY()); - stairs.add(block.getZ()); - z++; - } else { - return spigot.getRelative(x, y, z); - } - } - z = startZ; - x++; - } - z = startZ; - x = startX; - y++; - } - stairsloc = ArrayUtils.toPrimitive(stairs.toArray(new Integer[0])); - return null; - } - - public Block checkLBarrel() { - int direction = getDirection(spigot);// 1=x+ 2=x- 3=z+ 4=z- - if (direction == 0) { - return spigot; - } - int startX; - int startZ; - int endX; - int endZ; - - ArrayList stairs = new ArrayList<>(); - ArrayList woods = new ArrayList<>(); - - if (direction == 1) { - startX = 1; - endX = startX + 3; - startZ = -1; - endZ = 1; - } else if (direction == 2) { - startX = -4; - endX = startX + 3; - startZ = -1; - endZ = 1; - } else if (direction == 3) { - startX = -1; - endX = 1; - startZ = 1; - endZ = startZ + 3; - } else { - startX = -1; - endX = 1; - startZ = -4; - endZ = startZ + 3; - } - - Material type; - int x = startX; - int y = 0; - int z = startZ; - while (y <= 2) { - while (x <= endX) { - while (z <= endZ) { - Block block = spigot.getRelative(x, y, z); - type = block.getType(); - if (direction == 1 || direction == 2) { - if (y == 1 && z == 0) { - if (x == -1 || x == -4 || x == 1 || x == 4) { - woods.add(block.getX()); - woods.add(block.getY()); - woods.add(block.getZ()); - } - z++; - continue; - } - } else { - if (y == 1 && x == 0) { - if (z == -1 || z == -4 || z == 1 || z == 4) { - woods.add(block.getX()); - woods.add(block.getY()); - woods.add(block.getZ()); - } - z++; - continue; - } - } - if (LegacyUtil.isWoodPlanks(type) || LegacyUtil.isWoodStairs(type)) { - if (LegacyUtil.isWoodPlanks(type)) { - woods.add(block.getX()); - woods.add(block.getY()); - woods.add(block.getZ()); - } else { - stairs.add(block.getX()); - stairs.add(block.getY()); - stairs.add(block.getZ()); - } - z++; - } else { - return block; - } - } - z = startZ; - x++; - } - z = startZ; - x = startX; - y++; - } - stairsloc = ArrayUtils.toPrimitive(stairs.toArray(new Integer[0])); - woodsloc = ArrayUtils.toPrimitive(woods.toArray(new Integer[0])); - - return null; - } - public static class BarrelCheck extends BukkitRunnable { @Override public void run() { @@ -881,17 +537,17 @@ public class Barrel implements InventoryHolder { if (check < barrels.size()) { Barrel barrel = barrels.get(check); if (!barrel.checked) { - Block broken = barrel.getBrokenBlock(false); + Block broken = barrel.body.getBrokenBlock(false); if (broken != null) { - P.p.debugLog("Barrel at " + broken.getWorld().getName() + "/" + broken.getX() + "/" + broken.getY() + "/" + broken.getZ() - + " has been destroyed unexpectedly, contents will drop"); + P.p.debugLog("Barrel at " + + broken.getWorld().getName() + "/" + broken.getX() + "/" + broken.getY() + "/" + broken.getZ() + + " has been destroyed unexpectedly, contents will drop"); // remove the barrel if it was destroyed - barrel.willDestroy(); - barrel.remove(broken, null); + barrel.remove(broken, null, true); } else { - // Dont check this barrel again, its enough to check it once after every restart - // as now this is only the backup if we dont register the barrel breaking, as sample - // when removing it with some world editor + // Dont check this barrel again, its enough to check it once after every restart (and when randomly chosen) + // as now this is only the backup if we dont register the barrel breaking, + // for example when removing it with some world editor barrel.checked = true; } repeat = false; diff --git a/src/com/dre/brewery/BarrelBody.java b/src/com/dre/brewery/BarrelBody.java new file mode 100644 index 0000000..da0c617 --- /dev/null +++ b/src/com/dre/brewery/BarrelBody.java @@ -0,0 +1,384 @@ +package com.dre.brewery; + +import com.dre.brewery.utility.BUtil; +import com.dre.brewery.utility.BoundingBox; +import com.dre.brewery.utility.LegacyUtil; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.configuration.ConfigurationSection; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * The Blocks that make up a Barrel in the World + */ +public class BarrelBody { + + private final Barrel barrel; + private final Block spigot; + private BoundingBox bounds; + private byte signoffset; + + public BarrelBody(Barrel barrel, byte signoffset) { + this.barrel = barrel; + this.signoffset = signoffset; + spigot = barrel.getSpigot(); + this.bounds = new BoundingBox(0, 0, 0, 0, 0, 0); + } + + /** + * Loading from file + */ + public BarrelBody(Barrel barrel, byte signoffset, BoundingBox bounds) { + this(barrel, signoffset); + + if (bounds == null || bounds.area() > 64 ) { + // If loading from old data, or block locations are missing, or other error, regenerate BoundingBox + // This will only be done in those extreme cases. + Block broken = getBrokenBlock(true); + if (broken != null) { + barrel.remove(broken, null, true); + } + } else { + this.bounds = bounds; + } + } + + public Barrel getBarrel() { + return barrel; + } + + public Block getSpigot() { + return spigot; + } + + @NotNull + public BoundingBox getBounds() { + return bounds; + } + + public void setBounds(@NotNull BoundingBox bounds) { + Objects.requireNonNull(bounds); + this.bounds = bounds; + } + + public byte getSignoffset() { + return signoffset; + } + + public void setSignoffset(byte signoffset) { + this.signoffset = signoffset; + } + + /** + * If the Sign of a Large Barrel gets destroyed, set signOffset to 0 + */ + public void destroySign() { + signoffset = 0; + } + + /** + * direction of the barrel from the spigot + */ + public static int getDirection(Block spigot) { + int direction = 0;// 1=x+ 2=x- 3=z+ 4=z- + Material type = spigot.getRelative(0, 0, 1).getType(); + if (LegacyUtil.isWoodPlanks(type) || LegacyUtil.isWoodStairs(type)) { + direction = 3; + } + type = spigot.getRelative(0, 0, -1).getType(); + if (LegacyUtil.isWoodPlanks(type) || LegacyUtil.isWoodStairs(type)) { + if (direction == 0) { + direction = 4; + } else { + return 0; + } + } + type = spigot.getRelative(1, 0, 0).getType(); + if (LegacyUtil.isWoodPlanks(type) || LegacyUtil.isWoodStairs(type)) { + if (direction == 0) { + direction = 1; + } else { + return 0; + } + } + type = spigot.getRelative(-1, 0, 0).getType(); + if (LegacyUtil.isWoodPlanks(type) || LegacyUtil.isWoodStairs(type)) { + if (direction == 0) { + direction = 2; + } else { + return 0; + } + } + return direction; + } + + /** + * is this a Large barrel? + */ + public boolean isLarge() { + return barrel.isLarge(); + } + + /** + * is this a Small barrel? + */ + public boolean isSmall() { + return barrel.isSmall(); + } + + /** + * woodtype of the block the spigot is attached to + */ + public byte getWood() { + Block wood; + switch (getDirection(spigot)) { // 1=x+ 2=x- 3=z+ 4=z- + case 0: + return 0; + case 1: + wood = spigot.getRelative(1, 0, 0); + break; + case 2: + wood = spigot.getRelative(-1, 0, 0); + break; + case 3: + wood = spigot.getRelative(0, 0, 1); + break; + default: + wood = spigot.getRelative(0, 0, -1); + } + try { + return LegacyUtil.getWoodType(wood); + } catch (NoSuchFieldError | NoClassDefFoundError noSuchFieldError) { + // Using older minecraft versions some fields and classes do not exist + return 0; + } + } + + /** + * Returns true if this Block is part of this Barrel + * + * @param block the block to check + * @return true if the given block is part of this Barrel + */ + public boolean hasBlock(Block block) { + if (block != null) { + if (spigot.equals(block)) { + return true; + } + if (spigot.getWorld().equals(block.getWorld())) { + return bounds != null && bounds.contains(block.getX(), block.getY(), block.getZ()); + } + } + return false; + } + + /** + * Returns true if the Offset of the clicked Sign matches the Barrel. + *

This prevents adding another sign to the barrel and clicking that. + */ + public boolean isSignOfBarrel(byte offset) { + return offset == 0 || signoffset == 0 || signoffset == offset; + } + + /** + * returns the Sign of a large barrel, the spigot if there is none + */ + public Block getSignOfSpigot() { + if (signoffset != 0) { + if (LegacyUtil.isSign(spigot.getType())) { + return spigot; + } + + if (LegacyUtil.isSign(spigot.getRelative(0, signoffset, 0).getType())) { + return spigot.getRelative(0, signoffset, 0); + } else { + signoffset = 0; + } + } + return spigot; + } + + /** + * returns the fence above/below a block, itself if there is none + */ + public static Block getSpigotOfSign(Block block) { + + int y = -2; + while (y <= 1) { + // Fence and Netherfence + Block relative = block.getRelative(0, y, 0); + if (LegacyUtil.isFence(relative.getType())) { + return (relative); + } + y++; + } + return block; + } + + /** + * returns null if Barrel is correctly placed; the block that is missing when not. + *

the barrel needs to be formed correctly + * + * @param force to also check even if chunk is not loaded + */ + public Block getBrokenBlock(boolean force) { + if (force || BUtil.isChunkLoaded(spigot)) { + //spigot = getSpigotOfSign(spigot); + if (LegacyUtil.isSign(spigot.getType())) { + return checkSBarrel(); + } else { + return checkLBarrel(); + } + } + return null; + } + + public Block checkSBarrel() { + int direction = getDirection(spigot);// 1=x+ 2=x- 3=z+ 4=z- + if (direction == 0) { + return spigot; + } + int startX; + int startZ; + int endX; + int endZ; + + if (direction == 1) { + startX = 1; + startZ = -1; + } else if (direction == 2) { + startX = -2; + startZ = 0; + } else if (direction == 3) { + startX = 0; + startZ = 1; + } else { + startX = -1; + startZ = -2; + } + endX = startX + 1; + endZ = startZ + 1; + + Material type; + int x = startX; + int y = 0; + int z = startZ; + while (y <= 1) { + while (x <= endX) { + while (z <= endZ) { + Block block = spigot.getRelative(x, y, z); + type = block.getType(); + + if (LegacyUtil.isWoodStairs(type)) { + if (y == 0) { + // stairs have to be upside down + if (!LegacyUtil.areStairsInverted(block)) { + return block; + } + } + z++; + } else { + return spigot.getRelative(x, y, z); + } + } + z = startZ; + x++; + } + z = startZ; + x = startX; + y++; + } + bounds = new BoundingBox( + spigot.getX() + startX, + spigot.getY(), + spigot.getZ() + startZ, + spigot.getX() + endX, + spigot.getY() + 1, + spigot.getZ() + endZ); + return null; + } + + public Block checkLBarrel() { + int direction = getDirection(spigot);// 1=x+ 2=x- 3=z+ 4=z- + if (direction == 0) { + return spigot; + } + int startX; + int startZ; + int endX; + int endZ; + + if (direction == 1) { + startX = 1; + startZ = -1; + } else if (direction == 2) { + startX = -4; + startZ = -1; + } else if (direction == 3) { + startX = -1; + startZ = 1; + } else { + startX = -1; + startZ = -4; + } + if (direction == 1 || direction == 2) { + endX = startX + 3; + endZ = startZ + 2; + } else { + endX = startX + 2; + endZ = startZ + 3; + } + + Material type; + int x = startX; + int y = 0; + int z = startZ; + while (y <= 2) { + while (x <= endX) { + while (z <= endZ) { + Block block = spigot.getRelative(x, y, z); + type = block.getType(); + if (direction == 1 || direction == 2) { + if (y == 1 && z == 0) { + z++; + continue; + } + } else { + if (y == 1 && x == 0) { + z++; + continue; + } + } + if (LegacyUtil.isWoodPlanks(type) || LegacyUtil.isWoodStairs(type)) { + z++; + } else { + return block; + } + } + z = startZ; + x++; + } + z = startZ; + x = startX; + y++; + } + bounds = new BoundingBox( + spigot.getX() + startX, + spigot.getY(), + spigot.getZ() + startZ, + spigot.getX() + endX, + spigot.getY() + 2, + spigot.getZ() + endZ); + + return null; + } + + public void save(ConfigurationSection config, String prefix) { + if (signoffset != 0) { + config.set(prefix + ".sign", signoffset); + } + config.set(prefix + ".bounds", bounds.serialize()); + } +} diff --git a/src/com/dre/brewery/Brew.java b/src/com/dre/brewery/Brew.java index 60994b4..429d9dc 100644 --- a/src/com/dre/brewery/Brew.java +++ b/src/com/dre/brewery/Brew.java @@ -1,106 +1,214 @@ package com.dre.brewery; -import org.bukkit.Color; +import com.dre.brewery.api.events.brew.BrewModifyEvent; +import com.dre.brewery.filedata.BConfig; +import com.dre.brewery.filedata.ConfigUpdater; +import com.dre.brewery.lore.*; +import com.dre.brewery.recipe.BEffect; +import com.dre.brewery.recipe.BRecipe; +import com.dre.brewery.recipe.PotionColor; +import com.dre.brewery.utility.BUtil; import org.bukkit.Material; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.inventory.BrewerInventory; -import org.bukkit.inventory.ItemFlag; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.PotionMeta; import org.bukkit.potion.PotionData; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import org.bukkit.potion.PotionType; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Nullable; +import java.io.*; +import java.security.InvalidKeyException; +import java.security.SecureRandom; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; -public class Brew { - - // represents the liquid in the brewed Potions - - public static Map potions = new HashMap<>(); +/** + * Represents the liquid in the brewed Potions + */ +public class Brew implements Cloneable { + public static final byte SAVE_VER = 1; + private static long saveSeed; + private static List prevSaveSeeds = new ArrayList<>(); // Save Seeds that have been used in the past, stored to decode brews made at that time + public static Map legacyPotions = new HashMap<>(); public static long installTime = System.currentTimeMillis(); // plugin install time in millis after epoch - public static Boolean colorInBarrels; // color the Lore while in Barrels - public static Boolean colorInBrewer; // color the Lore while in Brewer private BIngredients ingredients; private int quality; - private int distillRuns; + private int alc; + private byte distillRuns; private float ageTime; private float wood; - private BRecipe currentRecipe; + private BRecipe currentRecipe; // Recipe this Brew is currently Based off. May change between modifications and is often null when not modifying private boolean unlabeled; - private boolean persistent; - private boolean stat; // static potions should not be changed + private boolean persistent; // Only for legacy + private boolean immutable; // static/immutable potions should not be changed private int lastUpdate; // last update in hours after install time + private boolean needsSave; // There was a change that has not yet been saved - public Brew(int uid, BIngredients ingredients) { + /** + * A new Brew with only ingredients + */ + public Brew(BIngredients ingredients) { this.ingredients = ingredients; touch(); - potions.put(uid, this); } - // quality already set - public Brew(int uid, int quality, BRecipe recipe, BIngredients ingredients) { + /** + * A Brew with quality, alc and recipe already set + */ + public Brew(int quality, int alc, BRecipe recipe, BIngredients ingredients) { this.ingredients = ingredients; this.quality = quality; + this.alc = alc; this.currentRecipe = recipe; touch(); - potions.put(uid, this); } - // loading from file - public Brew(int uid, BIngredients ingredients, int quality, int distillRuns, float ageTime, float wood, String recipe, boolean unlabeled, boolean persistent, boolean stat, int lastUpdate) { - potions.put(uid, this); + /** + * Loading a Brew with all values set + */ + public Brew(BIngredients ingredients, int quality, int alc, byte distillRuns, float ageTime, float wood, String recipe, boolean unlabeled, boolean immutable, int lastUpdate) { this.ingredients = ingredients; this.quality = quality; + this.alc = alc; this.distillRuns = distillRuns; this.ageTime = ageTime; this.wood = wood; this.unlabeled = unlabeled; - this.persistent = persistent; - this.stat = stat; + this.immutable = immutable; this.lastUpdate = lastUpdate; setRecipeFromString(recipe); } - // returns a Brew by its UID + // Loading from InputStream + private Brew() { + } + + /** + * returns a Brew by ItemMeta + * + * @param meta The meta to get the brew from + * @return The Brew if meta is a brew, null if not + */ + @Nullable + public static Brew get(ItemMeta meta) { + if (!P.useNBT && !meta.hasLore()) return null; + + Brew brew = load(meta); + + if (brew == null && meta instanceof PotionMeta && ((PotionMeta) meta).hasCustomEffect(PotionEffectType.REGENERATION)) { + // Load Legacy + return getFromPotionEffect(((PotionMeta) meta), false); + } + return brew; + } + + /** + * returns a Brew by ItemStack + * + * @param item The Item to get the brew from + * @return The Brew if item is a brew, null if not + */ + @Nullable + public static Brew get(ItemStack item) { + if (item.getType() != Material.POTION) return null; + if (!item.hasItemMeta()) return null; + + ItemMeta meta = item.getItemMeta(); + assert meta != null; + if (!P.useNBT && !meta.hasLore()) return null; + + Brew brew = load(meta); + + if (brew == null && meta instanceof PotionMeta && ((PotionMeta) meta).hasCustomEffect(PotionEffectType.REGENERATION)) { + // Load Legacy and convert + brew = getFromPotionEffect(((PotionMeta) meta), true); + if (brew == null) return null; + new BrewLore(brew, (PotionMeta) meta).removeLegacySpacing(); + brew.save(meta); + item.setItemMeta(meta); + } else if (brew != null && brew.needsSave) { + // Brew needs saving from a previous format + if (P.useNBT) { + new BrewLore(brew, (PotionMeta) meta).removeLoreData(); + P.p.debugLog("removed Data from Lore"); + } + brew.save(meta); + item.setItemMeta(meta); + } + return brew; + } + + // Legacy Brew Loading + private static Brew getFromPotionEffect(PotionMeta potionMeta, boolean remove) { + for (PotionEffect effect : potionMeta.getCustomEffects()) { + if (effect.getType().equals(PotionEffectType.REGENERATION)) { + if (effect.getDuration() < -1) { + if (remove) { + Brew b = legacyPotions.get(effect.getDuration()); + if (b != null) { + potionMeta.removeCustomEffect(PotionEffectType.REGENERATION); + if (b.persistent) { + return b; + } else { + return legacyPotions.remove(effect.getDuration()); + } + } + return null; + } else { + return legacyPotions.get(effect.getDuration()); + } + } + } + } + return null; + } + + /** + * returns a Brew by its UID + * + * @deprecated Does not work anymore with new save system + */ + @Deprecated public static Brew get(int uid) { if (uid < -1) { - if (!potions.containsKey(uid)) { + if (!legacyPotions.containsKey(uid)) { P.p.errorLog("Database failure! unable to find UID " + uid + " of a custom Potion!"); return null;// throw some exception? } } else { return null; } - return potions.get(uid); + return legacyPotions.get(uid); } - // returns a Brew by PotionMeta - public static Brew get(PotionMeta meta) { - return get(getUID(meta)); - } - - // returns a Brew by ItemStack - public static Brew get(ItemStack item) { - if (item.getType() == Material.POTION) { - if (item.hasItemMeta()) { - return get((PotionMeta) item.getItemMeta()); - } - } - return null; - } - - // returns UID of custom Potion item + /** + * returns UID of custom Potion item + * + * @deprecated Does not work anymore with new save system + */ + @Deprecated public static int getUID(ItemStack item) { return getUID((PotionMeta) item.getItemMeta()); } // returns UID of custom Potion meta + // Does not work anymore with new save system + + /** + * returns UID of custom Potion meta + * + * @deprecated Does not work anymore with new save system + */ + @Deprecated public static int getUID(PotionMeta potionMeta) { if (potionMeta.hasCustomEffect(PotionEffectType.REGENERATION)) { for (PotionEffect effect : potionMeta.getCustomEffects()) { @@ -115,20 +223,22 @@ public class Brew { } // generate an UID - public static int generateUID() { + /*public static int generateUID() { int uid = -2; while (potions.containsKey(uid)) { uid -= 1; } return uid; - } + }*/ - //returns the recipe with the given name, recalculates if not found + /** + * returns the recipe with the given name, recalculates if not found + */ public boolean setRecipeFromString(String name) { currentRecipe = null; if (name != null && !name.equals("")) { - for (BRecipe recipe : BIngredients.recipes) { - if (recipe.getName(5).equalsIgnoreCase(name)) { + for (BRecipe recipe : BRecipe.getAllRecipes()) { + if (recipe.getRecipeName().equalsIgnoreCase(name)) { currentRecipe = recipe; return true; } @@ -137,13 +247,13 @@ public class Brew { if (quality > 0) { currentRecipe = ingredients.getBestRecipe(wood, ageTime, distillRuns > 0); if (currentRecipe != null) { - if (!stat) { + /*if (!immutable) { this.quality = calcQuality(); - } - P.p.log("Brew was made from Recipe: '" + name + "' which could not be found. '" + currentRecipe.getName(5) + "' used instead!"); + }*/ + P.p.log("A Brew was made from Recipe: '" + name + "' which could not be found. '" + currentRecipe.getRecipeName() + "' used instead!"); return true; } else { - P.p.errorLog("Brew was made from Recipe: '" + name + "' which could not be found!"); + P.p.errorLog("A Brew was made from Recipe: '" + name + "' which could not be found!"); } } } @@ -151,11 +261,12 @@ public class Brew { } public boolean reloadRecipe() { - return currentRecipe == null || setRecipeFromString(currentRecipe.getName(5)); + return currentRecipe == null || setRecipeFromString(currentRecipe.getRecipeName()); } // Copy a Brew with a new unique ID and return its item - public ItemStack copy(ItemStack item) { + // Not needed anymore + /*public ItemStack copy(ItemStack item) { ItemStack copy = item.clone(); int uid = generateUID(); clone(uid); @@ -167,28 +278,64 @@ public class Brew { meta.addCustomEffect((PotionEffectType.REGENERATION).createEffect(uid, 0), true); copy.setItemMeta(meta); return copy; + }*/ + + public boolean isSimilar(Brew brew) { + if (brew == null) return false; + if (equals(brew)) return true; + return quality == brew.quality && + alc == brew.alc && + distillRuns == brew.distillRuns && + Float.compare(brew.ageTime, ageTime) == 0 && + Float.compare(brew.wood, wood) == 0 && + unlabeled == brew.unlabeled && + persistent == brew.persistent && + immutable == brew.immutable && + ingredients.equals(brew.ingredients) && + (Objects.equals(currentRecipe, brew.currentRecipe)); } - // Clones this instance with a new unique ID - public Brew clone(int uid) { - Brew brew = new Brew(uid, quality, currentRecipe, ingredients); - brew.distillRuns = distillRuns; - brew.ageTime = ageTime; - brew.unlabeled = unlabeled; - if (!brew.persistent) { - brew.stat = stat; + /** + * Clones this instance + */ + @Override + public Brew clone() { + try { + Brew brew = (Brew) super.clone(); + brew.ingredients = ingredients.copy(); + return brew; + } catch (CloneNotSupportedException e) { + throw new InternalError(e); } - return brew; + } + + @Override + public String toString() { + return "Brew{" + + ingredients + " ingredients" + + ", quality=" + quality + + ", alc=" + alc + + ", distillRuns=" + distillRuns + + ", ageTime=" + ageTime + + ", wood=" + wood + + ", currentRecipe=" + currentRecipe + + ", unlabeled=" + unlabeled + + ", immutable=" + immutable + + '}'; } // remove potion from file (drinking, despawning, combusting, cmdDeleting, should be more!) - public void remove(ItemStack item) { + // Not needed anymore + /*public void remove(ItemStack item) { if (!persistent) { potions.remove(getUID(item)); } - } + }*/ - // calculate alcohol from recipe + /** + * calculate alcohol from recipe + */ + @Contract(pure = true) public int calcAlcohol() { if (quality == 0) { // Give bad potions some alc @@ -215,13 +362,13 @@ public class Brew { return 0; } // bad quality can decrease alc by up to 40% - alc *= 1 - ((float) (10 - quality) * 0.04); + alc *= 1 - ((float) (10 - quality) * 0.04f); // distillable Potions should have half alc after one and full alc after all needed distills alc /= 2; alc *= 1.0F + ((float) distillRuns / currentRecipe.getDistillRuns()); } else { // quality decides 10% - 100% - alc *= ((float) quality / 10.0); + alc *= ((float) quality / 10.0f); } if (alc > 0) { return alc; @@ -230,7 +377,10 @@ public class Brew { return 0; } - // calculating quality + /** + * calculating quality + */ + @Contract(pure = true) public int calcQuality() { // calculate quality from all of the factors float quality = ingredients.getIngredientQuality(currentRecipe) + ingredients.getCookingQuality(currentRecipe, distillRuns > 0); @@ -248,7 +398,7 @@ public class Brew { } public boolean canDistill() { - if (stat) return false; + if (immutable) return false; if (currentRecipe != null) { return currentRecipe.getDistillRuns() > distillRuns; } else { @@ -256,35 +406,56 @@ public class Brew { } } - // return special effect - public ArrayList getEffects() { + /** + * Get Special Drink Effects + */ + public List getEffects() { if (currentRecipe != null && quality > 0) { return currentRecipe.getEffects(); } return null; } - // Set unlabeled to true to hide the numbers in Lore + /** + * Set unlabeled to true to hide the numbers in Lore + * + * @param item The Item this Brew is on + */ public void unLabel(ItemStack item) { - PotionMeta meta = (PotionMeta) item.getItemMeta(); - if (meta.hasLore()) { + unlabeled = true; + ItemMeta meta = item.getItemMeta(); + if (meta instanceof PotionMeta && meta.hasLore()) { + BrewLore lore = new BrewLore(this, ((PotionMeta) meta)); if (distillRuns > 0) { - addOrReplaceLore(meta, P.p.color("&7"), P.p.languageReader.get("Brew_Distilled")); + lore.updateDistillLore(false); } if (ageTime >= 1) { - addOrReplaceLore(meta, P.p.color("&7"), P.p.languageReader.get("Brew_BarrelRiped")); + lore.updateAgeLore(false); } + lore.updateQualityStars(false); + lore.updateAlc(false); + lore.write(); item.setItemMeta(meta); } - unlabeled = true; } - // Do some regular updates + /** + * Do some regular updates. + *

Not really used, apart from legacy potion timed purge + */ public void touch() { lastUpdate = (int) ((double) (System.currentTimeMillis() - installTime) / 3600000D); } - public int getDistillRuns() { + public int getOrCalcAlc() { + return alc > 0 ? alc : (alc = calcAlcohol()); + } + + public void setAlc(int alc) { + this.alc = alc; + } + + public byte getDistillRuns() { return distillRuns; } @@ -292,36 +463,52 @@ public class Brew { return ageTime; } + public float getWood() { + return wood; + } + + public BIngredients getIngredients() { + return ingredients; + } + + public boolean hasRecipe() { + return currentRecipe != null; + } + public BRecipe getCurrentRecipe() { return currentRecipe; } - public boolean isPersistent() { - return persistent; - } - - // Make a potion persistent to not delete it when drinking it - public void makePersistent() { - persistent = true; - } - - // Remove the Persistence Flag from a brew, so it will be normally deleted when drinking it - public void removePersistence() { - persistent = false; - } - public boolean isStatic() { - return stat; + return immutable; } - // Set the Static flag, so potion is unchangeable - public void setStatic(boolean stat, ItemStack potion) { - this.stat = stat; + public boolean isImmutable() { + return immutable; + } + + public boolean isUnlabeled() { + return unlabeled; + } + + public boolean needsSave() { + return needsSave; + } + + public void setNeedsSave(boolean needsSave) { + this.needsSave = needsSave; + } + + /** + * Set the Static flag, so potion is unchangeable + */ + public void setStatic(boolean immutable, ItemStack potion) { + this.immutable = immutable; if (currentRecipe != null && canDistill()) { - if (stat) { - PotionColor.fromString(currentRecipe.getColor()).colorBrew(((PotionMeta) potion.getItemMeta()), potion, false); + if (immutable) { + currentRecipe.getColor().colorBrew(((PotionMeta) potion.getItemMeta()), potion, false); } else { - PotionColor.fromString(currentRecipe.getColor()).colorBrew(((PotionMeta) potion.getItemMeta()), potion, true); + currentRecipe.getColor().colorBrew(((PotionMeta) potion.getItemMeta()), potion, true); } } } @@ -332,7 +519,12 @@ public class Brew { // Distilling section --------------- - // distill all custom potions in the brewer + /** + * distill all custom potions in the brewer + * + * @param inv The Inventory of the Distiller + * @param contents The Brews in the 3 slots of the Inventory + */ public static void distillAll(BrewerInventory inv, Brew[] contents) { for (int slot = 0; slot < 3; slot++) { if (contents[slot] != null) { @@ -343,42 +535,54 @@ public class Brew { } } - // distill custom potion in given slot + /** + * distill custom potion in a distiller slot + * + * @param slotItem The item in the slot + * @param potionMeta The meta of the item + */ public void distillSlot(ItemStack slotItem, PotionMeta potionMeta) { - if (stat) { - return; - } + if (immutable) return; distillRuns += 1; - BRecipe recipe = ingredients.getdistillRecipe(wood, ageTime); + BrewLore lore = new BrewLore(this, potionMeta); + BRecipe recipe = ingredients.getDistillRecipe(wood, ageTime); if (recipe != null) { // distillRuns will have an effect on the amount of alcohol, not the quality currentRecipe = recipe; quality = calcQuality(); - addOrReplaceEffects(potionMeta, getEffects(), quality); + lore.addOrReplaceEffects(getEffects(), quality); potionMeta.setDisplayName(P.p.color("&f" + recipe.getName(quality))); - PotionColor.fromString(recipe.getColor()).colorBrew(potionMeta, slotItem, canDistill()); + recipe.getColor().colorBrew(potionMeta, slotItem, canDistill()); } else { quality = 0; - removeEffects(potionMeta); + lore.removeEffects(); potionMeta.setDisplayName(P.p.color("&f" + P.p.languageReader.get("Brew_DistillUndefined"))); PotionColor.GREY.colorBrew(potionMeta, slotItem, canDistill()); } + alc = calcAlcohol(); // Distill Lore - if (currentRecipe != null) { - if (colorInBrewer != hasColorLore(potionMeta)) { - convertLore(potionMeta, colorInBrewer); - } + if (currentRecipe != null && BConfig.colorInBrewer != BrewLore.hasColorLore(potionMeta)) { + lore.convertLore(BConfig.colorInBrewer); + } else { + lore.updateQualityStars(BConfig.colorInBrewer); + lore.updateCustomLore(); + lore.updateDistillLore(BConfig.colorInBrewer); } - String prefix = P.p.color("&7"); - if (colorInBrewer && currentRecipe != null) { - prefix = getQualityColor(ingredients.getDistillQuality(currentRecipe, distillRuns)); - } - updateDistillLore(prefix, potionMeta); + lore.updateAlc(true); + lore.write(); touch(); + BrewModifyEvent modifyEvent = new BrewModifyEvent(this, potionMeta, BrewModifyEvent.Type.DISTILL); + P.p.getServer().getPluginManager().callEvent(modifyEvent); + if (modifyEvent.isCancelled()) { + // As the brew and everything connected to it is only saved on the meta from now on, + // not saving the brew into potionMeta is enough to not change anything in case of cancel + return; + } + save(potionMeta); slotItem.setItemMeta(potionMeta); } @@ -392,7 +596,7 @@ public class Brew { return currentRecipe.getDistillTime(); } - BRecipe recipe = ingredients.getdistillRecipe(wood, ageTime); + BRecipe recipe = ingredients.getDistillRecipe(wood, ageTime); if (recipe != null) { return recipe.getDistillTime(); } @@ -402,11 +606,10 @@ public class Brew { // Ageing Section ------------------ public void age(ItemStack item, float time, byte woodType) { - if (stat) { - return; - } - + if (immutable) return; PotionMeta potionMeta = (PotionMeta) item.getItemMeta(); + + BrewLore lore = new BrewLore(this, potionMeta); ageTime += time; // if younger than half a day, it shouldnt get aged form @@ -421,47 +624,59 @@ public class Brew { currentRecipe = recipe; quality = calcQuality(); - addOrReplaceEffects(potionMeta, getEffects(), quality); + lore.addOrReplaceEffects(getEffects(), quality); potionMeta.setDisplayName(P.p.color("&f" + recipe.getName(quality))); - PotionColor.fromString(recipe.getColor()).colorBrew(potionMeta, item, canDistill()); + recipe.getColor().colorBrew(potionMeta, item, canDistill()); } else { quality = 0; - removeEffects(potionMeta); + lore.convertLore(false); + lore.removeEffects(); + currentRecipe = null; potionMeta.setDisplayName(P.p.color("&f" + P.p.languageReader.get("Brew_BadPotion"))); PotionColor.GREY.colorBrew(potionMeta, item, canDistill()); } } + alc = calcAlcohol(); // Lore - if (currentRecipe != null) { - if (colorInBarrels != hasColorLore(potionMeta)) { - convertLore(potionMeta, colorInBarrels); - } - } - if (ageTime >= 1) { - String prefix = P.p.color("&7"); - if (colorInBarrels && currentRecipe != null) { - prefix = getQualityColor(ingredients.getAgeQuality(currentRecipe, ageTime)); - } - updateAgeLore(prefix, potionMeta); - } - if (ageTime > 0.5) { - if (colorInBarrels && !unlabeled && currentRecipe != null) { - updateWoodLore(potionMeta); + if (currentRecipe != null && BConfig.colorInBarrels != BrewLore.hasColorLore(potionMeta)) { + lore.convertLore(BConfig.colorInBarrels); + } else { + if (ageTime >= 1) { + lore.updateAgeLore(BConfig.colorInBarrels); + } + if (ageTime > 0.5) { + if (BConfig.colorInBarrels) { + lore.updateWoodLore(true); + } + lore.updateQualityStars(BConfig.colorInBarrels); + lore.updateCustomLore(); + lore.updateAlc(false); } } + lore.write(); touch(); + BrewModifyEvent modifyEvent = new BrewModifyEvent(this, potionMeta, BrewModifyEvent.Type.AGE); + P.p.getServer().getPluginManager().callEvent(modifyEvent); + if (modifyEvent.isCancelled()) { + // As the brew and everything connected to it is only saved on the meta from now on, + // not saving the brew into potionMeta is enough to not change anything in case of cancel + return; + } + save(potionMeta); item.setItemMeta(potionMeta); } - // Slowly shift the wood of the Brew to the new Type + /** + * Slowly shift the wood of the Brew to the new Type + */ public void woodShift(float time, byte to) { - byte factor = 1; + float factor = 1; if (ageTime > 5) { factor = 2; } else if (ageTime > 10) { factor = 2; - factor += Math.round(ageTime / 10); + factor += ageTime / 10F; } if (wood > to) { wood -= time / factor; @@ -476,182 +691,364 @@ public class Brew { } } - // Lore ----------- - - // Converts to/from qualitycolored Lore - public void convertLore(PotionMeta meta, Boolean toQuality) { - if (currentRecipe == null) { - return; - } - meta.setLore(null); - int quality; - String prefix = P.p.color("&7"); - String lore; - - // Ingredients - if (toQuality && !unlabeled) { - quality = ingredients.getIngredientQuality(currentRecipe); - prefix = getQualityColor(quality); - lore = P.p.languageReader.get("Brew_Ingredients"); - addOrReplaceLore(meta, prefix, lore); - } - - // Cooking - if (toQuality && !unlabeled) { - if (distillRuns > 0 == currentRecipe.needsDistilling()) { - quality = ingredients.getCookingQuality(currentRecipe, distillRuns > 0); - prefix = getQualityColor(quality) + ingredients.getCookedTime() + " " + P.p.languageReader.get("Brew_minute"); - if (ingredients.getCookedTime() > 1) { - prefix = prefix + P.p.languageReader.get("Brew_MinutePluralPostfix"); - } - lore = " " + P.p.languageReader.get("Brew_fermented"); - addOrReplaceLore(meta, prefix, lore); - } - } - - // Distilling - if (distillRuns > 0) { - if (toQuality) { - quality = ingredients.getDistillQuality(currentRecipe, distillRuns); - prefix = getQualityColor(quality); - } - updateDistillLore(prefix, meta); - } - - // Ageing - if (ageTime >= 1) { - if (toQuality) { - quality = ingredients.getAgeQuality(currentRecipe, ageTime); - prefix = getQualityColor(quality); - } - updateAgeLore(prefix, meta); - } - - // WoodType - if (toQuality && !unlabeled) { - if (ageTime > 0.5) { - updateWoodLore(meta); - } - } + /** + * Create a new Item of this Brew. A BrewModifyEvent type CREATE will be called. + * + * @param recipe Recipe is required if the brew doesn't have a currentRecipe + * @return The created Item, null if the Event is cancelled + */ + public ItemStack createItem(BRecipe recipe) { + return createItem(recipe, true); } - // sets the DistillLore. Prefix is the color to be used - public void updateDistillLore(String prefix, PotionMeta meta) { - if (!unlabeled) { - if (distillRuns > 1) { - prefix = prefix + distillRuns + P.p.languageReader.get("Brew_-times") + " "; + /** + * Create a new Item of this Brew. + * + * @param recipe Recipe is required if the brew doesn't have a currentRecipe + * @param event Set event to true if a BrewModifyEvent type CREATE should be called and may be cancelled. Only then may this method return null + * @return The created Item, null if the Event is cancelled + */ + @Contract("_, false -> !null") + public ItemStack createItem(BRecipe recipe, boolean event) { + if (recipe == null) { + recipe = getCurrentRecipe(); + } + if (recipe == null) { + throw new IllegalArgumentException("Argument recipe can't be null if the brew doesn't have a currentRecipe"); + } + ItemStack potion = new ItemStack(Material.POTION); + PotionMeta potionMeta = (PotionMeta) potion.getItemMeta(); + + recipe.getColor().colorBrew(potionMeta, potion, false); + potionMeta.setDisplayName(P.p.color("&f" + recipe.getName(quality))); + //if (!P.use1_14) { + // Before 1.14 the effects duration would strangely be only a quarter of what we tell it to be + // This is due to the Duration Modifier, that is removed in 1.14 + // uid *= 4; + //} + // This effect stores the UID in its Duration + //potionMeta.addCustomEffect((PotionEffectType.REGENERATION).createEffect((uid * 4), 0), true); + + BrewLore lore = new BrewLore(this, potionMeta); + lore.convertLore(false); + lore.addOrReplaceEffects(recipe.getEffects(), quality); + lore.write(); + touch(); + if (event) { + BrewModifyEvent modifyEvent = new BrewModifyEvent(this, potionMeta, BrewModifyEvent.Type.CREATE); + P.p.getServer().getPluginManager().callEvent(modifyEvent); + if (modifyEvent.isCancelled()) { + return null; } } - addOrReplaceLore(meta, prefix, P.p.languageReader.get("Brew_Distilled")); + save(potionMeta); + potion.setItemMeta(potionMeta); + P.p.metricsForCreate(true); + return potion; } - // sets the AgeLore. Prefix is the color to be used - public void updateAgeLore(String prefix, PotionMeta meta) { - if (!unlabeled) { - if (ageTime >= 1 && ageTime < 2) { - prefix = prefix + P.p.languageReader.get("Brew_OneYear") + " "; - } else if (ageTime < 201) { - prefix = prefix + (int) Math.floor(ageTime) + " " + P.p.languageReader.get("Brew_Years") + " "; - } else { - prefix = prefix + P.p.languageReader.get("Brew_HundredsOfYears") + " "; + /** + * Performant way of checking if this item is a Brew. + *

Does not give any guarantees that get() will return notnull for this item, i.e. if it is a brew but the data is corrupt + * + * @param item The Item to check + * @return True if the item is a brew + */ + public static boolean isBrew(ItemStack item) { + if (item == null || item.getType() != Material.POTION) return false; + if (!item.hasItemMeta()) return false; + + ItemMeta meta = item.getItemMeta(); + assert meta != null; + if (!P.useNBT && !meta.hasLore()) return false; + + if (P.useNBT) { + // Check for Data on PersistentDataContainer + if (NBTLoadStream.hasDataInMeta(meta)) { + return true; } } - addOrReplaceLore(meta, prefix, P.p.languageReader.get("Brew_BarrelRiped")); - } - - // updates/sets the color on WoodLore - public void updateWoodLore(PotionMeta meta) { - if (currentRecipe.getWood() > 0) { - int quality = ingredients.getWoodQuality(currentRecipe, wood); - addOrReplaceLore(meta, getQualityColor(quality), P.p.languageReader.get("Brew_Woodtype")); - } else if (meta.hasLore()) { - List existingLore = meta.getLore(); - int index = indexOfSubstring(existingLore, P.p.languageReader.get("Brew_Woodtype")); - if (index > -1) { - existingLore.remove(index); - meta.setLore(existingLore); - } - } - } - - // Adds or replaces a line of Lore. Searches for Substring lore and replaces it - public static void addOrReplaceLore(PotionMeta meta, String prefix, String lore) { + // If either NBT is not supported or no data was found in NBT, try finding data in lore if (meta.hasLore()) { - List existingLore = meta.getLore(); - int index = indexOfSubstring(existingLore, lore); - if (index > -1) { - existingLore.set(index, prefix + lore); - } else { - existingLore.add(prefix + lore); - } - meta.setLore(existingLore); - return; + // Find the Data Identifier in Lore + return BUtil.indexOfStart(meta.getLore(), LoreLoadStream.IDENTIFIER) > -1; } - List newLore = new ArrayList<>(); - newLore.add(""); - newLore.add(prefix + lore); - meta.setLore(newLore); + return false; } - // Adds the Effect names to the Items description - public static void addOrReplaceEffects(PotionMeta meta, ArrayList effects, int quality) { - if (!P.use1_9 && effects != null) { - for (BEffect effect : effects) { - if (!effect.isHidden()) { - effect.writeInto(meta, quality); - } + private static Brew load(ItemMeta meta) { + InputStream itemLoadStream = null; + if (P.useNBT) { + // Try loading the Item Data from PersistentDataContainer + NBTLoadStream nbtStream = new NBTLoadStream(meta); + if (nbtStream.hasData()) { + itemLoadStream = nbtStream; } } - } - - // Removes all effects except regeneration which stores data - public static void removeEffects(PotionMeta meta) { - if (meta.hasCustomEffects()) { - for (PotionEffect effect : meta.getCustomEffects()) { - PotionEffectType type = effect.getType(); - if (!type.equals(PotionEffectType.REGENERATION)) { - meta.removeCustomEffect(type); - } + if (itemLoadStream == null) { + // If either NBT is not supported or no data was found in NBT, try loading from Lore + try { + itemLoadStream = new Base91DecoderStream(new LoreLoadStream(meta, 0)); + } catch (IllegalArgumentException ignored) { + // No Brew data found in Meta + return null; } } - } - // Returns the Index of a String from the list that contains this substring - public static int indexOfSubstring(List list, String substring) { - for (int index = 0; index < list.size(); index++) { - String string = list.get(index); - if (string.contains(substring)) { - return index; + XORUnscrambleStream unscrambler = new XORUnscrambleStream(itemLoadStream, saveSeed, prevSaveSeeds); + try (DataInputStream in = new DataInputStream(unscrambler)) { + boolean parityFailed = false; + if (in.readByte() != 86) { + P.p.errorLog("Parity check failed on Brew while loading, trying to load anyways!"); + parityFailed = true; } + Brew brew = new Brew(); + byte ver = in.readByte(); + switch (ver) { + case 1: + + unscrambler.start(); + brew.loadFromStream(in, ver); + + break; + default: + if (parityFailed) { + P.p.errorLog("Failed to load Brew. Maybe something corrupted the Lore of the Item?"); + } else { + P.p.errorLog("Brew has data stored in v" + ver + " this Plugin version supports up to v1"); + } + return null; + } + + XORUnscrambleStream.SuccessType successType = unscrambler.getSuccessType(); + if (successType == XORUnscrambleStream.SuccessType.PREV_SEED) { + P.p.debugLog("Converting Brew from previous Seed"); + brew.setNeedsSave(true); + } else if (BConfig.enableEncode != (successType == XORUnscrambleStream.SuccessType.MAIN_SEED)) { + // We have either enabled encode and the data was not encoded or the other way round + P.p.debugLog("Converting Brew to new encode setting"); + brew.setNeedsSave(true); + } else if (P.useNBT && itemLoadStream instanceof Base91DecoderStream) { + // We are on a version that supports nbt but the data is still in the lore of the item + // Just save it again so that it gets saved to nbt + P.p.debugLog("Converting Brew to NBT"); + brew.setNeedsSave(true); + } + return brew; + } catch (IOException e) { + P.p.errorLog("IO Error while loading Brew"); + e.printStackTrace(); + } catch (InvalidKeyException e) { + P.p.errorLog("Failed to load Brew, has the data key 'encodeKey' in the config.yml been changed?"); + e.printStackTrace(); } - return -1; + return null; } - // True if the PotionMeta has colored Lore - public static Boolean hasColorLore(PotionMeta meta) { - return meta.hasLore() && (meta.getLore().size() > 1 && !meta.getLore().get(1).startsWith(P.p.color("&7"))); + private void loadFromStream(DataInputStream in, byte dataVersion) throws IOException { + quality = in.readByte(); + int bools = in.readUnsignedByte(); + if ((bools & 64) != 0) { + alc = in.readShort(); + } + if ((bools & 1) != 0) { + distillRuns = in.readByte(); + } + if ((bools & 2) != 0) { + ageTime = in.readFloat(); + } + if ((bools & 4) != 0) { + wood = in.readFloat(); + } + String recipe = null; + if ((bools & 8) != 0) { + recipe = in.readUTF(); + } + unlabeled = (bools & 16) != 0; + immutable = (bools & 32) != 0; + ingredients = BIngredients.load(in, dataVersion); + setRecipeFromString(recipe); } - // gets the Color that represents a quality in Lore - public static String getQualityColor(int quality) { - String color; - if (quality > 8) { - color = "&a"; - } else if (quality > 6) { - color = "&e"; - } else if (quality > 4) { - color = "&6"; - } else if (quality > 2) { - color = "&c"; + /** + * Save brew data into meta: lore/nbt. + *

Should be called after any changes made to the brew + */ + public void save(ItemMeta meta) { + OutputStream itemSaveStream; + if (P.useNBT) { + itemSaveStream = new NBTSaveStream(meta); } else { - color = "&4"; + itemSaveStream = new Base91EncoderStream(new LoreSaveStream(meta, 0)); + } + XORScrambleStream scrambler = new XORScrambleStream(itemSaveStream, saveSeed); + try (DataOutputStream out = new DataOutputStream(scrambler)) { + out.writeByte(86); // Parity/sanity + out.writeByte(SAVE_VER); // Version + if (BConfig.enableEncode) { + scrambler.start(); + } else { + scrambler.startUnscrambled(); + } + saveToStream(out); + } catch (IOException e) { + P.p.errorLog("IO Error while saving Brew"); + e.printStackTrace(); } - return P.p.color(color); } - // Saves all data - public static void save(ConfigurationSection config) { - for (Map.Entry entry : potions.entrySet()) { + /** + * Save brew data into the meta/lore of the specified item. + *

The meta on the item changes, so to make further changes to the meta, item.getItemMeta() has to be called again after this + * + * @param item The item to save this brew into + */ + public void save(ItemStack item) { + ItemMeta meta; + if (!item.hasItemMeta()) { + meta = P.p.getServer().getItemFactory().getItemMeta(item.getType()); + } else { + meta = item.getItemMeta(); + } + save(meta); + item.setItemMeta(meta); + } + + public void saveToStream(DataOutputStream out) throws IOException { + if (quality > 10) { + quality = 10; + } + alc = Math.min(alc, Short.MAX_VALUE); + out.writeByte((byte) quality); + int bools = 0; + bools |= ((distillRuns != 0) ? 1 : 0); + bools |= (ageTime > 0 ? 2 : 0); + bools |= (wood != -1 ? 4 : 0); + bools |= (currentRecipe != null ? 8 : 0); + bools |= (unlabeled ? 16 : 0); + bools |= (immutable ? 32 : 0); + bools |= (alc > 0 ? 64 : 0); + out.writeByte(bools); + if (alc > 0) { + out.writeShort(alc); + } + if (distillRuns != 0) { + out.writeByte(distillRuns); + } + if (ageTime > 0) { + out.writeFloat(ageTime); + } + if (wood != -1) { + out.writeFloat(wood); + } + if (currentRecipe != null) { + out.writeUTF(currentRecipe.getRecipeName()); + } + ingredients.save(out); + } + + public static void loadPrevSeeds(ConfigurationSection section) { + if (section.contains("prevSaveSeeds")) { + prevSaveSeeds = section.getLongList("prevSaveSeeds"); + if (!prevSaveSeeds.contains(saveSeed)) { + prevSaveSeeds.add(saveSeed); + } + } + } + + public static void writePrevSeeds(ConfigurationSection section) { + if (!prevSaveSeeds.isEmpty()) { + section.set("prevSaveSeeds", prevSaveSeeds); + } + } + + public static void loadSeed(ConfigurationSection config, File file) { + saveSeed = config.getLong("encodeKey", 0); + if (saveSeed == 0) { + while (saveSeed == 0) { + saveSeed = new SecureRandom().nextLong(); + } + ConfigUpdater updater = new ConfigUpdater(file); + updater.setEncodeKey(saveSeed); + updater.saveConfig(); + } + if (!prevSaveSeeds.contains(saveSeed)) { + prevSaveSeeds.add(saveSeed); + } + } + + public static boolean noLegacy() { + return legacyPotions.isEmpty(); + } + + /** + * Load potion data from data file for backwards compatibility + */ + public static void loadLegacy(BIngredients ingredients, int uid, int quality, int alc, byte distillRuns, float ageTime, float wood, String recipe, boolean unlabeled, boolean persistent, boolean stat, int lastUpdate) { + Brew brew = new Brew(ingredients, quality, alc, distillRuns, ageTime, wood, recipe, unlabeled, stat, lastUpdate); + brew.persistent = persistent; + if (brew.lastUpdate <= 0) { + // We failed to save the lastUpdate, restart the countdown + brew.touch(); + } + legacyPotions.put(uid, brew); + } + + /** + * remove legacy potiondata for an item + */ + public static void removeLegacy(ItemStack item) { + if (legacyPotions.isEmpty()) return; + if (!item.hasItemMeta()) return; + ItemMeta meta = item.getItemMeta(); + if (!(meta instanceof PotionMeta)) return; + getFromPotionEffect(((PotionMeta) meta), true); + } + + public void convertPre1_9(ItemStack item) { + removeLegacy(item); + PotionMeta potionMeta = ((PotionMeta) item.getItemMeta()); + assert potionMeta != null; + + BrewLore lore = new BrewLore(this, potionMeta); + lore.removeEffects(); + + if (hasRecipe()) { + currentRecipe.getColor().colorBrew(potionMeta, item, canDistill()); + } else { + PotionColor.GREY.colorBrew(potionMeta, item, canDistill()); + } + lore.removeLegacySpacing(); + save(potionMeta); + item.setItemMeta(potionMeta); + } + + public void convertPre1_11(ItemStack item) { + removeLegacy(item); + PotionMeta potionMeta = ((PotionMeta) item.getItemMeta()); + assert potionMeta != null; + + potionMeta.setBasePotionData(new PotionData(PotionType.UNCRAFTABLE)); + BrewLore lore = new BrewLore(this, potionMeta); + lore.removeEffects(); + + if (hasRecipe()) { + lore.addOrReplaceEffects(currentRecipe.getEffects(), getQuality()); + currentRecipe.getColor().colorBrew(potionMeta, item, canDistill()); + } else { + PotionColor.GREY.colorBrew(potionMeta, item, canDistill()); + } + lore.removeLegacySpacing(); + save(potionMeta); + item.setItemMeta(potionMeta); + } + + /** + * Saves all data, + * Legacy method to save to data file. + */ + public static void saveLegacy(ConfigurationSection config) { + for (Map.Entry entry : legacyPotions.entrySet()) { int uid = entry.getKey(); Brew brew = entry.getValue(); ConfigurationSection idConfig = config.createSection("" + uid); @@ -659,6 +1056,9 @@ public class Brew { if (brew.quality != 0) { idConfig.set("quality", brew.quality); } + if (brew.alc > 0) { + idConfig.set("alc", brew.alc); + } if (brew.distillRuns != 0) { idConfig.set("distillRuns", brew.distillRuns); } @@ -669,7 +1069,7 @@ public class Brew { idConfig.set("wood", brew.wood); } if (brew.currentRecipe != null) { - idConfig.set("recipe", brew.currentRecipe.getName(5)); + idConfig.set("recipe", brew.currentRecipe.getRecipeName()); } if (brew.unlabeled) { idConfig.set("unlabeled", true); @@ -677,109 +1077,15 @@ public class Brew { if (brew.persistent) { idConfig.set("persist", true); } - if (brew.stat) { + if (brew.immutable) { idConfig.set("stat", true); } if (brew.lastUpdate > 0) { idConfig.set("lastUpdate", brew.lastUpdate); } // save the ingredients - idConfig.set("ingId", brew.ingredients.save(config.getParent())); + idConfig.set("ingId", brew.ingredients.saveLegacy(config.getParent())); } } - public static class PotionColor { - public static final PotionColor PINK = new PotionColor(1, PotionType.REGEN, Color.FUCHSIA); - public static final PotionColor CYAN = new PotionColor(2, PotionType.SPEED, Color.AQUA); - public static final PotionColor ORANGE = new PotionColor(3, PotionType.FIRE_RESISTANCE, Color.ORANGE); - public static final PotionColor GREEN = new PotionColor(4, PotionType.POISON, Color.GREEN); - public static final PotionColor BRIGHT_RED = new PotionColor(5, PotionType.INSTANT_HEAL, Color.fromRGB(255,0,0)); - public static final PotionColor BLUE = new PotionColor(6, PotionType.NIGHT_VISION, Color.NAVY); - public static final PotionColor BLACK = new PotionColor(8, PotionType.WEAKNESS, Color.BLACK); - public static final PotionColor RED = new PotionColor(9, PotionType.STRENGTH, Color.fromRGB(196,0,0)); - public static final PotionColor GREY = new PotionColor(10, PotionType.SLOWNESS, Color.GRAY); - public static final PotionColor WATER = new PotionColor(11, P.use1_9 ? PotionType.WATER_BREATHING : null, Color.BLUE); - public static final PotionColor DARK_RED = new PotionColor(12, PotionType.INSTANT_DAMAGE, Color.fromRGB(128,0,0)); - public static final PotionColor BRIGHT_GREY = new PotionColor(14, PotionType.INVISIBILITY, Color.SILVER); - - private final int colorId; - private final PotionType type; - private final Color color; - - PotionColor(int colorId, PotionType type, Color color) { - this.colorId = colorId; - this.type = type; - this.color = color; - } - - public PotionColor(Color color) { - colorId = -1; - type = WATER.getType(); - this.color = color; - } - - // gets the Damage Value, that sets a color on the potion - // offset +32 is not accepted by brewer, so not further destillable - public short getColorId(boolean destillable) { - if (destillable) { - return (short) (colorId + 64); - } - return (short) (colorId + 32); - } - - public PotionType getType() { - return type; - } - - public Color getColor() { - return color; - } - - @SuppressWarnings("deprecation") - public void colorBrew(PotionMeta meta, ItemStack potion, boolean destillable) { - if (P.use1_9) { - meta.addItemFlags(ItemFlag.HIDE_POTION_EFFECTS); - if (P.use1_11) { - // BasePotionData was only used for the Color, so starting with 1.12 we can use setColor instead - meta.setColor(getColor()); - } else { - meta.setBasePotionData(new PotionData(getType())); - } - } else { - potion.setDurability(getColorId(destillable)); - } - } - - public static PotionColor fromString(String string) { - switch (string) { - case "PINK": return PINK; - case "CYAN": return CYAN; - case "ORANGE": return ORANGE; - case "GREEN": return GREEN; - case "BRIGHT_RED": return BRIGHT_RED; - case "BLUE": return BLUE; - case "BLACK": return BLACK; - case "RED": return RED; - case "GREY": return GREY; - case "WATER": return WATER; - case "DARK_RED": return DARK_RED; - case "BRIGHT_GREY": return BRIGHT_GREY; - default: - try{ - if (string.length() >= 7) { - string = string.substring(1); - } - return new PotionColor(Color.fromRGB( - Integer.parseInt(string.substring( 0, 2 ), 16 ), - Integer.parseInt(string.substring( 2, 4 ), 16 ), - Integer.parseInt(string.substring( 4, 6 ), 16 ) - )); - } catch (Exception e) { - return WATER; - } - } - } - - } - } diff --git a/src/com/dre/brewery/Words.java b/src/com/dre/brewery/DistortChat.java similarity index 82% rename from src/com/dre/brewery/Words.java rename to src/com/dre/brewery/DistortChat.java index 8301cf7..362faab 100644 --- a/src/com/dre/brewery/Words.java +++ b/src/com/dre/brewery/DistortChat.java @@ -1,5 +1,6 @@ package com.dre.brewery; +import com.dre.brewery.api.events.PlayerChatDistortEvent; import org.bukkit.event.block.SignChangeEvent; import org.bukkit.event.player.AsyncPlayerChatEvent; import org.bukkit.event.player.PlayerCommandPreprocessEvent; @@ -9,11 +10,11 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -public class Words { +public class DistortChat { // represends Words and letters, that are replaced in drunk players messages - public static ArrayList words = new ArrayList<>(); + public static List words = new ArrayList<>(); public static List commands; public static List ignoreText = new ArrayList<>(); public static Boolean doSigns; @@ -27,7 +28,7 @@ public class Words { private int alcohol = 1; private int percentage = 100; - public Words(Map part) { + public DistortChat(Map part) { for (Map.Entry wordPart : part.entrySet()) { String key = (String) wordPart.getKey(); @@ -79,9 +80,15 @@ public class Words { P.p.log(P.p.languageReader.get("Player_TriedToSay", name, chat)); } String message = chat.substring(command.length() + 1); - message = distortMessage(message, bPlayer.getDrunkeness()); + String distorted = distortMessage(message, bPlayer.getDrunkeness()); + PlayerChatDistortEvent call = new PlayerChatDistortEvent(event.isAsynchronous(), event.getPlayer(), bPlayer, message, distorted); + P.p.getServer().getPluginManager().callEvent(call); + if (call.isCancelled()) { + return; + } + distorted = call.getDistortedMessage(); - event.setMessage(chat.substring(0, command.length() + 1) + message); + event.setMessage(chat.substring(0, command.length() + 1) + distorted); waitPlayers.put(name, System.currentTimeMillis()); return; } @@ -101,12 +108,17 @@ public class Words { int index = 0; for (String message : event.getLines()) { if (message.length() > 1) { - message = distortMessage(message, bPlayer.getDrunkeness()); + String distorted = distortMessage(message, bPlayer.getDrunkeness()); + PlayerChatDistortEvent call = new PlayerChatDistortEvent(event.isAsynchronous(), event.getPlayer(), bPlayer, message, distorted); + P.p.getServer().getPluginManager().callEvent(call); + if (!call.isCancelled()) { + distorted = call.getDistortedMessage(); - if (message.length() > 15) { - message = message.substring(0, 14); + if (distorted.length() > 15) { + distorted = distorted.substring(0, 14); + } + event.setLine(index, distorted); } - event.setLine(index, message); } index++; } @@ -123,7 +135,16 @@ public class Words { if (log) { P.p.log(P.p.languageReader.get("Player_TriedToSay", event.getPlayer().getName(), message)); } - event.setMessage(distortMessage(message, bPlayer.getDrunkeness())); + + String distorted = distortMessage(message, bPlayer.getDrunkeness()); + PlayerChatDistortEvent call = new PlayerChatDistortEvent(event.isAsynchronous(), event.getPlayer(), bPlayer, message, distorted); + P.p.getServer().getPluginManager().callEvent(call); + if (call.isCancelled()) { + return; + } + distorted = call.getDistortedMessage(); + + event.setMessage(distorted); } } } @@ -164,7 +185,9 @@ public class Words { // distorts a message without checking ignoreText letters private static String distortString(String message, int drunkeness) { if (message.length() > 1) { - for (Words word : words) { + // Create our own reference to the words list, in case of config reload + List words = DistortChat.words; + for (DistortChat word : words) { if (word.alcohol <= drunkeness) { message = word.distort(message); } diff --git a/src/com/dre/brewery/MCBarrel.java b/src/com/dre/brewery/MCBarrel.java index 4374b3e..6fda3c3 100644 --- a/src/com/dre/brewery/MCBarrel.java +++ b/src/com/dre/brewery/MCBarrel.java @@ -80,8 +80,7 @@ public class MCBarrel { // This is the last viewer for (ItemStack item : inv.getContents()) { if (item != null) { - Brew brew = Brew.get(item); - if (brew != null) { + if (Brew.isBrew(item)) { // We found a brew, so set time on this Barrel if (inv.getHolder() instanceof org.bukkit.block.Barrel) { Barrel barrel = (Barrel) inv.getHolder(); @@ -97,6 +96,26 @@ public class MCBarrel { } } + public void countBrews() { + brews = 0; + for (ItemStack item : inv.getContents()) { + if (item != null) { + if (Brew.isBrew(item)) { + brews++; + } + } + } + } + + public Inventory getInventory() { + return inv; + } + + + public static void onUpdate() { + mcBarrelTime++; + } + // Used to visually stop Players from placing more than 6 (configurable) brews in the MC Barrels. // There are still methods to place more Brews in that would be too tedious to catch. // This is only for direct visual Notification, the age routine above will never age more than 6 brews in any case. @@ -113,11 +132,9 @@ public class MCBarrel { case SWAP_WITH_CURSOR: // Placing Brew in MC Barrel if (event.getCursor() != null && event.getClickedInventory() != null && event.getClickedInventory().getType() == InventoryType.BARREL && event.getCursor().getType() == Material.POTION) { - Brew b = Brew.get(event.getCursor()); - if (b != null) { + if (Brew.isBrew(event.getCursor())) { if (event.getAction() == InventoryAction.SWAP_WITH_CURSOR && event.getCurrentItem() != null && event.getCurrentItem().getType() == Material.POTION) { - Brew bb = Brew.get(event.getCurrentItem()); - if (bb != null) { + if (Brew.isBrew(event.getCurrentItem())) { // The item we are swapping with is also a brew, dont change the count and allow break; } @@ -130,8 +147,7 @@ public class MCBarrel { if (event.getCurrentItem() != null && event.getCurrentItem().getType() == Material.POTION && event.getClickedInventory() != null) { if (event.getClickedInventory().getType() == InventoryType.BARREL) { // Moving Brew out of MC Barrel - Brew b = Brew.get(event.getCurrentItem()); - if (b != null) { + if (Brew.isBrew(event.getCurrentItem())) { if (brews == -1) { countBrews(); } @@ -140,8 +156,7 @@ public class MCBarrel { break; } else if (event.getClickedInventory().getType() == InventoryType.PLAYER) { // Moving Brew into MC Barrel - Brew b = Brew.get(event.getCurrentItem()); - if (b != null) { + if (Brew.isBrew(event.getCurrentItem())) { adding = true; } } @@ -155,8 +170,7 @@ public class MCBarrel { case COLLECT_TO_CURSOR: // Pickup Brew from MC Barrel if (event.getCurrentItem() != null && event.getClickedInventory() != null && event.getClickedInventory().getType() == InventoryType.BARREL && event.getCurrentItem().getType() == Material.POTION) { - Brew b = Brew.get(event.getCurrentItem()); - if (b != null) { + if (Brew.isBrew(event.getCurrentItem())) { if (brews == -1) { countBrews(); } @@ -184,24 +198,4 @@ public class MCBarrel { } } - public void countBrews() { - brews = 0; - for (ItemStack item : inv.getContents()) { - if (item != null) { - Brew brew = Brew.get(item); - if (brew != null) { - brews++; - } - } - } - } - - public Inventory getInventory() { - return inv; - } - - public static void onUpdate() { - mcBarrelTime++; - } - } diff --git a/src/com/dre/brewery/P.java b/src/com/dre/brewery/P.java index f891907..e3020f5 100644 --- a/src/com/dre/brewery/P.java +++ b/src/com/dre/brewery/P.java @@ -1,62 +1,40 @@ package com.dre.brewery; -import com.dre.brewery.filedata.ConfigUpdater; +import com.dre.brewery.filedata.BConfig; +import com.dre.brewery.filedata.BData; import com.dre.brewery.filedata.DataSave; -import com.dre.brewery.filedata.DataUpdater; import com.dre.brewery.filedata.LanguageReader; import com.dre.brewery.filedata.UpdateChecker; -import com.dre.brewery.integration.LogBlockBarrel; -import com.dre.brewery.integration.WGBarrel; -import com.dre.brewery.integration.WGBarrel7; -import com.dre.brewery.integration.WGBarrelNew; -import com.dre.brewery.integration.WGBarrelOld; +import com.dre.brewery.integration.IntegrationListener; +import com.dre.brewery.integration.barrel.LogBlockBarrel; import com.dre.brewery.listeners.*; +import com.dre.brewery.recipe.BCauldronRecipe; +import com.dre.brewery.recipe.BRecipe; +import com.dre.brewery.utility.BUtil; +import com.dre.brewery.utility.LegacyUtil; import org.apache.commons.lang.math.NumberUtils; import org.bstats.bukkit.Metrics; import org.bukkit.Bukkit; import org.bukkit.ChatColor; -import org.bukkit.Location; import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.block.Block; import org.bukkit.command.CommandSender; -import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.command.PluginCommand; import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Player; import org.bukkit.event.HandlerList; -import org.bukkit.inventory.ItemStack; -import org.bukkit.plugin.Plugin; import org.bukkit.plugin.java.JavaPlugin; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; -import java.util.UUID; public class P extends JavaPlugin { public static P p; - public static final String configVersion = "1.8"; public static boolean debug; public static boolean useUUID; + public static boolean useNBT; public static boolean use1_9; public static boolean use1_11; public static boolean use1_13; public static boolean use1_14; - public static boolean updateCheck; - - // Third Party Enabled - public boolean useWG; //WorldGuard - public WGBarrel wg; - public boolean useLWC; //LWC - public boolean useLB; //LogBlock - public boolean useGP; //GriefPrevention - public boolean hasVault; // Vault - public boolean useCitadel; // CivCraft/DevotedMC Citadel // Listeners public BlockListener blockListener; @@ -64,12 +42,16 @@ public class P extends JavaPlugin { public EntityListener entityListener; public InventoryListener inventoryListener; public WorldListener worldListener; + public IntegrationListener integrationListener; // Language public String language; public LanguageReader languageReader; - private CommandSender reloader; + // Metrics + public int brewsCreated; + public int brewsCreatedCmd; // Created by command + public int exc, good, norm, bad, terr; // Brews drunken with quality @Override public void onEnable() { @@ -83,81 +65,37 @@ public class P extends JavaPlugin { use1_13 = !v.matches("(^|.*[^.\\d])1\\.1[0-2]([^\\d].*|$)") && !v.matches("(^|.*[^.\\d])1\\.[0-9]([^\\d].*|$)"); use1_14 = !v.matches("(^|.*[^.\\d])1\\.1[0-3]([^\\d].*|$)") && !v.matches("(^|.*[^.\\d])1\\.[0-9]([^\\d].*|$)"); - //P.p.log("§" + (use1_9 ? "a":"c") + "1.9 " + "§" + (use1_11 ? "a":"c") + "1.11 " + "§" + (use1_13 ? "a":"c") + "1.13 " + "§" + (use1_14 ? "a":"c") + "1.14"); + //MC 1.13 uses a different NBT API than the newer versions.. + // We decide here which to use, the new or the old or none at all + if (LegacyUtil.initNbt()) { + useNBT = true; + } + + if (use1_14) { + // Campfires are weird + // Initialize once now so it doesn't lag later when we check for campfires under Cauldrons + getServer().createBlockData(Material.CAMPFIRE); + } // load the Config try { - if (!readConfig()) { + FileConfiguration cfg = BConfig.loadConfigFile(); + if (cfg == null) { p = null; getServer().getPluginManager().disablePlugin(this); return; } + BConfig.readConfig(cfg); } catch (Exception e) { e.printStackTrace(); p = null; getServer().getPluginManager().disablePlugin(this); return; } - readData(); + BData.readData(); // Setup Metrics - try { - Metrics metrics = new Metrics(this); - metrics.addCustomChart(new Metrics.SingleLineChart("drunk_players", BPlayer::numDrunkPlayers)); - metrics.addCustomChart(new Metrics.SingleLineChart("brews_in_existence", () -> Brew.potions.size())); - metrics.addCustomChart(new Metrics.SingleLineChart("barrels_built", () -> Barrel.barrels.size())); - metrics.addCustomChart(new Metrics.SingleLineChart("cauldrons_boiling", () -> BCauldron.bcauldrons.size())); - metrics.addCustomChart(new Metrics.AdvancedPie("brew_quality", () -> { - Map map = new HashMap<>(5); - int exc = 0; - int good = 0; - int norm = 0; - int bad = 0; - int terr = 0; - for (Brew brew : Brew.potions.values()) { - if (brew.getQuality() >= 9) { - exc++; - } else if (brew.getQuality() >= 7) { - good++; - } else if (brew.getQuality() >= 5) { - norm++; - } else if (brew.getQuality() >= 3) { - bad++; - } else { - terr++; - } - } - - map.put("excellent", exc); - map.put("good", good); - map.put("normal", norm); - map.put("bad", bad); - map.put("terrible", terr); - return map; - })); - metrics.addCustomChart(new Metrics.SimplePie("number_of_recipes", () -> { - int recipes = BIngredients.recipes.size(); - if (recipes < 7) { - return "Less than 7"; - } else if (recipes < 11) { - return "7-10"; - } else if (recipes == 11) { - // There are 11 default recipes, so show this as its own slice - return "11"; - } else if (recipes <= 31) { - if (recipes % 2 == 0) { - return recipes + "-" + (recipes + 1); - } else { - return (recipes - 1) + "-" + recipes; - } - } else { - return "More than 31"; - } - - })); - } catch (Throwable e) { - e.printStackTrace(); - } + setupMetrics(); // Listeners blockListener = new BlockListener(); @@ -165,14 +103,19 @@ public class P extends JavaPlugin { entityListener = new EntityListener(); inventoryListener = new InventoryListener(); worldListener = new WorldListener(); - getCommand("Brewery").setExecutor(new CommandListener()); - getCommand("Brewery").setTabCompleter(new TabListener()); + integrationListener = new IntegrationListener(); + PluginCommand c = getCommand("Brewery"); + if (c != null) { + c.setExecutor(new CommandListener()); + c.setTabCompleter(new TabListener()); + } p.getServer().getPluginManager().registerEvents(blockListener, p); p.getServer().getPluginManager().registerEvents(playerListener, p); p.getServer().getPluginManager().registerEvents(entityListener, p); p.getServer().getPluginManager().registerEvents(inventoryListener, p); p.getServer().getPluginManager().registerEvents(worldListener, p); + p.getServer().getPluginManager().registerEvents(integrationListener, p); if (use1_9) { p.getServer().getPluginManager().registerEvents(new CauldronListener(), p); } @@ -181,7 +124,7 @@ public class P extends JavaPlugin { p.getServer().getScheduler().runTaskTimer(p, new BreweryRunnable(), 650, 1200); p.getServer().getScheduler().runTaskTimer(p, new DrunkRunnable(), 120, 120); - if (updateCheck) { + if (BConfig.updateCheck) { try { p.getServer().getScheduler().runTaskLaterAsynchronously(p, new UpdateChecker(), 135); } catch (Exception e) { @@ -208,52 +151,28 @@ public class P extends JavaPlugin { // save Data to Disk DataSave.save(true); - // save LanguageReader - languageReader.save(); - - // delete Data from Ram - Barrel.barrels.clear(); - BCauldron.bcauldrons.clear(); - BIngredients.possibleIngredients.clear(); - BIngredients.recipes.clear(); - BIngredients.cookedNames.clear(); - BPlayer.clear(); - Brew.potions.clear(); - Wakeup.wakeups.clear(); - Words.words.clear(); - Words.ignoreText.clear(); - Words.commands = null; + // delete config data, in case this is a reload and to clear up some ram + clearConfigData(); this.log(this.getDescription().getName() + " disabled!"); } public void reload(CommandSender sender) { if (sender != null && !sender.equals(getServer().getConsoleSender())) { - reloader = sender; + BConfig.reloader = sender; } + FileConfiguration cfg = BConfig.loadConfigFile(); + if (cfg == null) { + // Could not read yml file, do not proceed, error was printed + return; + } + // clear all existent config Data - BIngredients.possibleIngredients.clear(); - BIngredients.recipes.clear(); - BIngredients.cookedNames.clear(); - Words.words.clear(); - Words.ignoreText.clear(); - Words.commands = null; - BPlayer.drainItems.clear(); - if (useLB) { - try { - LogBlockBarrel.clear(); - } catch (Exception e) { - e.printStackTrace(); - } - } + clearConfigData(); // load the Config try { - if (!readConfig()) { - p = null; - getServer().getPluginManager().disablePlugin(this); - return; - } + BConfig.readConfig(cfg); } catch (Exception e) { e.printStackTrace(); p = null; @@ -261,23 +180,179 @@ public class P extends JavaPlugin { return; } - // save and load LanguageReader - languageReader.save(); - languageReader = new LanguageReader(new File(p.getDataFolder(), "languages/" + language + ".yml")); - // Reload Recipes boolean successful = true; - for (Brew brew : Brew.potions.values()) { + for (Brew brew : Brew.legacyPotions.values()) { if (!brew.reloadRecipe()) { successful = false; } } - if (!successful && sender != null) { - msg(sender, p.languageReader.get("Error_Recipeload")); + if (sender != null) { + if (!successful) { + msg(sender, p.languageReader.get("Error_Recipeload")); + } else { + p.msg(sender, p.languageReader.get("CMD_Reload")); + } } - reloader = null; + BConfig.reloader = null; } + private void clearConfigData() { + BRecipe.getConfigRecipes().clear(); + BRecipe.numConfigRecipes = 0; + BCauldronRecipe.acceptedMaterials.clear(); + BCauldronRecipe.acceptedCustom.clear(); + BCauldronRecipe.acceptedSimple.clear(); + BCauldronRecipe.getConfigRecipes().clear(); + BCauldronRecipe.numConfigRecipes = 0; + BConfig.customItems.clear(); + BConfig.hasSlimefun = null; + BConfig.hasMMOItems = null; + DistortChat.commands = null; + BConfig.drainItems.clear(); + if (BConfig.useLB) { + try { + LogBlockBarrel.clear(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + public static P getInstance() { + return p; + } + + private void setupMetrics() { + try { + Metrics metrics = new Metrics(this); + metrics.addCustomChart(new Metrics.SingleLineChart("drunk_players", BPlayer::numDrunkPlayers)); + metrics.addCustomChart(new Metrics.SingleLineChart("brews_in_existence", () -> brewsCreated)); + metrics.addCustomChart(new Metrics.SingleLineChart("barrels_built", () -> Barrel.barrels.size())); + metrics.addCustomChart(new Metrics.SingleLineChart("cauldrons_boiling", () -> BCauldron.bcauldrons.size())); + metrics.addCustomChart(new Metrics.AdvancedPie("brew_quality", () -> { + Map map = new HashMap<>(8); + map.put("excellent", exc); + map.put("good", good); + map.put("normal", norm); + map.put("bad", bad); + map.put("terrible", terr); + return map; + })); + metrics.addCustomChart(new Metrics.AdvancedPie("brews_created", () -> { + Map map = new HashMap<>(4); + map.put("by command", brewsCreatedCmd); + map.put("brewing", brewsCreated - brewsCreatedCmd); + return map; + })); + + metrics.addCustomChart(new Metrics.SimplePie("number_of_recipes", () -> { + int recipes = BRecipe.getAllRecipes().size(); + if (recipes < 7) { + return "Less than 7"; + } else if (recipes < 11) { + return "7-10"; + } else if (recipes == 11) { + // There are 11 default recipes, so show this as its own slice + return "11"; + } else if (recipes <= 31) { + if (recipes % 2 == 0) { + return recipes + "-" + (recipes + 1); + } else { + return (recipes - 1) + "-" + recipes; + } + } else { + return "More than 31"; + } + + })); + metrics.addCustomChart(new Metrics.SimplePie("v2_mc_version", () -> { + String mcv = Bukkit.getBukkitVersion(); + mcv = mcv.substring(0, mcv.indexOf('.', 2)); + if (mcv.matches("^\\d\\.\\d{1,2}$")) { + // Start, digit, dot, 1-2 digits, end + return mcv; + } else { + return "undef"; + } + })); + metrics.addCustomChart(new Metrics.DrilldownPie("plugin_mc_version", () -> { + Map> map = new HashMap<>(3); + String mcv = Bukkit.getBukkitVersion(); + mcv = mcv.substring(0, mcv.indexOf('.', 2)); + if (mcv.matches("^\\d\\.\\d{1,2}$")) { + // Start, digit, dot, 1-2 digits, end + mcv = "MC " + mcv; + } else { + mcv = "undef"; + } + Map innerMap = new HashMap<>(3); + innerMap.put(mcv, 1); + map.put(getDescription().getVersion(), innerMap); + return map; + })); + metrics.addCustomChart(new Metrics.SimplePie("language", () -> language)); + metrics.addCustomChart(new Metrics.SimplePie("config_scramble", () -> BConfig.enableEncode ? "enabled" : "disabled")); + metrics.addCustomChart(new Metrics.SimplePie("config_lore_color", () -> { + if (BConfig.colorInBarrels) { + if (BConfig.colorInBrewer) { + return "both"; + } else { + return "in barrels"; + } + } else { + if (BConfig.colorInBrewer) { + return "in distiller"; + } else { + return "none"; + } + } + })); + metrics.addCustomChart(new Metrics.SimplePie("config_always_show", () -> { + if (BConfig.alwaysShowQuality) { + if (BConfig.alwaysShowAlc) { + return "both"; + } else { + return "quality stars"; + } + } else { + if (BConfig.alwaysShowAlc) { + return "alc content"; + } else { + return "none"; + } + } + })); + } catch (Throwable e) { + e.printStackTrace(); + } + } + + public void metricsForCreate(boolean byCmd) { + if (brewsCreated == Integer.MAX_VALUE) return; + brewsCreated++; + if (byCmd) { + if (brewsCreatedCmd == Integer.MAX_VALUE) return; + brewsCreatedCmd++; + } + } + + public void metricsForDrink(Brew brew) { + if (brew.getQuality() >= 9) { + exc++; + } else if (brew.getQuality() >= 7) { + good++; + } else if (brew.getQuality() >= 5) { + norm++; + } else if (brew.getQuality() >= 3) { + bad++; + } else { + terr++; + } + } + + // Utility + public void msg(CommandSender sender, String msg) { sender.sendMessage(color("&2[Brewery] &f" + msg)); } @@ -294,507 +369,23 @@ public class P extends JavaPlugin { public void errorLog(String msg) { Bukkit.getConsoleSender().sendMessage(ChatColor.DARK_GREEN + "[Brewery] " + ChatColor.DARK_RED + "ERROR: " + ChatColor.RED + msg); - if (reloader != null) { - reloader.sendMessage(ChatColor.DARK_GREEN + "[Brewery] " + ChatColor.DARK_RED + "ERROR: " + ChatColor.RED + msg); + if (BConfig.reloader != null) { + BConfig.reloader.sendMessage(ChatColor.DARK_GREEN + "[Brewery] " + ChatColor.DARK_RED + "ERROR: " + ChatColor.RED + msg); } } - public boolean readConfig() { - File file = new File(p.getDataFolder(), "config.yml"); - if (!checkConfigs()) { - return false; - } - FileConfiguration config = YamlConfiguration.loadConfiguration(file); - - // Set the Language - language = config.getString("language", "en"); - - // Load LanguageReader - languageReader = new LanguageReader(new File(p.getDataFolder(), "languages/" + language + ".yml")); - - // Has to config still got old materials - boolean oldMat = config.getBoolean("oldMat", false); - - // Check if config is the newest version - String version = config.getString("version", null); - if (version != null) { - if (!version.equals(configVersion) || (oldMat && use1_13)) { - copyDefaultConfigs(true); - new ConfigUpdater(file).update(version, oldMat, language); - P.p.log("Config Updated to version: " + configVersion); - config = YamlConfiguration.loadConfiguration(file); - } - } - - // If the Update Checker should be enabled - updateCheck = config.getBoolean("updateCheck", false); - - // Third-Party - useWG = config.getBoolean("useWorldGuard", true) && getServer().getPluginManager().isPluginEnabled("WorldGuard"); - if (useWG) { - Plugin plugin = Bukkit.getPluginManager().getPlugin("WorldEdit"); - if (plugin != null) { - String wgv = plugin.getDescription().getVersion(); - if (wgv.startsWith("6.")) { - wg = new WGBarrelNew(); - } else if (wgv.startsWith("5.")) { - wg = new WGBarrelOld(); - } else { - wg = new WGBarrel7(); - } - } - if (wg == null) { - P.p.errorLog("Failed loading WorldGuard Integration! Opening Barrels will NOT work!"); - P.p.errorLog("Brewery was tested with version 5.8, 6.1 and 7.0 of WorldGuard!"); - P.p.errorLog("Disable the WorldGuard support in the config and do /brew reload"); - } - } - useLWC = config.getBoolean("useLWC", true) && getServer().getPluginManager().isPluginEnabled("LWC"); - useGP = config.getBoolean("useGriefPrevention", true) && getServer().getPluginManager().isPluginEnabled("GriefPrevention"); - useLB = config.getBoolean("useLogBlock", false) && getServer().getPluginManager().isPluginEnabled("LogBlock"); - useCitadel = config.getBoolean("useCitadel", false) && getServer().getPluginManager().isPluginEnabled("Citadel"); - // The item util has been removed in Vault 1.7+ - hasVault = getServer().getPluginManager().isPluginEnabled("Vault") - && Integer.parseInt(getServer().getPluginManager().getPlugin("Vault").getDescription().getVersion().split("\\.")[1]) <= 6; - - // various Settings - DataSave.autosave = config.getInt("autosave", 3); - debug = config.getBoolean("debug", false); - BPlayer.pukeItem = Material.matchMaterial(config.getString("pukeItem", "SOUL_SAND")); - BPlayer.hangoverTime = config.getInt("hangoverDays", 0) * 24 * 60; - BPlayer.overdrinkKick = config.getBoolean("enableKickOnOverdrink", false); - BPlayer.enableHome = config.getBoolean("enableHome", false); - BPlayer.enableLoginDisallow = config.getBoolean("enableLoginDisallow", false); - BPlayer.enablePuke = config.getBoolean("enablePuke", false); - BPlayer.pukeDespawntime = config.getInt("pukeDespawntime", 60) * 20; - BPlayer.homeType = config.getString("homeType", null); - Brew.colorInBarrels = config.getBoolean("colorInBarrels", false); - Brew.colorInBrewer = config.getBoolean("colorInBrewer", false); - PlayerListener.openEverywhere = config.getBoolean("openLargeBarrelEverywhere", false); - MCBarrel.maxBrews = config.getInt("maxBrewsInMCBarrels", 6); - - // loading recipes - ConfigurationSection configSection = config.getConfigurationSection("recipes"); - if (configSection != null) { - for (String recipeId : configSection.getKeys(false)) { - BRecipe recipe = new BRecipe(configSection, recipeId); - if (recipe.isValid()) { - BIngredients.recipes.add(recipe); - } else { - errorLog("Loading the Recipe with id: '" + recipeId + "' failed!"); - } - } - } - - // loading cooked names and possible ingredients - configSection = config.getConfigurationSection("cooked"); - if (configSection != null) { - for (String ingredient : configSection.getKeys(false)) { - Material mat = Material.matchMaterial(ingredient); - if (mat == null && hasVault) { - try { - net.milkbowl.vault.item.ItemInfo vaultItem = net.milkbowl.vault.item.Items.itemByString(ingredient); - if (vaultItem != null) { - mat = vaultItem.getType(); - } - } catch (Exception e) { - P.p.errorLog("Could not check vault for Item Name"); - e.printStackTrace(); - } - } - if (mat != null) { - BIngredients.cookedNames.put(mat, (configSection.getString(ingredient, null))); - BIngredients.possibleIngredients.add(mat); - } else { - errorLog("Unknown Material: " + ingredient); - } - } - } - - // loading drainItems - List drainList = config.getStringList("drainItems"); - if (drainList != null) { - for (String drainString : drainList) { - String[] drainSplit = drainString.split("/"); - if (drainSplit.length > 1) { - Material mat = Material.matchMaterial(drainSplit[0]); - int strength = p.parseInt(drainSplit[1]); - if (mat == null && hasVault && strength > 0) { - try { - net.milkbowl.vault.item.ItemInfo vaultItem = net.milkbowl.vault.item.Items.itemByString(drainSplit[0]); - if (vaultItem != null) { - mat = vaultItem.getType(); - } - } catch (Exception e) { - P.p.errorLog("Could not check vault for Item Name"); - e.printStackTrace(); - } - } - if (mat != null && strength > 0) { - BPlayer.drainItems.put(mat, strength); - } - } - } - } - - // Loading Words - if (config.getBoolean("enableChatDistortion", false)) { - for (Map map : config.getMapList("words")) { - new Words(map); - } - for (String bypass : config.getStringList("distortBypass")) { - Words.ignoreText.add(bypass.split(",")); - } - Words.commands = config.getStringList("distortCommands"); - } - Words.log = config.getBoolean("logRealChat", false); - Words.doSigns = config.getBoolean("distortSignText", false); - - return true; - } - - // load all Data - public void readData() { - File file = new File(p.getDataFolder(), "data.yml"); - if (file.exists()) { - - FileConfiguration data = YamlConfiguration.loadConfiguration(file); - - Brew.installTime = data.getLong("installTime", System.currentTimeMillis()); - MCBarrel.mcBarrelTime = data.getLong("MCBarrelTime", 0); - - // Check if data is the newest version - String version = data.getString("Version", null); - if (version != null) { - if (!version.equals(DataSave.dataVersion)) { - P.p.log("Data File is being updated..."); - new DataUpdater(data, file).update(version); - data = YamlConfiguration.loadConfiguration(file); - P.p.log("Data Updated to version: " + DataSave.dataVersion); - } - } - - // loading Ingredients into ingMap - Map ingMap = new HashMap<>(); - ConfigurationSection section = data.getConfigurationSection("Ingredients"); - if (section != null) { - for (String id : section.getKeys(false)) { - ConfigurationSection matSection = section.getConfigurationSection(id + ".mats"); - if (matSection != null) { - // matSection has all the materials + amount as Integers - ArrayList ingredients = deserializeIngredients(matSection); - ingMap.put(id, new BIngredients(ingredients, section.getInt(id + ".cookedTime", 0))); - } else { - errorLog("Ingredient id: '" + id + "' incomplete in data.yml"); - } - } - } - - // loading Brew - section = data.getConfigurationSection("Brew"); - if (section != null) { - // All sections have the UID as name - for (String uid : section.getKeys(false)) { - BIngredients ingredients = getIngredients(ingMap, section.getString(uid + ".ingId")); - int quality = section.getInt(uid + ".quality", 0); - int distillRuns = section.getInt(uid + ".distillRuns", 0); - float ageTime = (float) section.getDouble(uid + ".ageTime", 0.0); - float wood = (float) section.getDouble(uid + ".wood", -1.0); - String recipe = section.getString(uid + ".recipe", null); - boolean unlabeled = section.getBoolean(uid + ".unlabeled", false); - boolean persistent = section.getBoolean(uid + ".persist", false); - boolean stat = section.getBoolean(uid + ".stat", false); - int lastUpdate = section.getInt("lastUpdate", 0); - - new Brew(parseInt(uid), ingredients, quality, distillRuns, ageTime, wood, recipe, unlabeled, persistent, stat, lastUpdate); - } - } - - // loading BPlayer - section = data.getConfigurationSection("Player"); - if (section != null) { - // keys have players name - for (String name : section.getKeys(false)) { - try { - //noinspection ResultOfMethodCallIgnored - UUID.fromString(name); - if (!useUUID) { - continue; - } - } catch (IllegalArgumentException e) { - if (useUUID) { - continue; - } - } - - int quality = section.getInt(name + ".quality"); - int drunk = section.getInt(name + ".drunk"); - int offDrunk = section.getInt(name + ".offDrunk", 0); - - new BPlayer(name, quality, drunk, offDrunk); - } - } - - for (World world : p.getServer().getWorlds()) { - if (world.getName().startsWith("DXL_")) { - loadWorldData(Util.getDxlName(world.getName()), world); - } else { - loadWorldData(world.getUID().toString(), world); - } - } - - } else { - errorLog("No data.yml found, will create new one!"); - } - } - - public ArrayList deserializeIngredients(ConfigurationSection matSection) { - ArrayList ingredients = new ArrayList<>(); - for (String mat : matSection.getKeys(false)) { - String[] matSplit = mat.split(","); - Material m = Material.getMaterial(matSplit[0]); - if (m == null && use1_13) { - if (matSplit[0].equals("LONG_GRASS")) { - m = Material.GRASS; - } else { - m = Material.matchMaterial(matSplit[0], true); - } - debugLog("converting Data Material from " + matSplit[0] + " to " + m); - } - if (m == null) continue; - ItemStack item = new ItemStack(m, matSection.getInt(mat)); - if (matSplit.length == 2) { - item.setDurability((short) P.p.parseInt(matSplit[1])); - } - ingredients.add(item); - } - return ingredients; - } - - // returns Ingredients by id from the specified ingMap - public BIngredients getIngredients(Map ingMap, String id) { - if (!ingMap.isEmpty()) { - if (ingMap.containsKey(id)) { - return ingMap.get(id); - } - } - errorLog("Ingredient id: '" + id + "' not found in data.yml"); - return new BIngredients(); - } - - // loads BIngredients from an ingredient section - public BIngredients loadIngredients(ConfigurationSection section) { - if (section != null) { - return new BIngredients(deserializeIngredients(section), 0); - } else { - errorLog("Cauldron is missing Ingredient Section"); - } - return new BIngredients(); - } - - // load Block locations of given world - public void loadWorldData(String uuid, World world) { - - File file = new File(p.getDataFolder(), "data.yml"); - if (file.exists()) { - - FileConfiguration data = YamlConfiguration.loadConfiguration(file); - - // loading BCauldron - if (data.contains("BCauldron." + uuid)) { - ConfigurationSection section = data.getConfigurationSection("BCauldron." + uuid); - for (String cauldron : section.getKeys(false)) { - // block is splitted into x/y/z - String block = section.getString(cauldron + ".block"); - if (block != null) { - String[] splitted = block.split("/"); - if (splitted.length == 3) { - - Block worldBlock = world.getBlockAt(parseInt(splitted[0]), parseInt(splitted[1]), parseInt(splitted[2])); - BIngredients ingredients = loadIngredients(section.getConfigurationSection(cauldron + ".ingredients")); - int state = section.getInt(cauldron + ".state", 1); - - new BCauldron(worldBlock, ingredients, state); - } else { - errorLog("Incomplete Block-Data in data.yml: " + section.getCurrentPath() + "." + cauldron); - } - } else { - errorLog("Missing Block-Data in data.yml: " + section.getCurrentPath() + "." + cauldron); - } - } - } - - // loading Barrel - if (data.contains("Barrel." + uuid)) { - ConfigurationSection section = data.getConfigurationSection("Barrel." + uuid); - for (String barrel : section.getKeys(false)) { - // block spigot is splitted into x/y/z - String spigot = section.getString(barrel + ".spigot"); - if (spigot != null) { - String[] splitted = spigot.split("/"); - if (splitted.length == 3) { - - // load itemStacks from invSection - ConfigurationSection invSection = section.getConfigurationSection(barrel + ".inv"); - Block block = world.getBlockAt(parseInt(splitted[0]), parseInt(splitted[1]), parseInt(splitted[2])); - float time = (float) section.getDouble(barrel + ".time", 0.0); - byte sign = (byte) section.getInt(barrel + ".sign", 0); - String[] st = section.getString(barrel + ".st", "").split(","); - String[] wo = section.getString(barrel + ".wo", "").split(","); - - if (invSection != null) { - new Barrel(block, sign, st, wo, invSection.getValues(true), time); - } else { - // Barrel has no inventory - new Barrel(block, sign, st, wo, null, time); - } - - } else { - errorLog("Incomplete Block-Data in data.yml: " + section.getCurrentPath() + "." + barrel); - } - } else { - errorLog("Missing Block-Data in data.yml: " + section.getCurrentPath() + "." + barrel); - } - } - } - - // loading Wakeup - if (data.contains("Wakeup." + uuid)) { - ConfigurationSection section = data.getConfigurationSection("Wakeup." + uuid); - for (String wakeup : section.getKeys(false)) { - // loc of wakeup is splitted into x/y/z/pitch/yaw - String loc = section.getString(wakeup); - if (loc != null) { - String[] splitted = loc.split("/"); - if (splitted.length == 5) { - - double x = NumberUtils.toDouble(splitted[0]); - double y = NumberUtils.toDouble(splitted[1]); - double z = NumberUtils.toDouble(splitted[2]); - float pitch = NumberUtils.toFloat(splitted[3]); - float yaw = NumberUtils.toFloat(splitted[4]); - Location location = new Location(world, x, y, z, yaw, pitch); - - Wakeup.wakeups.add(new Wakeup(location)); - - } else { - errorLog("Incomplete Location-Data in data.yml: " + section.getCurrentPath() + "." + wakeup); - } - } - } - } - - } - } - - private boolean checkConfigs() { - File cfg = new File(p.getDataFolder(), "config.yml"); - if (!cfg.exists()) { - errorLog("No config.yml found, creating default file! You may want to choose a config according to your language!"); - errorLog("You can find them in plugins/Brewery/configs/"); - InputStream defconf = getResource("config/" + (use1_13 ? "v13/" : "v12/") + "en/config.yml"); - if (defconf == null) { - errorLog("default config file not found, your jarfile may be corrupt. Disabling Brewery!"); - return false; - } - try { - Util.saveFile(defconf, getDataFolder(), "config.yml", false); - } catch (IOException e) { - e.printStackTrace(); - return false; - } - } - if (!cfg.exists()) { - errorLog("default config file could not be copied, your jarfile may be corrupt. Disabling Brewery!"); - return false; - } - - copyDefaultConfigs(false); - return true; - } - - private void copyDefaultConfigs(boolean overwrite) { - File configs = new File(getDataFolder(), "configs"); - File languages = new File(getDataFolder(), "languages"); - for (String l : new String[] {"de", "en", "fr", "it", "zh", "tw"}) { - File lfold = new File(configs, l); - try { - Util.saveFile(getResource("config/" + (use1_13 ? "v13/" : "v12/") + l + "/config.yml"), lfold, "config.yml", overwrite); - Util.saveFile(getResource("languages/" + l + ".yml"), languages, l + ".yml", false); // Never overwrite languages for now - } catch (IOException e) { - if (!(l.equals("zh") || l.equals("tw"))) { - e.printStackTrace(); - } - } - } - } - - // Utility - public int parseInt(String string) { return NumberUtils.toInt(string, 0); } - - // Returns true if the Block can be destroyed by the Player or something else (null) - public boolean blockDestroy(Block block, Player player) { - Material type = block.getType(); - if (type == Material.CAULDRON) { - // will only remove when existing - BCauldron.remove(block); - return true; - - } else if (LegacyUtil.isFence(type)) { - // remove barrel and throw potions on the ground - Barrel barrel = Barrel.getBySpigot(block); - if (barrel != null) { - if (barrel.hasPermsDestroy(player)) { - barrel.remove(null, player); - return true; - } else { - return false; - } - } - return true; - - } else if (LegacyUtil.isSign(type)) { - // remove small Barrels - Barrel barrel2 = Barrel.getBySpigot(block); - if (barrel2 != null) { - if (!barrel2.isLarge()) { - if (barrel2.hasPermsDestroy(player)) { - barrel2.remove(null, player); - return true; - } else { - return false; - } - } else { - barrel2.destroySign(); - } - } - return true; - - } else if (LegacyUtil.isWoodPlanks(type) || LegacyUtil.isWoodStairs(type)){ - Barrel barrel3 = Barrel.getByWood(block); - if (barrel3 != null) { - if (barrel3.hasPermsDestroy(player)) { - barrel3.remove(block, player); - } else { - return false; - } - } - } - return true; - } - public String color(String msg) { - return Util.color(msg); + return BUtil.color(msg); } // Runnables - public class DrunkRunnable implements Runnable { + public static class DrunkRunnable implements Runnable { @Override public void run() { if (!BPlayer.isEmpty()) { @@ -806,17 +397,28 @@ public class P extends JavaPlugin { public class BreweryRunnable implements Runnable { @Override public void run() { - reloader = null; - for (BCauldron cauldron : BCauldron.bcauldrons) { + long t1 = System.nanoTime(); + BConfig.reloader = null; + for (BCauldron cauldron : BCauldron.bcauldrons.values()) { cauldron.onUpdate();// runs every min to update cooking time } + long t2 = System.nanoTime(); Barrel.onUpdate();// runs every min to check and update ageing time + long t3 = System.nanoTime(); if (use1_14) MCBarrel.onUpdate(); + long t4 = System.nanoTime(); BPlayer.onUpdate();// updates players drunkeness - debugLog("Update"); - + long t5 = System.nanoTime(); DataSave.autoSave(); + long t6 = System.nanoTime(); + + debugLog("BreweryRunnable: " + + "t1: " + (t2 - t1) / 1000000.0 + "ms" + + " | t2: " + (t3 - t2) / 1000000.0 + "ms" + + " | t3: " + (t4 - t3) / 1000000.0 + "ms" + + " | t4: " + (t5 - t4) / 1000000.0 + "ms" + + " | t5: " + (t6 - t5) / 1000000.0 + "ms" ); } } diff --git a/src/com/dre/brewery/Util.java b/src/com/dre/brewery/Util.java deleted file mode 100644 index bc7cf4a..0000000 --- a/src/com/dre/brewery/Util.java +++ /dev/null @@ -1,157 +0,0 @@ -package com.dre.brewery; - -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.World; -import org.bukkit.block.Block; -import org.bukkit.command.CommandSender; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.entity.Player; -import org.bukkit.potion.PotionEffect; -import org.bukkit.potion.PotionEffectType; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.ListIterator; -import java.util.UUID; - -public class Util { - - /********** Bukkit Utils **********/ - - // Check if the Chunk of a Block is loaded !without loading it in the process! - public static boolean isChunkLoaded(Block block) { - return block.getWorld().isChunkLoaded(block.getX() >> 4, block.getZ() >> 4); - } - - public static String color(String msg) { - if (msg != null) { - msg = ChatColor.translateAlternateColorCodes('&', msg); - } - return msg; - } - - // Returns either uuid or Name of player, depending on bukkit version - public static String playerString(Player player) { - if (P.useUUID) { - return player.getUniqueId().toString(); - } else { - return player.getName(); - } - } - - // returns the Player if online - public static Player getPlayerfromString(String name) { - if (P.useUUID) { - try { - return Bukkit.getPlayer(UUID.fromString(name)); - } catch (Exception e) { - return Bukkit.getPlayerExact(name); - } - } - return Bukkit.getPlayerExact(name); - } - - // Apply a Potion Effect, if player already has this effect, overwrite the existing effect. - // Optionally only overwrite if the new one is stronger, i.e. has higher level or longer duration - public static void reapplyPotionEffect(Player player, PotionEffect effect, boolean onlyIfStronger) { - final PotionEffectType type = effect.getType(); - if (player.hasPotionEffect(type)) { - PotionEffect plEffect; - if (P.use1_11) { - plEffect = player.getPotionEffect(type); - } else { - plEffect = player.getActivePotionEffects().stream().filter(e -> e.getType().equals(type)).findAny().get(); - } - if (plEffect.getAmplifier() < effect.getAmplifier() || (plEffect.getAmplifier() == effect.getAmplifier() && plEffect.getDuration() < effect.getDuration())) { - player.removePotionEffect(type); - } else { - return; - } - } - effect.apply(player); - } - - /********** Other Utils **********/ - - // prints a list of Strings at the specified page - public static void list(CommandSender sender, ArrayList strings, int page) { - int pages = (int) Math.ceil(strings.size() / 7F); - if (page > pages || page < 1) { - page = 1; - } - - sender.sendMessage(color("&7-------------- &f" + P.p.languageReader.get("Etc_Page") + " &6" + page + "&f/&6" + pages + " &7--------------")); - - ListIterator iter = strings.listIterator((page - 1) * 7); - - for (int i = 0; i < 7; i++) { - if (iter.hasNext()) { - sender.sendMessage(color(iter.next())); - } else { - break; - } - } - } - - // gets the Name of a DXL World - public static String getDxlName(String worldName) { - File dungeonFolder = new File(worldName); - if (dungeonFolder.isDirectory()) { - for (File file : dungeonFolder.listFiles()) { - if (!file.isDirectory()) { - if (file.getName().startsWith(".id_")) { - return file.getName().substring(1).toLowerCase(); - } - } - } - } - return worldName; - } - - // create empty World save Sections - public static void createWorldSections(ConfigurationSection section) { - for (World world : P.p.getServer().getWorlds()) { - String worldName = world.getName(); - if (worldName.startsWith("DXL_")) { - worldName = getDxlName(worldName); - } else { - worldName = world.getUID().toString(); - } - section.createSection(worldName); - } - } - - @SuppressWarnings("ResultOfMethodCallIgnored") - public static void saveFile(InputStream in, File dest, String name, boolean overwrite) throws IOException { - if (in == null) return; - if (!dest.exists()) { - dest.mkdirs(); - } - File result = new File(dest, name); - if (result.exists()) { - if (overwrite) { - result.delete(); - } else { - return; - } - } - - OutputStream out = new FileOutputStream(result); - byte[] buffer = new byte[1024]; - - int length; - //copy the file content in bytes - while ((length = in.read(buffer)) > 0){ - out.write(buffer, 0, length); - } - - in.close(); - out.close(); - } - -} diff --git a/src/com/dre/brewery/Wakeup.java b/src/com/dre/brewery/Wakeup.java index 78aed34..46e2906 100644 --- a/src/com/dre/brewery/Wakeup.java +++ b/src/com/dre/brewery/Wakeup.java @@ -1,16 +1,18 @@ package com.dre.brewery; +import com.dre.brewery.utility.BUtil; +import org.bukkit.Location; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.entity.Player; + import java.util.ArrayList; import java.util.Iterator; - -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.command.CommandSender; -import org.bukkit.Location; -import org.bukkit.entity.Player; +import java.util.List; public class Wakeup { - public static ArrayList wakeups = new ArrayList<>(); + public static List wakeups = new ArrayList<>(); public static P p = P.p; public static int checkId = -1; public static Player checkPlayer = null; @@ -140,7 +142,7 @@ public class Wakeup { locs.add("&6" + s + id + "&f" + s + ": " + world + " " + x + "," + y + "," + z); } } - Util.list(sender, locs, page); + BUtil.list(sender, locs, page); } public static void check(CommandSender sender, int id, boolean all) { @@ -228,7 +230,7 @@ public class Wakeup { public static void save(ConfigurationSection section, ConfigurationSection oldData) { - Util.createWorldSections(section); + BUtil.createWorldSections(section); // loc is saved as a String in world sections with format x/y/z/pitch/yaw if (!wakeups.isEmpty()) { @@ -245,7 +247,7 @@ public class Wakeup { String prefix; if (worldName.startsWith("DXL_")) { - prefix = Util.getDxlName(worldName) + "." + id; + prefix = BUtil.getDxlName(worldName) + "." + id; } else { prefix = wakeup.loc.getWorld().getUID().toString() + "." + id; } diff --git a/src/com/dre/brewery/api/BreweryApi.java b/src/com/dre/brewery/api/BreweryApi.java new file mode 100644 index 0000000..b53def9 --- /dev/null +++ b/src/com/dre/brewery/api/BreweryApi.java @@ -0,0 +1,372 @@ +package com.dre.brewery.api; + +import com.dre.brewery.BCauldron; +import com.dre.brewery.BPlayer; +import com.dre.brewery.filedata.BConfig; +import com.dre.brewery.recipe.BCauldronRecipe; +import com.dre.brewery.recipe.BRecipe; +import com.dre.brewery.Barrel; +import com.dre.brewery.Brew; +import org.apache.commons.lang.NotImplementedException; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * Convenience methods to get common objects or do common things. + *

Contains shortcuts and collects of some of the main functions of this Plugin + * + *

Next to this there are lots of public Methods in many Objects + * like Brew, Barrel, BCauldron, BRecipe, etc + *

In the api package, you can also find custom Events. + */ +public class BreweryApi { + + /** + * Remove any data that this Plugin may associate with the given Block. + *

Currently Cauldrons and Barrels (Cauldron, Wood, Woodstairs, Fence, Sign) + *

Does not remove any actual Blocks + *

Returns true if anything was removed + * + * @return true if anything was removed + */ + public static boolean removeAny(Block block) { + if (removeCauldron(block)) return true; + return removeBarrel(block, true); + } + + /** + *

Like removeAny() but removes data as if the given player broke the Block. + *

Currently only makes a difference for Logging + */ + public static boolean removeAnyByPlayer(Block block, Player player) { + if (removeCauldron(block)) return true; + return removeBarrelByPlayer(block, player, true); + } + + + // # # # # # # # # # # # # + // # # # # # Player # # # # # + // # # # # # # # # # # # # + + /** + * Get the BPlayer for the given Player, containing drunkeness and hangover data. + */ + public static BPlayer getBPlayer(Player player) { + return BPlayer.get(player); + } + + /** + * Set the Players drunkeness state. + * + * @param player The Player to set the drunkeness on + * @param drunkeness The amount of drunkeness 0-100 to apply to the player + * @param quality The Quality 1-10 the drunkeness of the player should have. + *
zero Quality keeps the players current quality + */ + public static void setPlayerDrunk(Player player, int drunkeness, int quality) { + if (drunkeness < 0) { + throw new IllegalArgumentException("Drunkeness can not be <0"); + } + if (quality > 10) { + throw new IllegalArgumentException("Quality can not be >10"); + } + BPlayer bPlayer = BPlayer.get(player); + if (bPlayer == null && player != null) { + if (drunkeness == 0) { + return; + } + bPlayer = BPlayer.addPlayer(player); + } + if (bPlayer == null) { + return; + } + + if (drunkeness == 0) { + bPlayer.remove(); + } else { + bPlayer.setData(drunkeness, quality); + } + + if (drunkeness > 100) { + if (player != null) { + bPlayer.drinkCap(player); + } else { + if (!BConfig.overdrinkKick) { + bPlayer.setData(100, 0); + } + } + } + } + + + // # # # # # # # # # # # # + // # # # # # Brew # # # # # + // # # # # # # # # # # # # + + /** + * Get a Brew from an ItemStack. + *

Reads the Brew data from the saved data on the item + *

Checks if item is actually a Brew + *

Returns null if item is not a Brew + */ + @Nullable + public static Brew getBrew(ItemStack item) { + return Brew.get(item); + } + + /** + * Get a Brew from an ItemMeta. + *

Reads the Brew data from the saved data in the Meta + *

Checks if meta has a Brew saved + *

Returns null if meta is not a Brew + */ + @Nullable + public static Brew getBrew(ItemMeta meta) { + return Brew.get(meta); + } + + /** + * Performant way to check if an item is a brew. + *

Does not give any guarantees that getBrew() will return notnull for this item, i.e. if it is a brew but couldn't be loaded + */ + public static boolean isBrew(ItemStack item) { + return Brew.isBrew(item); + } + + /** + * Create a Brew from the given Recipe. + * + * @param recipe The Recipe to create a brew from + * @return The Brew that was created. Can use brew.createItem() to get an ItemStack + */ + public static Brew createBrew(BRecipe recipe, int quality) { + return recipe.createBrew(quality); + } + + + // # # # # # # # # # # # # + // # # # # # Barrel # # # # # + // # # # # # # # # # # # # + + /** + * Get a Barrel from a Block. + *

May be any Wood, Fence, Sign that is part of a Barrel + *

Returns null if block is not part of a Barrel + */ + @Nullable + public static Barrel getBarrel(Block block) { + return Barrel.get(block); + } + + /** + * Get the Inventory of a Block part of a Barrel. + *

May be any Wood, Fence or Sign that is part of a Barrel + *

Returns null if block is not part of a Barrel + */ + @Nullable + public static Inventory getBarrelInventory(Block block) { + Barrel barrel = Barrel.get(block); + if (barrel != null) { + return barrel.getInventory(); + } + return null; + } + + /** + * Remove any Barrel that this Block may be Part of. + * Does not remove any actual Block + * + * @param block The Block thats part of the barrel, potions will drop there + * @param dropItems If the items in the barrels inventory should drop to the ground + * @return True if a Barrel was removed + */ + public static boolean removeBarrel(Block block, boolean dropItems) { + return removeBarrelByPlayer(block, null, dropItems); + } + + /** + * Remove any Barrel that this Block may be Part of, as if broken by the Player. + * Does not remove any actual Block from the World + * + * @param block The Block thats part of the barrel, potions will drop there + * @param player The Player that broke the Block + * @param dropItems If the items in the barrels inventory should drop to the ground + * @return True if a Barrel was removed + */ + public static boolean removeBarrelByPlayer(Block block, Player player, boolean dropItems) { + Barrel barrel = Barrel.get(block); + if (barrel != null) { + barrel.remove(block, player, dropItems); + return true; + } + return false; + } + + // # # # # # # # # # # # # + // # # # # # Cauldron # # # # # + // # # # # # # # # # # # # + + /** + * Get a BCauldron from a Block. + *

Returns null if block is not a BCauldron + */ + @Nullable + public static BCauldron getCauldron(Block block) { + return BCauldron.get(block); + } + + /** + * Remove any data associated with a Cauldron at that given Block. + *

Returns true if a Cauldron was removed + *

Does not remove the Block from the World + */ + public static boolean removeCauldron(Block block) { + return BCauldron.remove(block); + } + + + // # # # # # # # # # # # # + // # # # # # Recipe # # # # # + // # # # # # # # # # # # # + + /** + * Get a BRecipe by its name. + *

The name is the middle one of the three if three are set in the config + *

Returns null if recipe with that name does not exist + */ + @Nullable + public static BRecipe getRecipe(String name) { + return BRecipe.get(name); + } + + /** + * Add a New Recipe. + *

Brews can be made out of this Recipe. + *

The recipe can be changed or removed later. + * + * @param recipe The Recipe to add + * @param saveForever Not Implemented yet. + *
If the recipe should be saved forever, even after the Server restarts + *
If True: Recipe will be saved until removed manually + *
If False: Recipe will be removed when the Server restarts, existing potions using + *
this Recipe will become bad after continued aging, if the recipe is not added again. + */ + public static void addRecipe(BRecipe recipe, boolean saveForever) { + //recipe.setSaveInData(saveForever); + if (saveForever) { + throw new NotImplementedException("SaveForever is not implemented yet"); + } + BRecipe.getAddedRecipes().add(recipe); + recipe.updateAcceptedLists(); + } + + /** + * Removes a Recipe from the List of all Recipes. + *

This can also remove Recipes that were loaded from config, though these will be readded when reloading the config + * + * @param name The name of the recipe to remove + * @return The Recipe that was removed, null if none was removed + */ + @Nullable + public static BRecipe removeRecipe(String name) { + List recipes = BRecipe.getAllRecipes(); + for (int i = 0; i < recipes.size(); i++) { + if (recipes.get(i).getRecipeName().equalsIgnoreCase(name)) { + BRecipe remove = recipes.remove(i); + if (i < BRecipe.numConfigRecipes) { + // We removed one of the Config Recipes + BRecipe.numConfigRecipes--; + } + return remove; + } + } + return null; + } + + /** + * Create a New Recipe with a Recipe Builder. + * + * @param recipeNames Either 1 or 3 names. Sets the Name for Quality (Bad, Normal, Good) + * @return A Recipe Builder + */ + public static BRecipe.Builder recipeBuilder(String... recipeNames) { + return new BRecipe.Builder(recipeNames); + } + + + + // # # # # # # # # # # # # + // # # # # # Cauldron Recipe # # # # # + // # # # # # # # # # # # # + + /** + * Get A BCauldronRecipe by its name. + *

Returns null if recipe with that name does not exist + */ + @Nullable + public static BCauldronRecipe getCauldronRecipe(String name) { + return BCauldronRecipe.get(name); + } + + /** + * Add a New Cauldron Recipe. + *

Base Brews coming out of the Cauldron can be made from this recipe + *

The recipe can be changed or removed later. + * + * @param recipe The Cauldron Recipe to add + * @param saveForever Not Implemented yet. + *
If the recipe should be saved forever, even after the Server restarts + *
If True: Recipe will be saved until removed manually + *
If False: Recipe will be removed when the Server restarts + */ + public static void addCauldronRecipe(BCauldronRecipe recipe, boolean saveForever) { + //recipe.setSaveInData(saveForever); + if (saveForever) { + throw new NotImplementedException(); + } + BCauldronRecipe.getAddedRecipes().add(recipe); + recipe.updateAcceptedLists(); + } + + /** + * Removes a Cauldron Recipe from the List of all Cauldron Recipes. + *

This can also remove Cauldron Recipes that were loaded from config, + * though these will be readded when reloading the config + * + * @param name The name of the cauldron recipe to remove + * @return The Cauldron Recipe that was removed, null if none was removed + */ + @Nullable + public static BCauldronRecipe removeCauldronRecipe(String name) { + List recipes = BCauldronRecipe.getAllRecipes(); + for (int i = 0; i < recipes.size(); i++) { + if (recipes.get(i).getName().equalsIgnoreCase(name)) { + BCauldronRecipe remove = recipes.remove(i); + if (i < BCauldronRecipe.numConfigRecipes) { + // We removed one of the Config Recipes + BCauldronRecipe.numConfigRecipes--; + } + return remove; + } + } + return null; + } + + /** + * Create a New Cauldron Recipe with a Recipe Builder. + * + * @param name The name of the new Cauldron Recipe + * @return A Cauldron Recipe Builder + */ + public static BCauldronRecipe.Builder cauldronRecipeBuilder(String name) { + return new BCauldronRecipe.Builder(name); + } + + +} diff --git a/src/com/dre/brewery/api/events/ConfigLoadEvent.java b/src/com/dre/brewery/api/events/ConfigLoadEvent.java new file mode 100644 index 0000000..e4ea2af --- /dev/null +++ b/src/com/dre/brewery/api/events/ConfigLoadEvent.java @@ -0,0 +1,49 @@ +package com.dre.brewery.api.events; + +import com.dre.brewery.api.BreweryApi; +import com.dre.brewery.recipe.BCauldronRecipe; +import com.dre.brewery.recipe.BRecipe; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +/** + * The Brewery Config was reloaded. + *

The Recipes added by a Plugin to the Added Recipes will not be reloaded and stay where they are. + */ +public class ConfigLoadEvent extends Event { + private static final HandlerList handlers = new HandlerList(); + + /** + * Removes a Recipe, can also remove config recipes. + * One of the things one might need to do after reloading. + * + * @param name Name of the Recipe to remove + * @return The Recipe that was removed, null if none was removed + */ + public BRecipe removeRecipe(String name) { + return BreweryApi.removeRecipe(name); + } + + /** + * Removes a Cauldron Recipe, can also remove config recipes. + * One of the things one might need to do after reloading. + * + * @param name Name of the Cauldron Recipe to remove + * @return The Cauldron Recipe that was removed, null if none was removed + */ + public BCauldronRecipe removeCauldronRecipe(String name) { + return BreweryApi.removeCauldronRecipe(name); + } + + @NotNull + @Override + public HandlerList getHandlers() { + return handlers; + } + + // Required by Bukkit + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/src/com/dre/brewery/api/events/IngedientAddEvent.java b/src/com/dre/brewery/api/events/IngedientAddEvent.java new file mode 100644 index 0000000..37461f3 --- /dev/null +++ b/src/com/dre/brewery/api/events/IngedientAddEvent.java @@ -0,0 +1,147 @@ +package com.dre.brewery.api.events; + +import com.dre.brewery.BCauldron; +import com.dre.brewery.recipe.RecipeItem; +import com.dre.brewery.utility.LegacyUtil; +import org.bukkit.block.Block; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.Levelled; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.player.PlayerEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Player adding an ingredient to a cauldron. + *

Always one item added at a time. + *

If needed use the caudrons add method to manually add more Items + */ +public class IngedientAddEvent extends PlayerEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final Block block; + private final BCauldron cauldron; + private ItemStack ingredient; + private RecipeItem rItem; + private boolean cancelled; + private boolean takeItem = true; + + public IngedientAddEvent(Player who, Block block, BCauldron bCauldron, ItemStack ingredient, RecipeItem rItem) { + super(who); + this.block = block; + cauldron = bCauldron; + this.rItem = rItem; + this.ingredient = ingredient; + } + + public Block getBlock() { + return block; + } + + public BCauldron getCauldron() { + return cauldron; + } + + /** + * The Recipe item that matches the ingredient. + *

This might not be the only recipe item that will match the ingredient + *

Will be recalculated if the Ingredient is changed with the setIngredient Method + */ + public RecipeItem getRecipeItem() { + return rItem; + } + + /** + * Get the item currently being added to the cauldron by the player. + *

Can be changed directly (mutable) or with the setter Method + *

The amount is ignored and always one added + * + * @return The item being added + */ + public ItemStack getIngredient() { + return ingredient; + } + + /** + * Set the ingredient added to the cauldron to something else. + *

Will always be accepted, even when not in a recipe or the cooked lis + *

The amount is ignored and always one added + *

This also recalculates the recipeItem! + * + * @param ingredient The item to add instead + */ + public void setIngredient(ItemStack ingredient) { + this.ingredient = ingredient; + // The Ingredient has been changed. Recalculate RecipeItem! + rItem = RecipeItem.getMatchingRecipeItem(ingredient, true); + } + + /** + * If the amount of the item in the players hand should be decreased. + * (Default true) + */ + public boolean willTakeItem() { + return takeItem; + } + + /** + * Set if the amount of the item in the players hand should be decreased. + * + * @param takeItem if the item amount in the hand should be decreased + */ + public void setTakeItem(boolean takeItem) { + this.takeItem = takeItem; + } + + /** + * Get the BlockData of the Cauldron. + *

May be null if the Cauldron does not exist anymore + * + * @return The BlockData of the cauldron + */ + @Nullable + public Levelled getCauldronData() { + BlockData data = block.getBlockData(); + if (data instanceof Levelled) { + return (Levelled) data; + } + return null; + } + + /** + * Get the water fill level of the Cauldron. + *

0 = empty, 1 = something in, 2 = full + *

Can use BCauldron.EMPTY, BCauldron.SOME, BCauldron.FULL + * + * @return The fill level as a byte 0-2 + */ + public byte getFillLevel() { + return LegacyUtil.getFillLevel(block); + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + /** + * If the event is cancelled, no item will be added or taken from the player. + */ + @Override + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } + + @NotNull + @Override + public HandlerList getHandlers() { + return handlers; + } + + // Required by Bukkit + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/src/com/dre/brewery/api/events/PlayerChatDistortEvent.java b/src/com/dre/brewery/api/events/PlayerChatDistortEvent.java new file mode 100644 index 0000000..15e7dfc --- /dev/null +++ b/src/com/dre/brewery/api/events/PlayerChatDistortEvent.java @@ -0,0 +1,95 @@ +package com.dre.brewery.api.events; + +import com.dre.brewery.BPlayer; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.bukkit.event.player.PlayerEvent; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * The Player writes something in Chat or on a Sign and his words are distorted. + * + *

This Event may be Async if the Chat Event is Async! + */ +public class PlayerChatDistortEvent extends Event implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + + private final Player player; + private final BPlayer bPlayer; + private final String prevMsg; + private String distortMsg; + private boolean cancelled; + + public PlayerChatDistortEvent(boolean async, Player player, BPlayer bPlayer, String prevMsg, String distortMsg) { + super(async); + this.player = player; + this.bPlayer = bPlayer; + this.prevMsg = prevMsg; + this.distortMsg = distortMsg; + } + + @NotNull + public Player getPlayer() { + return player; + } + + @NotNull + public BPlayer getbPlayer() { + return bPlayer; + } + + /** + * @return The Message the Player had actually written + */ + @NotNull + public String getWrittenMessage() { + return prevMsg; + } + + /** + * @return The message after it was distorted + */ + @NotNull + public String getDistortedMessage() { + return distortMsg; + } + + /** + * @return The drunkeness of the player that is writing the message + */ + public int getDrunkeness() { + return bPlayer.getDrunkeness(); + } + + /** + * Set the Message that the player will say instead of what he wrote + */ + public void setDistortedMessage(String distortMsg) { + this.distortMsg = Objects.requireNonNull(distortMsg); + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } + + @NotNull + @Override + public HandlerList getHandlers() { + return handlers; + } + + // Required by Bukkit + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/src/com/dre/brewery/api/events/PlayerEffectEvent.java b/src/com/dre/brewery/api/events/PlayerEffectEvent.java new file mode 100644 index 0000000..3d1b432 --- /dev/null +++ b/src/com/dre/brewery/api/events/PlayerEffectEvent.java @@ -0,0 +1,98 @@ +package com.dre.brewery.api.events; + +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.player.PlayerEvent; +import org.bukkit.potion.PotionEffect; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * A List of effects is applied to the player. + *

This happens for various reasons like Alcohol level, Brew quality, Brew effects, etc. + * + *

Can be changed or cancelled + */ +public class PlayerEffectEvent extends PlayerEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final EffectType effectType; + private List effects; + private boolean cancelled; + + public PlayerEffectEvent(Player who, EffectType effectType, List effects) { + super(who); + this.effectType = effectType; + this.effects = effects; + } + + /** + * @return The effects being applied. Effects can be added or removed from this list. + */ + public List getEffects() { + return effects; + } + + public void setEffects(List effects) { + this.effects = effects; + } + + /** + * @return What type of effects are applied, see EffectType + */ + public EffectType getEffectType() { + return effectType; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } + + @NotNull + @Override + public HandlerList getHandlers() { + return handlers; + } + + // Required by Bukkit + public static HandlerList getHandlerList() { + return handlers; + } + + + /** + * The Type of Effect, or why an effect is being added to the player. + */ + public enum EffectType { + /** + * The Alcohol level demands its toll. + *

Regularly applied depending on the players alcohol level + *

By default it is just one Confusion effect + */ + ALCOHOL, + + /** + * Effects of a Brew are applied to the player (drinking the Brew). + *

These depend on alcohol and quality of the brew + */ + DRINK, + + /** + * When drinking a Brew with low Quality, these effects are applied. + */ + QUALITY, + + /** + * When logging in after drinking, Hangover Effects are applied. + */ + HANGOVER + + } +} diff --git a/src/com/dre/brewery/api/events/PlayerPukeEvent.java b/src/com/dre/brewery/api/events/PlayerPukeEvent.java new file mode 100644 index 0000000..05f3bbc --- /dev/null +++ b/src/com/dre/brewery/api/events/PlayerPukeEvent.java @@ -0,0 +1,69 @@ +package com.dre.brewery.api.events; + +import com.dre.brewery.BPlayer; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.player.PlayerEvent; +import org.jetbrains.annotations.NotNull; + +/** + * The player pukes (throws puke items to the ground). + *

Those items can never be picked up and despawn after the time set in the config + *

Number of items to drop can be changed with count + */ +public class PlayerPukeEvent extends PlayerEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private int count; + private boolean cancelled; + private BPlayer bPlayer; + + + public PlayerPukeEvent(Player who, int count) { + super(who); + this.count = count; + } + + /** + * Get the Amount of items being dropped this time + */ + public int getCount() { + return count; + } + + /** + * Set the amount of items being dropped this time + */ + public void setCount(int count) { + this.count = count; + } + + public BPlayer getBPlayer() { + if (bPlayer == null) { + bPlayer = BPlayer.get(player); + } + return bPlayer; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } + + @NotNull + @Override + public HandlerList getHandlers() { + return handlers; + } + + // Required by Bukkit + public static HandlerList getHandlerList() { + return handlers; + } + +} diff --git a/src/com/dre/brewery/api/events/PlayerPushEvent.java b/src/com/dre/brewery/api/events/PlayerPushEvent.java new file mode 100644 index 0000000..03290fe --- /dev/null +++ b/src/com/dre/brewery/api/events/PlayerPushEvent.java @@ -0,0 +1,75 @@ +package com.dre.brewery.api.events; + +import com.dre.brewery.BPlayer; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.player.PlayerEvent; +import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; + +/** + * The Players movement is hindered because of drunkeness. + *

Called each time before pushing the Player with the Vector push 10 times + *

The Push Vector can be changed or multiplied + */ +public class PlayerPushEvent extends PlayerEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final BPlayer bPlayer; + private Vector push; + private boolean cancelled; + + public PlayerPushEvent(Player who, Vector push, BPlayer bPlayer) { + super(who); + this.push = push; + this.bPlayer = bPlayer; + } + + public BPlayer getBPlayer() { + return bPlayer; + } + + /** + * Get the Vector in which direction and magnitude the player is pushed. + *

Can be changed directly or through setPush + * + * @return The current push vector + */ + public Vector getPush() { + return push; + } + + /** + * Set the Push vector. + * + * @param push The new push vector, not null + */ + public void setPush(@NotNull Vector push) { + if (push == null) { + throw new NullPointerException("Push Vector is null"); + } + this.push = push; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } + + @NotNull + @Override + public HandlerList getHandlers() { + return handlers; + } + + // Required by Bukkit + public static HandlerList getHandlerList() { + return handlers; + } + +} diff --git a/src/com/dre/brewery/api/events/barrel/BarrelAccessEvent.java b/src/com/dre/brewery/api/events/barrel/BarrelAccessEvent.java new file mode 100644 index 0000000..b3c853d --- /dev/null +++ b/src/com/dre/brewery/api/events/barrel/BarrelAccessEvent.java @@ -0,0 +1,59 @@ +package com.dre.brewery.api.events.barrel; + +import com.dre.brewery.Barrel; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +/** + * A Player opens a Barrel by rightclicking it. + *

The PlayerInteractEvent on the Barrel may be cancelled. In that case this never gets called + *

Can be cancelled to silently deny opening the Barrel + */ +public class BarrelAccessEvent extends BarrelEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final Player player; + private final Block clickedBlock; + private boolean isCancelled; + + public BarrelAccessEvent(Barrel barrel, Player player, Block clickedBlock) { + super(barrel); + this.player = player; + this.clickedBlock = clickedBlock; + } + + /** + * Gets the Block that was actually clicked. + *

For access Permissions getSpigot() should be used + */ + public Block getClickedBlock() { + return clickedBlock; + } + + @Override + public boolean isCancelled() { + return isCancelled; + } + + @Override + public void setCancelled(boolean cancelled) { + isCancelled = cancelled; + } + + public Player getPlayer() { + return player; + } + + @NotNull + @Override + public HandlerList getHandlers() { + return handlers; + } + + // Required by Bukkit + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/src/com/dre/brewery/api/events/barrel/BarrelCreateEvent.java b/src/com/dre/brewery/api/events/barrel/BarrelCreateEvent.java new file mode 100644 index 0000000..ef9129e --- /dev/null +++ b/src/com/dre/brewery/api/events/barrel/BarrelCreateEvent.java @@ -0,0 +1,47 @@ +package com.dre.brewery.api.events.barrel; + +import com.dre.brewery.Barrel; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +/** + * Called when a Barrel is created by a Player by placing a Sign. + *

Cancelling this will silently fail the Barrel creation + */ +public class BarrelCreateEvent extends BarrelEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final Player player; + private boolean cancelled; + + public BarrelCreateEvent(Barrel barrel, Player player) { + super(barrel); + this.player = player; + } + + public Player getPlayer() { + return player; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } + + @NotNull + @Override + public HandlerList getHandlers() { + return handlers; + } + + // Required by Bukkit + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/src/com/dre/brewery/api/events/barrel/BarrelDestroyEvent.java b/src/com/dre/brewery/api/events/barrel/BarrelDestroyEvent.java new file mode 100644 index 0000000..45ee763 --- /dev/null +++ b/src/com/dre/brewery/api/events/barrel/BarrelDestroyEvent.java @@ -0,0 +1,114 @@ +package com.dre.brewery.api.events.barrel; + +import com.dre.brewery.Barrel; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A Barrel is being destroyed by something, may not be by a Player. + *

A BarrelRemoveEvent will be called after this, if this is not cancelled + *

Use the BarrelRemoveEvent to monitor any and all barrels being removed in a non cancellable way + *

Cancelling the Event will stop the barrel from being destroyed +*/ +public class BarrelDestroyEvent extends BarrelEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final Block broken; + private final Reason reason; + private final Player player; + private boolean cancelled; + + public BarrelDestroyEvent(Barrel barrel, Block broken, Reason reason, Player player) { + super(barrel); + this.broken = broken; + this.player = player; + this.reason = reason; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + /** + * Cancelling the Event will stop the barrel from being destroyed. + * Any Blocks that are part of the barrel will not be destroyed + */ + @Override + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } + + /** + * @return The Block of the Barrel that was broken + */ + public Block getBroken() { + return broken; + } + + /** + * @return The Reason of destruction of this barrel, see Reason + */ + public Reason getReason() { + return reason; + } + + /** + * If a Player was recorded destroying the barrel + */ + public boolean hasPlayer() { + return player != null; + } + + /** + * @return The Player, Null if no Player is involved + */ + @Nullable + public Player getPlayerOptional() { + return player; + } + + @NotNull + @Override + public HandlerList getHandlers() { + return handlers; + } + + // Required by Bukkit + public static HandlerList getHandlerList() { + return handlers; + } + + /** + * The Reason why the Barrel is being destroyed. + */ + public enum Reason { + /** + * A Player Broke the Barrel + */ + PLAYER, + + /** + * A Block was broken by something + */ + BROKEN, + + /** + * A Block burned away + */ + BURNED, + + /** + * The Barrel exploded somehow + */ + EXPLODED, + + /** + * The Barrel was broken somehow else + */ + UNKNOWN + } +} diff --git a/src/com/dre/brewery/api/events/barrel/BarrelEvent.java b/src/com/dre/brewery/api/events/barrel/BarrelEvent.java new file mode 100644 index 0000000..871b085 --- /dev/null +++ b/src/com/dre/brewery/api/events/barrel/BarrelEvent.java @@ -0,0 +1,29 @@ +package com.dre.brewery.api.events.barrel; + +import com.dre.brewery.Barrel; +import org.bukkit.block.Block; +import org.bukkit.event.Event; +import org.bukkit.inventory.Inventory; + +public abstract class BarrelEvent extends Event { + protected final Barrel barrel; + + public BarrelEvent(Barrel barrel) { + this.barrel = barrel; + } + + public Barrel getBarrel() { + return barrel; + } + + public Inventory getInventory() { + return barrel.getInventory(); + } + + /** + * @return The Spigot Block of the Barrel, usually Sign or a Fence + */ + public Block getSpigot() { + return barrel.getSpigot(); + } +} diff --git a/src/com/dre/brewery/api/events/barrel/BarrelRemoveEvent.java b/src/com/dre/brewery/api/events/barrel/BarrelRemoveEvent.java new file mode 100644 index 0000000..827d6de --- /dev/null +++ b/src/com/dre/brewery/api/events/barrel/BarrelRemoveEvent.java @@ -0,0 +1,42 @@ +package com.dre.brewery.api.events.barrel; + +import com.dre.brewery.Barrel; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +/** + * A Barrel is being removed. + *

There may have been a BarrelDestroyEvent before this. + * If not, Worldedit, other Plugins etc may be the cause for unexpected removal + */ +public class BarrelRemoveEvent extends BarrelEvent { + private static final HandlerList handlers = new HandlerList(); + private boolean dropItems; + + public BarrelRemoveEvent(Barrel barrel, boolean dropItems) { + super(barrel); + this.dropItems = dropItems; + } + + public boolean willDropItems() { + return dropItems; + } + + /** + * @param dropItems Should the Items contained in this Barrel drop to the ground? + */ + public void setShouldDropItems(boolean dropItems) { + this.dropItems = dropItems; + } + + @NotNull + @Override + public HandlerList getHandlers() { + return handlers; + } + + // Required by Bukkit + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/src/com/dre/brewery/api/events/brew/BrewDrinkEvent.java b/src/com/dre/brewery/api/events/brew/BrewDrinkEvent.java new file mode 100644 index 0000000..49f3378 --- /dev/null +++ b/src/com/dre/brewery/api/events/brew/BrewDrinkEvent.java @@ -0,0 +1,79 @@ +package com.dre.brewery.api.events.brew; + +import com.dre.brewery.BPlayer; +import com.dre.brewery.Brew; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; + +/** + * A Player Drinks a Brew. + *

The amount of alcohol and quality that will be added to the player can be get/set here + *

If cancelled the drinking will fail silently + */ +public class BrewDrinkEvent extends BrewEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final Player player; + private final BPlayer bPlayer; + private int alc; + private int quality; + private boolean cancelled; + + public BrewDrinkEvent(Brew brew, ItemMeta meta, Player player, BPlayer bPlayer) { + super(brew, meta); + this.player = player; + this.bPlayer = bPlayer; + alc = brew.getOrCalcAlc(); + quality = brew.getQuality(); + } + + public Player getPlayer() { + return player; + } + + public BPlayer getbPlayer() { + return bPlayer; + } + + public int getAddedAlcohol() { + return alc; + } + + public void setAddedAlcohol(int alc) { + this.alc = alc; + } + + public int getQuality() { + return quality; + } + + public void setQuality(int quality) { + if (quality > 10 || quality < 0) { + throw new IllegalArgumentException("Quality must be in range from 0 to 10"); + } + this.quality = quality; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } + + @NotNull + @Override + public HandlerList getHandlers() { + return handlers; + } + + // Required by Bukkit + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/src/com/dre/brewery/api/events/brew/BrewEvent.java b/src/com/dre/brewery/api/events/brew/BrewEvent.java new file mode 100644 index 0000000..aa76e7e --- /dev/null +++ b/src/com/dre/brewery/api/events/brew/BrewEvent.java @@ -0,0 +1,29 @@ +package com.dre.brewery.api.events.brew; + +import com.dre.brewery.Brew; +import org.bukkit.event.Event; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; + +public abstract class BrewEvent extends Event { + protected final Brew brew; + protected final ItemMeta meta; + + public BrewEvent(@NotNull Brew brew, @NotNull ItemMeta meta) { + this.brew = brew; + this.meta = meta; + } + + @NotNull + public Brew getBrew() { + return brew; + } + + /** + * Gets the Meta of the Item this Brew is attached to + */ + @NotNull + public ItemMeta getItemMeta() { + return meta; + } +} diff --git a/src/com/dre/brewery/api/events/brew/BrewModifyEvent.java b/src/com/dre/brewery/api/events/brew/BrewModifyEvent.java new file mode 100644 index 0000000..74bed88 --- /dev/null +++ b/src/com/dre/brewery/api/events/brew/BrewModifyEvent.java @@ -0,0 +1,109 @@ +package com.dre.brewery.api.events.brew; + +import com.dre.brewery.Brew; +import com.dre.brewery.lore.BrewLore; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.PotionMeta; +import org.jetbrains.annotations.NotNull; + +/** + * A Brew has been created or modified. + *

Usually happens on filling from cauldron, distilling and aging. + *

Modifications to the Brew or the PotionMeta can be done now + *

Cancelling reverts the Brew to the state it was before the modification + */ +public class BrewModifyEvent extends BrewEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final Type type; + private boolean cancelled; + + + public BrewModifyEvent(@NotNull Brew brew, @NotNull ItemMeta meta, @NotNull Type type) { + super(brew, meta); + this.type = type; + } + + /** + * Get the Type of modification being applied to the Brew. + */ + @NotNull + public Type getType() { + return type; + } + + /** + * Get the BrewLore to modify lore on the Brew + */ + @NotNull + public BrewLore getLore() { + return new BrewLore(getBrew(), (PotionMeta) getItemMeta()); + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + /** + * Setting the Event cancelled cancels all modificatons to the brew. + *

Modifications to the Brew or ItemMeta will not be applied + */ + @Override + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } + + @NotNull + @Override + public HandlerList getHandlers() { + return handlers; + } + + // Required by Bukkit + public static HandlerList getHandlerList() { + return handlers; + } + + /** + * The Type of Modification being applied to the Brew. + */ + public enum Type { + /** + * A new Brew is created with arbitrary ways, like the create command. + *

Cancelling this will disallow the creation + */ + CREATE, + + /** + * Filled from a Cauldron into a new Brew. + */ + FILL, + + /** + * Distilled in the Brewing stand. + */ + DISTILL, + + /** + * Aged in a Barrel. + */ + AGE, + + /** + * Unlabeling Brew with command. + */ + UNLABEL, + + /** + * Making Brew static with command. + */ + STATIC, + + /** + * Unknown modification, unused. + */ + UNKNOWN + } +} diff --git a/src/com/dre/brewery/filedata/BConfig.java b/src/com/dre/brewery/filedata/BConfig.java new file mode 100644 index 0000000..5588440 --- /dev/null +++ b/src/com/dre/brewery/filedata/BConfig.java @@ -0,0 +1,331 @@ +package com.dre.brewery.filedata; + +import com.dre.brewery.Brew; +import com.dre.brewery.DistortChat; +import com.dre.brewery.MCBarrel; +import com.dre.brewery.P; +import com.dre.brewery.api.events.ConfigLoadEvent; +import com.dre.brewery.integration.barrel.WGBarrel; +import com.dre.brewery.integration.barrel.WGBarrel5; +import com.dre.brewery.integration.barrel.WGBarrel6; +import com.dre.brewery.integration.barrel.WGBarrel7; +import com.dre.brewery.integration.item.BreweryPluginItem; +import com.dre.brewery.integration.item.MMOItemsPluginItem; +import com.dre.brewery.integration.item.SlimefunPluginItem; +import com.dre.brewery.recipe.BCauldronRecipe; +import com.dre.brewery.recipe.BRecipe; +import com.dre.brewery.recipe.PluginItem; +import com.dre.brewery.recipe.RecipeItem; +import com.dre.brewery.utility.BUtil; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginManager; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class BConfig { + + public static final String configVersion = "2.0"; + public static boolean updateCheck; + public static CommandSender reloader; + + // Third Party Enabled + public static boolean useWG; //WorldGuard + public static WGBarrel wg; + public static boolean useLWC; //LWC + public static boolean useLB; //LogBlock + public static boolean useGP; //GriefPrevention + public static boolean hasVault; // Vault + public static boolean useCitadel; // CivCraft/DevotedMC Citadel + public static boolean useGMInventories; // GamemodeInventories + public static Boolean hasSlimefun = null; // Slimefun ; Null if not checked + public static Boolean hasMMOItems = null; // MMOItems ; Null if not checked + + // Barrel + public static boolean openEverywhere; + + //BPlayer + public static Map drainItems = new HashMap<>();// DrainItem Material and Strength + public static Material pukeItem; + public static int pukeDespawntime; + public static int hangoverTime; + public static boolean overdrinkKick; + public static boolean enableHome; + public static boolean enableLoginDisallow; + public static boolean enablePuke; + public static String homeType; + + //Brew + public static boolean colorInBarrels; // color the Lore while in Barrels + public static boolean colorInBrewer; // color the Lore while in Brewer + public static boolean enableEncode; + public static boolean alwaysShowQuality; // Always show quality stars + public static boolean alwaysShowAlc; // Always show alc% + + //Item + public static List customItems = new ArrayList<>(); + + public static P p = P.p; + + private static boolean checkConfigs() { + File cfg = new File(p.getDataFolder(), "config.yml"); + if (!cfg.exists()) { + p.log("§1§lNo config.yml found, creating default file! You may want to choose a config according to your language!"); + p.log("§1§lYou can find them in plugins/Brewery/configs/"); + p.log("§1§lJust copy the config for your language into the Brewery folder and /brew reload"); + InputStream defconf = p.getResource("config/" + (P.use1_13 ? "v13/" : "v12/") + "en/config.yml"); + if (defconf == null) { + p.errorLog("default config file not found, your jarfile may be corrupt. Disabling Brewery!"); + return false; + } + try { + BUtil.saveFile(defconf, p.getDataFolder(), "config.yml", false); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + } + if (!cfg.exists()) { + p.errorLog("default config file could not be copied, your jarfile may be corrupt. Disabling Brewery!"); + return false; + } + + copyDefaultConfigs(false); + return true; + } + + private static void copyDefaultConfigs(boolean overwrite) { + File configs = new File(p.getDataFolder(), "configs"); + File languages = new File(p.getDataFolder(), "languages"); + for (String l : new String[] {"de", "en", "fr", "it", "zh", "tw"}) { + File lfold = new File(configs, l); + try { + BUtil.saveFile(p.getResource("config/" + (P.use1_13 ? "v13/" : "v12/") + l + "/config.yml"), lfold, "config.yml", overwrite); + BUtil.saveFile(p.getResource("languages/" + l + ".yml"), languages, l + ".yml", false); // Never overwrite languages, they get updated with their updater + } catch (IOException e) { + if (!(l.equals("zh") || l.equals("tw"))) { + // zh and tw not available for some versions + e.printStackTrace(); + } + } + } + } + + public static FileConfiguration loadConfigFile() { + File file = new File(P.p.getDataFolder(), "config.yml"); + if (!checkConfigs()) { + return null; + } + + try { + YamlConfiguration cfg = YamlConfiguration.loadConfiguration(file); + if (cfg.contains("version") && cfg.contains("language")) { + return cfg; + } + } catch (Exception e) { + e.printStackTrace(); + } + + // Failed to load + if (p.languageReader != null) { + P.p.errorLog(p.languageReader.get("Error_YmlRead")); + } else { + P.p.errorLog("Could not read file config.yml, please make sure the file is in valid yml format (correct spaces etc.)"); + } + return null; + } + + public static void readConfig(FileConfiguration config) { + // Set the Language + p.language = config.getString("language", "en"); + + // Load LanguageReader + p.languageReader = new LanguageReader(new File(p.getDataFolder(), "languages/" + p.language + ".yml"), "languages/" + p.language + ".yml"); + + // Has to config still got old materials + boolean oldMat = config.getBoolean("oldMat", false); + + // Check if config is the newest version + String version = config.getString("version", null); + if (version != null) { + if (!version.equals(configVersion) || (oldMat && P.use1_13)) { + File file = new File(P.p.getDataFolder(), "config.yml"); + copyDefaultConfigs(true); + new ConfigUpdater(file).update(version, oldMat, p.language, config); + P.p.log("Config Updated to version: " + configVersion); + config = YamlConfiguration.loadConfiguration(file); + } + } + + // If the Update Checker should be enabled + updateCheck = config.getBoolean("updateCheck", false); + + PluginManager plMan = p.getServer().getPluginManager(); + + // Third-Party + useWG = config.getBoolean("useWorldGuard", true) && plMan.isPluginEnabled("WorldGuard"); + + if (useWG) { + Plugin plugin = Bukkit.getPluginManager().getPlugin("WorldEdit"); + if (plugin != null) { + String wgv = plugin.getDescription().getVersion(); + if (wgv.startsWith("6.")) { + wg = new WGBarrel6(); + } else if (wgv.startsWith("5.")) { + wg = new WGBarrel5(); + } else { + wg = new WGBarrel7(); + } + } + if (wg == null) { + P.p.errorLog("Failed loading WorldGuard Integration! Opening Barrels will NOT work!"); + P.p.errorLog("Brewery was tested with version 5.8, 6.1 and 7.0 of WorldGuard!"); + P.p.errorLog("Disable the WorldGuard support in the config and do /brew reload"); + } + } + useLWC = config.getBoolean("useLWC", true) && plMan.isPluginEnabled("LWC"); + useGP = config.getBoolean("useGriefPrevention", true) && plMan.isPluginEnabled("GriefPrevention"); + useLB = config.getBoolean("useLogBlock", false) && plMan.isPluginEnabled("LogBlock"); + useGMInventories = config.getBoolean("useGMInventories", false); + useCitadel = config.getBoolean("useCitadel", false) && plMan.isPluginEnabled("Citadel"); + // The item util has been removed in Vault 1.7+ + hasVault = plMan.isPluginEnabled("Vault") + && Integer.parseInt(plMan.getPlugin("Vault").getDescription().getVersion().split("\\.")[1]) <= 6; + + // various Settings + DataSave.autosave = config.getInt("autosave", 3); + P.debug = config.getBoolean("debug", false); + pukeItem = Material.matchMaterial(config.getString("pukeItem", "SOUL_SAND")); + hangoverTime = config.getInt("hangoverDays", 0) * 24 * 60; + overdrinkKick = config.getBoolean("enableKickOnOverdrink", false); + enableHome = config.getBoolean("enableHome", false); + enableLoginDisallow = config.getBoolean("enableLoginDisallow", false); + enablePuke = config.getBoolean("enablePuke", false); + pukeDespawntime = config.getInt("pukeDespawntime", 60) * 20; + homeType = config.getString("homeType", null); + colorInBarrels = config.getBoolean("colorInBarrels", false); + colorInBrewer = config.getBoolean("colorInBrewer", false); + alwaysShowQuality = config.getBoolean("alwaysShowQuality", false); + alwaysShowAlc = config.getBoolean("alwaysShowAlc", false); + enableEncode = config.getBoolean("enableEncode", false); + openEverywhere = config.getBoolean("openLargeBarrelEverywhere", false); + MCBarrel.maxBrews = config.getInt("maxBrewsInMCBarrels", 6); + + Brew.loadSeed(config, new File(P.p.getDataFolder(), "config.yml")); + + PluginItem.registerForConfig("brewery", BreweryPluginItem::new); + PluginItem.registerForConfig("mmoitems", MMOItemsPluginItem::new); + PluginItem.registerForConfig("slimefun", SlimefunPluginItem::new); + PluginItem.registerForConfig("exoticgarden", SlimefunPluginItem::new); + + // Loading custom items + ConfigurationSection configSection = config.getConfigurationSection("customItems"); + if (configSection != null) { + for (String custId : configSection.getKeys(false)) { + RecipeItem custom = RecipeItem.fromConfigCustom(configSection, custId); + if (custom != null) { + custom.makeImmutable(); + customItems.add(custom); + } else { + p.errorLog("Loading the Custom Item with id: '" + custId + "' failed!"); + } + } + } + + // loading recipes + configSection = config.getConfigurationSection("recipes"); + if (configSection != null) { + List configRecipes = BRecipe.getConfigRecipes(); + for (String recipeId : configSection.getKeys(false)) { + BRecipe recipe = BRecipe.fromConfig(configSection, recipeId); + if (recipe != null && recipe.isValid()) { + configRecipes.add(recipe); + } else { + p.errorLog("Loading the Recipe with id: '" + recipeId + "' failed!"); + } + } + BRecipe.numConfigRecipes = configRecipes.size(); + } + + // Loading Cauldron Recipes + configSection = config.getConfigurationSection("cauldron"); + if (configSection != null) { + List configRecipes = BCauldronRecipe.getConfigRecipes(); + for (String id : configSection.getKeys(false)) { + BCauldronRecipe recipe = BCauldronRecipe.fromConfig(configSection, id); + if (recipe != null) { + configRecipes.add(recipe); + } else { + p.errorLog("Loading the Cauldron-Recipe with id: '" + id + "' failed!"); + } + } + BCauldronRecipe.numConfigRecipes = configRecipes.size(); + } + + // Recalculating Cauldron-Accepted Items for non-config recipes + for (BRecipe recipe : BRecipe.getAddedRecipes()) { + recipe.updateAcceptedLists(); + } + for (BCauldronRecipe recipe : BCauldronRecipe.getAddedRecipes()) { + recipe.updateAcceptedLists(); + } + + // loading drainItems + List drainList = config.getStringList("drainItems"); + if (drainList != null) { + for (String drainString : drainList) { + String[] drainSplit = drainString.split("/"); + if (drainSplit.length > 1) { + Material mat = Material.matchMaterial(drainSplit[0]); + int strength = p.parseInt(drainSplit[1]); + if (mat == null && hasVault && strength > 0) { + try { + net.milkbowl.vault.item.ItemInfo vaultItem = net.milkbowl.vault.item.Items.itemByString(drainSplit[0]); + if (vaultItem != null) { + mat = vaultItem.getType(); + } + } catch (Exception e) { + P.p.errorLog("Could not check vault for Item Name"); + e.printStackTrace(); + } + } + if (mat != null && strength > 0) { + drainItems.put(mat, strength); + } + } + } + } + + // Loading Words + DistortChat.words = new ArrayList<>(); + DistortChat.ignoreText = new ArrayList<>(); + if (config.getBoolean("enableChatDistortion", false)) { + for (Map map : config.getMapList("words")) { + new DistortChat(map); + } + for (String bypass : config.getStringList("distortBypass")) { + DistortChat.ignoreText.add(bypass.split(",")); + } + DistortChat.commands = config.getStringList("distortCommands"); + } + DistortChat.log = config.getBoolean("logRealChat", false); + DistortChat.doSigns = config.getBoolean("distortSignText", false); + + // The Config was reloaded, call Event + ConfigLoadEvent event = new ConfigLoadEvent(); + P.p.getServer().getPluginManager().callEvent(event); + + + } +} diff --git a/src/com/dre/brewery/filedata/BData.java b/src/com/dre/brewery/filedata/BData.java new file mode 100644 index 0000000..37e4263 --- /dev/null +++ b/src/com/dre/brewery/filedata/BData.java @@ -0,0 +1,393 @@ +package com.dre.brewery.filedata; + +import com.dre.brewery.*; +import com.dre.brewery.lore.Base91DecoderStream; +import com.dre.brewery.recipe.CustomItem; +import com.dre.brewery.recipe.Ingredient; +import com.dre.brewery.recipe.PluginItem; +import com.dre.brewery.recipe.SimpleItem; +import com.dre.brewery.utility.BUtil; +import com.dre.brewery.utility.BoundingBox; +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.math.NumberUtils; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.File; +import java.io.IOException; +import java.util.*; + +public class BData { + + + // load all Data + public static void readData() { + File file = new File(P.p.getDataFolder(), "data.yml"); + if (file.exists()) { + + long t1 = System.currentTimeMillis(); + + FileConfiguration data = YamlConfiguration.loadConfiguration(file); + + long t2 = System.currentTimeMillis(); + + if (t2 - t1 > 8000) { + // Spigot is very slow at loading inventories from yml. Notify Admin that loading Data took long + P.p.log("Bukkit took " + (t2 - t1) / 1000.0 + "s to load the Data File,"); + P.p.log("consider switching to Paper, or have less items in Barrels"); + } else { + P.p.debugLog("Loading data.yml: " + (t2 - t1) + "ms"); + } + + Brew.installTime = data.getLong("installTime", System.currentTimeMillis()); + MCBarrel.mcBarrelTime = data.getLong("MCBarrelTime", 0); + + Brew.loadPrevSeeds(data); + + List brewsCreated = data.getIntegerList("brewsCreated"); + if (brewsCreated != null && brewsCreated.size() == 7) { + int hash = data.getInt("brewsCreatedH"); + // Check the hash to prevent tampering with statistics + if (brewsCreated.hashCode() == hash) { + P.p.brewsCreated = brewsCreated.get(0); + P.p.brewsCreatedCmd = brewsCreated.get(1); + P.p.exc = brewsCreated.get(2); + P.p.good = brewsCreated.get(3); + P.p.norm = brewsCreated.get(4); + P.p.bad = brewsCreated.get(5); + P.p.terr = brewsCreated.get(6); + } + } + + // Check if data is the newest version + String version = data.getString("Version", null); + if (version != null) { + if (!version.equals(DataSave.dataVersion)) { + P.p.log("Data File is being updated..."); + new DataUpdater(data, file).update(version); + data = YamlConfiguration.loadConfiguration(file); + P.p.log("Data Updated to version: " + DataSave.dataVersion); + } + } + + // Register Item Loaders + CustomItem.registerItemLoader(); + SimpleItem.registerItemLoader(); + PluginItem.registerItemLoader(); + + // loading Ingredients into ingMap + // Only for Legacy Brews + Map ingMap = new HashMap<>(); + ConfigurationSection section = data.getConfigurationSection("Ingredients"); + if (section != null) { + for (String id : section.getKeys(false)) { + if (section.isConfigurationSection(id + ".mats")) { + // Old way of saving + ConfigurationSection matSection = section.getConfigurationSection(id + ".mats"); + if (matSection != null) { + // matSection has all the materials + amount as Integers + List ingredients = oldDeserializeIngredients(matSection); + ingMap.put(id, new BIngredients(ingredients, section.getInt(id + ".cookedTime", 0), true)); + } else { + P.p.errorLog("Ingredient id: '" + id + "' incomplete in data.yml"); + } + } else { + // New way of saving ingredients + ingMap.put(id, deserializeIngredients(section.getString(id + ".mats"))); + } + } + } + + // loading Brew legacy + section = data.getConfigurationSection("Brew"); + if (section != null) { + // All sections have the UID as name + for (String uid : section.getKeys(false)) { + BIngredients ingredients = getIngredients(ingMap, section.getString(uid + ".ingId")); + int quality = section.getInt(uid + ".quality", 0); + int alc = section.getInt(uid + ".alc", 0); + byte distillRuns = (byte) section.getInt(uid + ".distillRuns", 0); + float ageTime = (float) section.getDouble(uid + ".ageTime", 0.0); + float wood = (float) section.getDouble(uid + ".wood", -1.0); + String recipe = section.getString(uid + ".recipe", null); + boolean unlabeled = section.getBoolean(uid + ".unlabeled", false); + boolean persistent = section.getBoolean(uid + ".persist", false); + boolean stat = section.getBoolean(uid + ".stat", false); + int lastUpdate = section.getInt(uid + ".lastUpdate", 0); + + Brew.loadLegacy(ingredients, P.p.parseInt(uid), quality, alc, distillRuns, ageTime, wood, recipe, unlabeled, persistent, stat, lastUpdate); + } + } + + // Store how many legacy brews were created + if (P.p.brewsCreated <= 0) { + P.p.brewsCreated = 0; + P.p.brewsCreatedCmd = 0; + P.p.exc = 0; + P.p.good = 0; + P.p.norm = 0; + P.p.bad = 0; + P.p.terr = 0; + if (!Brew.noLegacy()) { + for (Brew brew : Brew.legacyPotions.values()) { + P.p.metricsForCreate(false); + } + } + } + + // Remove Legacy Potions that haven't been touched in a long time, these may have been lost + if (!Brew.noLegacy()) { + int currentHoursAfterInstall = (int) ((double) (System.currentTimeMillis() - Brew.installTime) / 3600000D); + int purgeTime = currentHoursAfterInstall - (24 * 30 * 4); // Purge Time is 4 Months ago + if (purgeTime > 0) { + int removed = 0; + for (Iterator iterator = Brew.legacyPotions.values().iterator(); iterator.hasNext(); ) { + Brew brew = iterator.next(); + if (brew.getLastUpdate() < purgeTime) { + iterator.remove(); + removed++; + } + } + if (removed > 0) { + P.p.log("Removed " + removed + " Legacy Brews older than 3 months"); + } + } + } + + // loading BPlayer + section = data.getConfigurationSection("Player"); + if (section != null) { + // keys have players name + for (String name : section.getKeys(false)) { + try { + //noinspection ResultOfMethodCallIgnored + UUID.fromString(name); + if (!P.useUUID) { + continue; + } + } catch (IllegalArgumentException e) { + if (P.useUUID) { + continue; + } + } + + int quality = section.getInt(name + ".quality"); + int drunk = section.getInt(name + ".drunk"); + int offDrunk = section.getInt(name + ".offDrunk", 0); + + new BPlayer(name, quality, drunk, offDrunk); + } + } + + for (World world : P.p.getServer().getWorlds()) { + if (world.getName().startsWith("DXL_")) { + loadWorldData(BUtil.getDxlName(world.getName()), world, data); + } else { + loadWorldData(world.getUID().toString(), world, data); + } + } + + } else { + P.p.log("No data.yml found, will create new one!"); + } + } + + public static BIngredients deserializeIngredients(String mat) { + try (DataInputStream in = new DataInputStream(new Base91DecoderStream(new ByteArrayInputStream(mat.getBytes())))) { + byte ver = in.readByte(); + return BIngredients.load(in, ver); + } catch (IOException e) { + e.printStackTrace(); + return new BIngredients(); + } + } + + // Loading from the old way of saving ingredients + public static List oldDeserializeIngredients(ConfigurationSection matSection) { + List ingredients = new ArrayList<>(); + for (String mat : matSection.getKeys(false)) { + String[] matSplit = mat.split(","); + Material m = Material.getMaterial(matSplit[0]); + if (m == null && P.use1_13) { + if (matSplit[0].equals("LONG_GRASS")) { + m = Material.GRASS; + } else { + m = Material.matchMaterial(matSplit[0], true); + } + P.p.debugLog("converting Data Material from " + matSplit[0] + " to " + m); + } + if (m == null) continue; + SimpleItem item; + if (matSplit.length == 2) { + item = new SimpleItem(m, (short) P.p.parseInt(matSplit[1])); + } else { + item = new SimpleItem(m); + } + item.setAmount(matSection.getInt(mat)); + ingredients.add(item); + } + return ingredients; + } + + // returns Ingredients by id from the specified ingMap + public static BIngredients getIngredients(Map ingMap, String id) { + if (!ingMap.isEmpty()) { + if (ingMap.containsKey(id)) { + return ingMap.get(id); + } + } + P.p.errorLog("Ingredient id: '" + id + "' not found in data.yml"); + return new BIngredients(); + } + + // loads BIngredients from an ingredient section + public static BIngredients loadCauldronIng(ConfigurationSection section, String path) { + if (section.isConfigurationSection(path)) { + // Old way of saving + ConfigurationSection matSection = section.getConfigurationSection(path); + if (matSection != null) { + // matSection has all the materials + amount as Integers + return new BIngredients(oldDeserializeIngredients(section), 0); + } else { + P.p.errorLog("Cauldron is missing Ingredient Section"); + return new BIngredients(); + } + } else { + // New way of saving ingredients + return deserializeIngredients(section.getString(path)); + } + } + + // load Block locations of given world + public static void loadWorldData(String uuid, World world, FileConfiguration data) { + + if (data == null) { + File file = new File(P.p.getDataFolder(), "data.yml"); + if (file.exists()) { + data = YamlConfiguration.loadConfiguration(file); + } else { + return; + } + } + + // loading BCauldron + if (data.contains("BCauldron." + uuid)) { + ConfigurationSection section = data.getConfigurationSection("BCauldron." + uuid); + for (String cauldron : section.getKeys(false)) { + // block is splitted into x/y/z + String block = section.getString(cauldron + ".block"); + if (block != null) { + String[] splitted = block.split("/"); + if (splitted.length == 3) { + + Block worldBlock = world.getBlockAt(P.p.parseInt(splitted[0]), P.p.parseInt(splitted[1]), P.p.parseInt(splitted[2])); + BIngredients ingredients = loadCauldronIng(section, cauldron + ".ingredients"); + int state = section.getInt(cauldron + ".state", 1); + + new BCauldron(worldBlock, ingredients, state); + } else { + P.p.errorLog("Incomplete Block-Data in data.yml: " + section.getCurrentPath() + "." + cauldron); + } + } else { + P.p.errorLog("Missing Block-Data in data.yml: " + section.getCurrentPath() + "." + cauldron); + } + } + } + + // loading Barrel + if (data.contains("Barrel." + uuid)) { + ConfigurationSection section = data.getConfigurationSection("Barrel." + uuid); + for (String barrel : section.getKeys(false)) { + // block spigot is splitted into x/y/z + String spigot = section.getString(barrel + ".spigot"); + if (spigot != null) { + String[] splitted = spigot.split("/"); + if (splitted.length == 3) { + + // load itemStacks from invSection + ConfigurationSection invSection = section.getConfigurationSection(barrel + ".inv"); + Block block = world.getBlockAt(P.p.parseInt(splitted[0]), P.p.parseInt(splitted[1]), P.p.parseInt(splitted[2])); + float time = (float) section.getDouble(barrel + ".time", 0.0); + byte sign = (byte) section.getInt(barrel + ".sign", 0); + + BoundingBox box = null; + if (section.contains(barrel + ".bounds")) { + String[] bds = section.getString(barrel + ".bounds", "").split(","); + if (bds.length == 6) { + box = new BoundingBox(P.p.parseInt(bds[0]), P.p.parseInt(bds[1]), P.p.parseInt(bds[2]), P.p.parseInt(bds[3]), P.p.parseInt(bds[4]), P.p.parseInt(bds[5])); + } + } else if (section.contains(barrel + ".st")) { + // Convert from Stair and Wood Locations to BoundingBox + String[] st = section.getString(barrel + ".st", "").split(","); + String[] wo = section.getString(barrel + ".wo", "").split(","); + int woLength = wo.length; + if (woLength <= 1) { + woLength = 0; + } + String[] points = new String[st.length + woLength]; + System.arraycopy(st, 0, points, 0, st.length); + if (woLength > 1) { + System.arraycopy(wo, 0, points, st.length, woLength); + } + int[] locs = ArrayUtils.toPrimitive(Arrays.stream(points).map(s -> P.p.parseInt(s)).toArray(Integer[]::new)); + box = BoundingBox.fromPoints(locs); + } + + Barrel b; + if (invSection != null) { + b = new Barrel(block, sign, box, invSection.getValues(true), time); + } else { + // Barrel has no inventory + b = new Barrel(block, sign, box, null, time); + } + + // In case Barrel Block locations were missing and could not be recreated: do not add the barrel + + if (b.getBody().getBounds() != null) { + Barrel.barrels.add(b); + } + + } else { + P.p.errorLog("Incomplete Block-Data in data.yml: " + section.getCurrentPath() + "." + barrel); + } + } else { + P.p.errorLog("Missing Block-Data in data.yml: " + section.getCurrentPath() + "." + barrel); + } + } + } + + // loading Wakeup + if (data.contains("Wakeup." + uuid)) { + ConfigurationSection section = data.getConfigurationSection("Wakeup." + uuid); + for (String wakeup : section.getKeys(false)) { + // loc of wakeup is splitted into x/y/z/pitch/yaw + String loc = section.getString(wakeup); + if (loc != null) { + String[] splitted = loc.split("/"); + if (splitted.length == 5) { + + double x = NumberUtils.toDouble(splitted[0]); + double y = NumberUtils.toDouble(splitted[1]); + double z = NumberUtils.toDouble(splitted[2]); + float pitch = NumberUtils.toFloat(splitted[3]); + float yaw = NumberUtils.toFloat(splitted[4]); + Location location = new Location(world, x, y, z, yaw, pitch); + + Wakeup.wakeups.add(new Wakeup(location)); + + } else { + P.p.errorLog("Incomplete Location-Data in data.yml: " + section.getCurrentPath() + "." + wakeup); + } + } + } + } + + } +} diff --git a/src/com/dre/brewery/filedata/ConfigUpdater.java b/src/com/dre/brewery/filedata/ConfigUpdater.java index ed2d0af..d4fbba7 100644 --- a/src/com/dre/brewery/filedata/ConfigUpdater.java +++ b/src/com/dre/brewery/filedata/ConfigUpdater.java @@ -1,17 +1,16 @@ package com.dre.brewery.filedata; -import com.dre.brewery.LegacyUtil; import com.dre.brewery.P; +import com.dre.brewery.utility.LegacyUtil; import org.bukkit.Material; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; +import java.io.*; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; +import java.util.Objects; public class ConfigUpdater { @@ -49,6 +48,25 @@ public class ConfigUpdater { config.addAll(index, Arrays.asList(newLines)); } + public void removeLine(int index) { + config.remove(index); + } + + public void addLinesAt(String[] search, int offset, String... newLines) { + int index = indexOfStart(search[0]); + int s = 1; + while (index == -1 && s < search.length) { + index = indexOfStart(search[s]); + s++; + } + + if (index != -1) { + addLines(index + offset, newLines); + } else { + appendLines(newLines); + } + } + public void saveConfig() { StringBuilder stringBuilder = new StringBuilder(); for (String line : config) { @@ -82,10 +100,35 @@ public class ConfigUpdater { + // ---- Updating Scramble Seed ---- + + public void setEncodeKey(long key) { + int index = indexOfStart("encodeKey:"); + if (index != -1) { + setLine(index, "encodeKey: " + key); + return; + } + + // Old key not present + index = indexOfStart("enableEncode:"); + if (index == -1) { + index = indexOfStart("# So enable this if you want to make recipe cheating harder"); + } + if (index == -1) { + index = indexOfStart("version:"); + } + if (index != -1) { + addLines(index + 1, "encodeKey: " + key); + } else { + addLines(1, "encodeKey: " + key); + } + + } + // ---- Updating to newer Versions ---- // Update from a specified Config version and language to the newest version - public void update(String fromVersion, boolean oldMat, String lang) { + public void update(String fromVersion, boolean oldMat, String lang, FileConfiguration yml) { if (fromVersion.equals("0.5")) { // Version 0.5 was only released for de, but with en as setting, so default to de if (!lang.equals("de")) { @@ -162,12 +205,24 @@ public class ConfigUpdater { fromVersion = "1.8"; } + if (fromVersion.equals("1.8")) { + if (de) { + update18de(yml); + } else if (lang.equals("fr")) { + update18fr(yml); + } else { + update18en(yml); + } + updateVersion(BConfig.configVersion); + fromVersion = BConfig.configVersion; + } + if (P.use1_13 && oldMat) { updateMaterials(true); updateMaterialDescriptions(de); } - if (!fromVersion.equals("1.8")) { + if (!fromVersion.equals(BConfig.configVersion)) { P.p.log(P.p.languageReader.get("Error_ConfigUpdate", fromVersion)); return; } @@ -526,12 +581,12 @@ public class ConfigUpdater { if (index != -1) { index = indexOfStart("# (WEAKNESS, INCREASE_DAMAGE, SLOW und SPEED sind immer verborgen.) Mögliche Effekte:"); if (index != -1) { - config.remove(index); + removeLine(index); } } index = indexOfStart("# Bei Effekten mit sofortiger Wirkung "); if (index != -1) { - config.remove(index); + removeLine(index); } } @@ -625,12 +680,12 @@ public class ConfigUpdater { if (index != -1) { index = indexOfStart("# (WEAKNESS, INCREASE_DAMAGE, SLOW and SPEED are always hidden.) Possible Effects:"); if (index != -1) { - config.remove(index); + removeLine(index); } } index = indexOfStart("# instant effects "); if (index != -1) { - config.remove(index); + removeLine(index); } } @@ -693,7 +748,7 @@ public class ConfigUpdater { int index = indexOfStart("# SamplePlugin = installiertes home plugin. Unterstützt: ManagerXL."); if (index != -1) { - config.remove(index); + removeLine(index); } index = indexOfStart("# Ob der Spieler nach etwas kürzerem Ausloggen an einem zufälligen Ort \"aufwacht\" (diese müssen durch '/br Wakeup add"); @@ -815,7 +870,7 @@ public class ConfigUpdater { int index = indexOfStart("# SamplePlugin = installed home plugin. Supports: ManagerXL."); if (index != -1) { - config.remove(index); + removeLine(index); } index = indexOfStart("# If the player \"wakes up\" at a random place when offline for some time while drinking (the places have to be defined with '/br Wakeup add'"); @@ -1221,13 +1276,360 @@ public class ConfigUpdater { if (P.use1_13) updateMaterialDescriptions(false); } + private void update18de(FileConfiguration yml) { + int index = indexOfStart("# Löschen einzelner Einstellungen"); + if (index != -1) { + removeLine(index); + } + + addLinesAt(new String[] {"colorInBrewer:", "colorInBarrels:", "hangoverDays:", "language:"}, 1, "", + "# Ob in den Iteminformationen immer 1-5 Sterne für die Qualität angezeigt werden sollen, oder nur beim brauen [true]", + "alwaysShowQuality: true", + "", + "# Ob in den Iteminformationen immer der Alkoholgehalt angezeigt weden soll, oder nur im Braustand [false]", + "alwaysShowAlc: false"); + + addLinesAt(new String[] {"maxBrewsInMCBarrels:", "openLargeBarrelEverywhere:", "language:"}, 1, "", + "# Benutzte Zutaten und andere Brau-Daten werden in allen Brewery Tränken gespeichert. Um zu verhindern,", + "# dass gehackte clients diese Daten auslesen um Rezepte herauszufinden, können diese encodiert werden.", + "# Einziger Nachteil: Tränke können nur auf Servern mit dem gleichen encodeKey benutzt werden.", + "# Dies kann also aktiviert werden um Rezept-cheating schwerer zu machen, aber keine Tränke per World Download, Schematic, o.ä. geteilt werden. [false]", + "enableEncode: false", + "encodeKey: 0"); + + if (indexOfStart("debug:") == -1) { + addLinesAt(new String[]{"autosave:", "version:"}, 1, "", + "# Debug Nachrichten im Log anzeigen [false]", + "debug: false"); + } + + index = indexOfStart("oldMat:") + 1; + if (index == 0) { + index = indexOfStart("version:") + 1; + if (index == 0) { + index = 2; + } + } + // Custom Items and start of cauldron section + applyPatch("config/patches/de18.txt", index); + + index = indexOfStart("%%%%MAT1%%%%"); + if (index != -1) { + if (P.use1_13) { + setLine(index, " material: Barrier"); + } else { + setLine(index, " material: BEDROCK"); + } + } + index = indexOfStart("%%%%MAT2%%%%"); + if (index != -1) { + removeLine(index); + if (P.use1_13) { + addLines(index, " material:", + " - Acacia_Door", + " - Oak_Door", + " - Spruce_Door"); + } else { + addLines(index, " material:", + " - WOODEN_DOOR", + " - IRON_DOOR"); + } + } + + index = indexOfStart("# -- Eine Zutat:"); + if (index == -1) { + index = indexOfStart("cauldron:"); + if (index == -1) { + // Patch failed + index = indexOfStart("version:"); + if (index == -1) { + index = 0; + } + addLines(index + 1, "cauldron:"); + index ++; + } + } + convertCookedSection(yml, index + 1); + + addLinesAt(new String[]{"# Eine Liste von allen Materialien", "# ingredients:"}, 1, + "# Plugin Items mit 'Plugin:Id' (Im Moment ExoticGarden, Slimefun, MMOItems, Brewery)", + "# Oder ein oben definiertes Custom Item"); + + addLinesAt(new String[]{"# alcohol:", "# difficulty:", "# ingredients:", "# -- Rezepte"}, 1, + "# lore: Auflistung von zusätzlichem Text auf dem fertigen Trank. (Farbcodes möglich: z.b. &6)", + "# Lore nur für bestimmte Qualität möglich mit + Schlecht, ++ Mittel, +++ Gut, vorne anhängen.", + "# servercommands: Liste von Befehlen ausgeführt vom Server wenn der Trank getrunken wird", + "# playercommands: Liste von Befehlen ausgeführt vom Spieler wenn der Trank getrunken wird", + "# drinkmessage: Nachricht im Chat beim trinken des Trankes", + "# drinktitle: Nachricht als Titel auf dem Bildschirm an den Spieler beim trinken des Trankes"); + + addLinesAt(new String[]{"useGriefPrevention:", "useWorldGuard:", "# -- Plugin Kompatiblit"}, 1, "useGMInventories: true"); + + index = indexOfStart("# cooked:"); + if (index != -1) { + removeLine(index); + } + index = indexOfStart("# [Beispiel] MATERIAL:"); + if (index != -1) { + removeLine(index); + } + + } + + private void update18fr(FileConfiguration yml) { + int index = indexOfStart("# Supprimer un para"); + if (index != -1) { + removeLine(index); + } + + addLinesAt(new String[] {"colorInBrewer:", "colorInBarrels:", "hangoverDays:", "language:"}, 1, "\n" + + "# Toujours montrer les 1-5 étoiles sur les objets en fonction de leur qualité. S'ils sont faux, ils n'apparaîtront que lors de l'infusion. [true]", + "alwaysShowQuality: true", + "", + "# Toujours indiquer la teneur en alcool sur les objets. S'il est false, il n'apparaîtra que dans le stand de brassage. [false]", + "alwaysShowAlc: false"); + + addLinesAt(new String[] {"maxBrewsInMCBarrels:", "openLargeBarrelEverywhere:", "language:"}, 1, "", + "# Les ingrédients et autres données de brassage utilisés sont sauvegardés dans tous les articles de brasserie. [false]", + "# Pour empêcher les clients piratés de lire exactement ce qui a été utilisé pour infuser un élément, les données peuvent être encodées/brouillées.", + "# Il s'agit d'un processus rapide pour empêcher les joueurs de pirater des recettes, une fois qu'ils mettent la main sur une bière.", + "# Seul inconvénient: Les boissons brassicoles ne peuvent être utilisés que sur un autre serveur avec la même clé de chiffrement.", + "# Activez cette option si vous voulez rendre la tricherie des recettes plus difficile, mais ne partagez pas les infusions par téléchargement mondial, schémas ou autres moyens.", + "enableEncode: false", + "encodeKey: 0"); + + if (indexOfStart("debug:") == -1) { + addLinesAt(new String[]{"autosave:", "version:"}, 1, "", + "# Show debug messages in log [false]", + "debug: false"); + } + + index = indexOfStart("oldMat:") + 1; + if (index == 0) { + index = indexOfStart("version:") + 1; + if (index == 0) { + index = 2; + } + } + // Custom Items and start of cauldron section + applyPatch("config/patches/fr18.txt", index); + + index = indexOfStart("%%%%MAT1%%%%"); + if (index != -1) { + if (P.use1_13) { + setLine(index, " material: Barrier"); + } else { + setLine(index, " material: BEDROCK"); + } + } + index = indexOfStart("%%%%MAT2%%%%"); + if (index != -1) { + removeLine(index); + if (P.use1_13) { + addLines(index, " material:", + " - Acacia_Door", + " - Oak_Door", + " - Spruce_Door"); + } else { + addLines(index, " material:", + " - WOODEN_DOOR", + " - IRON_DOOR"); + } + } + + index = indexOfStart(" # -- Un ingr"); + if (index == -1) { + index = indexOfStart("cauldron:"); + if (index == -1) { + // Patch failed + index = indexOfStart("version:"); + if (index == -1) { + index = 0; + } + addLines(index + 1, "cauldron:"); + index ++; + } + } + convertCookedSection(yml, index + 1); + + addLinesAt(new String[]{"# Une liste des mat", "# ingredients:"}, 1, + "# Plugin items avec 'plugin:id' (Actuellement supporté ExoticGarden, Slimefun, MMOItems, Brewery)", + "# Ou un élément personnalisé défini ci-dessus"); + + addLinesAt(new String[]{"# alcohol:", "# difficulty:", "# ingredients:", "# -- Recette "}, 1, + "# lore: Liste des textes supplémentaires sur le breuvage fini. (Codes de formatage possibles : tels que &6)", + "# Texte spécifique de qualité possible, en utilisant + mauvais, ++ normal, +++ bon, ajouté à l'avant de la ligne.", + "# servercommands: Liste des commandes exécutées par le serveur lors de la consommation de la potion", + "# playercommands: Liste des commandes exécutées par le joueur lors de la consommation de la potion", + "# drinkmessage: Chat-message au joueur lorsqu'il boit la potion", + "# drinktitle: Titre à l'écran du joueur lorsqu'il boit la potion"); + + addLinesAt(new String[]{"useGriefPrevention:", "useWorldGuard:", "# -- Plugin Compatibility"}, 1, "useGMInventories: true"); + + index = indexOfStart("# cooked:"); + if (index != -1) { + removeLine(index); + } + index = indexOfStart("# [Exemple] MATERIEL"); + if (index != -1) { + removeLine(index); + } + + } + + private void update18en(FileConfiguration yml) { + int index = indexOfStart("# Deleting of single settings"); + if (index != -1) { + removeLine(index); + } + + addLinesAt(new String[] {"colorInBrewer:", "colorInBarrels:", "hangoverDays:", "language:"}, 1, "", + "# Always show the 1-5 stars on the item depending on the quality. If false, they will only appear when brewing [true]", + "alwaysShowQuality: true", + "", + "# Always show the alcohol content on the item. If false, it will only show in the brewing stand [false]", + "alwaysShowAlc: false"); + + addLinesAt(new String[] {"maxBrewsInMCBarrels:", "openLargeBarrelEverywhere:", "language:"}, 1, "", + "# The used Ingredients and other brewing-data is saved to all Brewery Items. To prevent", + "# hacked clients from reading what exactly was used to brew an item, the data can be encoded/scrambled.", + "# This is a fast process to stop players from hacking out recipes, once they get hold of a brew.", + "# Only drawback: brew items can only be used on another server with the same encodeKey.", + "# So enable this if you want to make recipe cheating harder, but don't share any brews by world download, schematics, or other means. [false]", + "enableEncode: false", + "encodeKey: 0"); + + if (indexOfStart("debug:") == -1) { + addLinesAt(new String[]{"autosave:", "version:"}, 1, "", + "# Show debug messages in log [false]", + "debug: false"); + } + + index = indexOfStart("oldMat:") + 1; + if (index == 0) { + index = indexOfStart("version:") + 1; + if (index == 0) { + index = 2; + } + } + // Custom Items and start of cauldron section + applyPatch("config/patches/en18.txt", index); + + index = indexOfStart("%%%%MAT1%%%%"); + if (index != -1) { + if (P.use1_13) { + setLine(index, " material: Barrier"); + } else { + setLine(index, " material: BEDROCK"); + } + } + index = indexOfStart("%%%%MAT2%%%%"); + if (index != -1) { + removeLine(index); + if (P.use1_13) { + addLines(index, " material:", + " - Acacia_Door", + " - Oak_Door", + " - Spruce_Door"); + } else { + addLines(index, " material:", + " - WOODEN_DOOR", + " - IRON_DOOR"); + } + } + + index = indexOfStart(" # -- One Ingredient"); + if (index == -1) { + index = indexOfStart("cauldron:"); + if (index == -1) { + // Patch failed + index = indexOfStart("version:"); + if (index == -1) { + index = 0; + } + addLines(index + 1, "cauldron:"); + index ++; + } + } + convertCookedSection(yml, index + 1); + + addLinesAt(new String[]{"# A list of materials", "# ingredients:"}, 1, + "# Plugin items with 'plugin:id' (Currently supporting ExoticGarden, Slimefun, MMOItems, Brewery)", + "# Or a custom item defined above"); + + addLinesAt(new String[]{"# alcohol:", "# difficulty:", "# ingredients:", "# -- Recipes"}, 1, + "# lore: List of additional text on the finished brew. (Formatting codes possible: such as &6)", + "# Specific lore for quality possible, using + bad, ++ normal, +++ good, added to the front of the line.", + "# servercommands: List of Commands executed by the Server when drinking the brew", + "# playercommands: List of Commands executed by the Player when drinking the brew", + "# drinkmessage: Chat-message to the Player when drinking the Brew", + "# drinktitle: Title on Screen to the Player when drinking the Brew"); + + addLinesAt(new String[]{"useGriefPrevention:", "useWorldGuard:", "# -- Plugin Compatibility"}, 1, "useGMInventories: true"); + + index = indexOfStart("# cooked:"); + if (index != -1) { + removeLine(index); + } + index = indexOfStart("# [Example] MATERIAL:"); + if (index != -1) { + removeLine(index); + } + + } + + + + private void convertCookedSection(FileConfiguration yml, int toLine) { + ConfigurationSection cookedSection = yml.getConfigurationSection("cooked"); + if (cookedSection != null) { + for (String ing : cookedSection.getKeys(false)) { + String name = cookedSection.getString(ing); + addLines(toLine, + " " + ing.toLowerCase() + ":", + " name: " + name, + " ingredients:", + " - " + ing, + ""); + toLine += 5; + } + + int index = indexOfStart("cooked:"); + if (index != -1) { + int size = cookedSection.getKeys(false).size(); + while (size >= 0) { + removeLine(index); + size--; + } + } + } + + + } + + public void applyPatch(String resourcePath, int toLine) { + try { + List patch = new ArrayList<>(); + BufferedReader reader = new BufferedReader(new InputStreamReader(Objects.requireNonNull(P.p.getResource(resourcePath), "Resource not found"))); + String currentLine; + while((currentLine = reader.readLine()) != null) { + patch.add(currentLine); + } + reader.close(); + config.addAll(toLine, patch); + } catch (IOException | NullPointerException e) { + P.p.errorLog("Could not apply Patch: " + resourcePath); + e.printStackTrace(); + } + } + // Update all Materials to Minecraft 1.13 private void updateMaterials(boolean toMC113) { int index; if (toMC113) { index = indexOfStart("oldMat:"); if (index != -1) { - config.remove(index); + removeLine(index); } } else { index = indexOfStart("version:"); @@ -1286,6 +1688,29 @@ public class ConfigUpdater { index++; } } + + index = indexOfStart("cauldron:"); + if (index != -1) { + index++; + int endIndex = indexOfStart("recipes:"); + if (endIndex < index) { + endIndex = indexOfStart(" cookingtime:"); + } + if (endIndex < index) { + endIndex = indexOfStart("useWorldGuard:"); + } + while (index < endIndex) { + if (config.get(index).matches("^\\s+ingredients:.*")) { + index++; + while (config.get(index).matches("^\\s+- .+")) { + line = config.get(index); + setLine(index, convertMaterial(line, "^\\s+- ", "(,.*|)/.*", toMC113)); + index++; + } + } + index++; + } + } } private String convertMaterial(String line, String regexPrefix, String regexPostfix, boolean toMC113) { @@ -1337,33 +1762,33 @@ public class ConfigUpdater { index = indexOfStart("# Es kann ein Data-Wert (durability) angegeben werden"); if (index != -1) { - config.remove(index); + removeLine(index); } index = indexOfStart("# Wenn Vault installiert ist"); if (index != -1) { - config.remove(index); + removeLine(index); } index = indexOfStart("# Vault erkennt Namen wie"); if (index != -1) { - config.remove(index); + removeLine(index); } index = indexOfStart("# - Jungle Leaves/64 # Nur mit Vault"); if (index != -1) { - config.remove(index); + removeLine(index); } index = indexOfStart("# - Green Dye/6 # Nur mit Vault"); if (index != -1) { - config.remove(index); + removeLine(index); } index = indexOfStart("# Ein 'X' an den Namen"); if (index != -1) { - config.remove(index); + removeLine(index); } index = indexOfStart("# Effekte sind ab der 1.9 immer verborgen"); if (index != -1) { - config.remove(index); + removeLine(index); } } else { index = indexOfStart("# ingredients: List of 'material,data/amount'"); @@ -1373,33 +1798,33 @@ public class ConfigUpdater { index = indexOfStart("# You can specify a data (durability) value"); if (index != -1) { - config.remove(index); + removeLine(index); } index = indexOfStart("# If Vault is installed normal names can be used"); if (index != -1) { - config.remove(index); + removeLine(index); } index = indexOfStart("# Vault will recognize things"); if (index != -1) { - config.remove(index); + removeLine(index); } index = indexOfStart("# - Jungle Leaves/64 # Only with Vault"); if (index != -1) { - config.remove(index); + removeLine(index); } index = indexOfStart("# - Green Dye/6 # Only with Vault"); if (index != -1) { - config.remove(index); + removeLine(index); } index = indexOfStart("# Suffix name with 'X' to hide effect"); if (index != -1) { - config.remove(index); + removeLine(index); } index = indexOfStart("# Effects are always hidden in 1.9 and newer"); if (index != -1) { - config.remove(index); + removeLine(index); } } } diff --git a/src/com/dre/brewery/filedata/DataSave.java b/src/com/dre/brewery/filedata/DataSave.java index ffd9043..5c3dfa3 100644 --- a/src/com/dre/brewery/filedata/DataSave.java +++ b/src/com/dre/brewery/filedata/DataSave.java @@ -1,22 +1,16 @@ package com.dre.brewery.filedata; -import java.io.File; - -import com.dre.brewery.MCBarrel; -import com.dre.brewery.Util; +import com.dre.brewery.*; +import com.dre.brewery.utility.BUtil; import org.bukkit.World; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.scheduler.BukkitRunnable; -import com.dre.brewery.BCauldron; -import com.dre.brewery.BPlayer; -import com.dre.brewery.Barrel; -import com.dre.brewery.Brew; -import com.dre.brewery.P; -import com.dre.brewery.Wakeup; +import java.util.ArrayList; +import java.util.List; public class DataSave extends BukkitRunnable { @@ -39,6 +33,7 @@ public class DataSave extends BukkitRunnable { @Override public void run() { + long saveTime = System.nanoTime(); FileConfiguration oldData; if (read != null) { if (!read.done) { @@ -64,8 +59,21 @@ public class DataSave extends BukkitRunnable { configFile.set("installTime", Brew.installTime); configFile.set("MCBarrelTime", MCBarrel.mcBarrelTime); - if (!Brew.potions.isEmpty()) { - Brew.save(configFile.createSection("Brew")); + Brew.writePrevSeeds(configFile); + + List brewsCreated = new ArrayList<>(7); + brewsCreated.add(P.p.brewsCreated); + brewsCreated.add(P.p.brewsCreatedCmd); + brewsCreated.add(P.p.exc); + brewsCreated.add(P.p.good); + brewsCreated.add(P.p.norm); + brewsCreated.add(P.p.bad); + brewsCreated.add(P.p.terr); + configFile.set("brewsCreated", brewsCreated); + configFile.set("brewsCreatedH", brewsCreated.hashCode()); + + if (!Brew.legacyPotions.isEmpty()) { + Brew.saveLegacy(configFile.createSection("Brew")); } if (!BCauldron.bcauldrons.isEmpty() || oldData.contains("BCauldron")) { @@ -88,6 +96,9 @@ public class DataSave extends BukkitRunnable { configFile.set("Version", dataVersion); collected = true; + + P.p.debugLog("saving: " + ((System.nanoTime() - saveTime) / 1000000.0) + "ms"); + if (P.p.isEnabled()) { P.p.getServer().getScheduler().runTaskAsynchronously(P.p, new WriteData(configFile)); } else { @@ -111,7 +122,6 @@ public class DataSave extends BukkitRunnable { // Save all data. Takes a boolean whether all data should be collected in instantly public static void save(boolean collectInstant) { - long time = System.nanoTime(); if (running != null) { P.p.log("Another Save was started while a Save was in Progress"); if (collectInstant) { @@ -119,24 +129,17 @@ public class DataSave extends BukkitRunnable { } return; } - File datafile = new File(P.p.getDataFolder(), "data.yml"); - if (datafile.exists()) { - ReadOldData read = new ReadOldData(); - if (collectInstant) { - read.run(); - running = new DataSave(read); - running.run(); - } else { - read.runTaskAsynchronously(P.p); - running = new DataSave(read); - running.runTaskTimer(P.p, 1, 2); - } - } else { - running = new DataSave(null); + ReadOldData read = new ReadOldData(); + if (collectInstant) { + read.run(); + running = new DataSave(read); running.run(); + } else { + read.runTaskAsynchronously(P.p); + running = new DataSave(read); + running.runTaskTimer(P.p, 1, 2); } - P.p.debugLog("saving: " + ((System.nanoTime() - time) / 1000000.0) + "ms"); } public static void autoSave() { @@ -154,7 +157,7 @@ public class DataSave extends BukkitRunnable { for (World world : P.p.getServer().getWorlds()) { String worldName = world.getName(); if (worldName.startsWith("DXL_")) { - worldName = Util.getDxlName(worldName); + worldName = BUtil.getDxlName(worldName); root.set("Worlds." + worldName, 0); } else { worldName = world.getUID().toString(); diff --git a/src/com/dre/brewery/filedata/DataUpdater.java b/src/com/dre/brewery/filedata/DataUpdater.java index 0bacfe1..fc84159 100644 --- a/src/com/dre/brewery/filedata/DataUpdater.java +++ b/src/com/dre/brewery/filedata/DataUpdater.java @@ -1,107 +1,106 @@ -package com.dre.brewery.filedata; - -import com.dre.brewery.LegacyUtil; -import java.io.File; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -import org.bukkit.Material; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.FileConfiguration; - -import com.dre.brewery.P; - -public class DataUpdater { - - private FileConfiguration data; - private File file; - - public DataUpdater(FileConfiguration data, File file) { - this.data = data; - this.file = file; - } - - - - public void update(String fromVersion) { - if (fromVersion.equalsIgnoreCase("1.0")) { - update10(); - //fromVersion = "1.1"; - } - - try { - data.save(file); - } catch (IOException e) { - e.printStackTrace(); - } - } - - - - - @SuppressWarnings("deprecation") - public void update10() { - - data.set("Version", DataSave.dataVersion); - - ConfigurationSection section = data.getConfigurationSection("Ingredients"); - try { - if (section != null) { - for (String id : section.getKeys(false)) { - ConfigurationSection matSection = section.getConfigurationSection(id + ".mats"); - if (matSection != null) { - // matSection has all the materials + amount as Integers - Map ingredients = new HashMap<>(); - for (String ingredient : matSection.getKeys(false)) { - // convert to Material - Material mat = LegacyUtil.getMaterial(P.p.parseInt(ingredient)); - if (mat != null) { - ingredients.put(mat.name(), matSection.getInt(ingredient)); - } - } - section.set(id + ".mats", ingredients); - } else { - P.p.errorLog("Ingredient id: '" + id + "' incomplete in data.yml"); - } - } - } - } catch (Exception e) { - // Getting Material by id may not work in the future - P.p.errorLog("Error Converting Ingredient Section of the Data File, newer versions of Bukkit may not support the old Save File anymore:"); - e.printStackTrace(); - } - - section = data.getConfigurationSection("BCauldron"); - if (section != null) { - try { - for (String uuid : section.getKeys(false)) { - ConfigurationSection cauldrons = section.getConfigurationSection(uuid); - if (cauldrons != null) { - for (String id : cauldrons.getKeys(false)) { - ConfigurationSection ingredientSection = cauldrons.getConfigurationSection(id + ".ingredients"); - if (ingredientSection != null) { - // has all the materials + amount as Integers - Map ingredients = new HashMap<>(); - for (String ingredient : ingredientSection.getKeys(false)) { - // convert to Material - Material mat = LegacyUtil.getMaterial(P.p.parseInt(ingredient)); - if (mat != null) { - ingredients.put(mat.name(), ingredientSection.getInt(ingredient)); - } - } - cauldrons.set(id + ".ingredients", ingredients); - } else { - P.p.errorLog("BCauldron " + id + " is missing Ingredient Section"); - } - } - } - } - } catch (Exception e) { - // Getting Material by id may not work in the future - P.p.errorLog("Error Converting Ingredient Section of Cauldrons, newer versions of Bukkit may not support the old Save File anymore:"); - e.printStackTrace(); - } - } - } -} +package com.dre.brewery.filedata; + +import com.dre.brewery.utility.LegacyUtil; +import com.dre.brewery.P; +import org.bukkit.Material; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class DataUpdater { + + private FileConfiguration data; + private File file; + + public DataUpdater(FileConfiguration data, File file) { + this.data = data; + this.file = file; + } + + + + public void update(String fromVersion) { + if (fromVersion.equalsIgnoreCase("1.0")) { + update10(); + //fromVersion = "1.1"; + } + + try { + data.save(file); + } catch (IOException e) { + e.printStackTrace(); + } + } + + + + + @SuppressWarnings("deprecation") + public void update10() { + + data.set("Version", DataSave.dataVersion); + + ConfigurationSection section = data.getConfigurationSection("Ingredients"); + try { + if (section != null) { + for (String id : section.getKeys(false)) { + ConfigurationSection matSection = section.getConfigurationSection(id + ".mats"); + if (matSection != null) { + // matSection has all the materials + amount as Integers + Map ingredients = new HashMap<>(); + for (String ingredient : matSection.getKeys(false)) { + // convert to Material + Material mat = LegacyUtil.getMaterial(P.p.parseInt(ingredient)); + if (mat != null) { + ingredients.put(mat.name(), matSection.getInt(ingredient)); + } + } + section.set(id + ".mats", ingredients); + } else { + P.p.errorLog("Ingredient id: '" + id + "' incomplete in data.yml"); + } + } + } + } catch (Exception e) { + // Getting Material by id may not work in the future + P.p.errorLog("Error Converting Ingredient Section of the Data File, newer versions of Bukkit may not support the old Save File anymore:"); + e.printStackTrace(); + } + + section = data.getConfigurationSection("BCauldron"); + if (section != null) { + try { + for (String uuid : section.getKeys(false)) { + ConfigurationSection cauldrons = section.getConfigurationSection(uuid); + if (cauldrons != null) { + for (String id : cauldrons.getKeys(false)) { + ConfigurationSection ingredientSection = cauldrons.getConfigurationSection(id + ".ingredients"); + if (ingredientSection != null) { + // has all the materials + amount as Integers + Map ingredients = new HashMap<>(); + for (String ingredient : ingredientSection.getKeys(false)) { + // convert to Material + Material mat = LegacyUtil.getMaterial(P.p.parseInt(ingredient)); + if (mat != null) { + ingredients.put(mat.name(), ingredientSection.getInt(ingredient)); + } + } + cauldrons.set(id + ".ingredients", ingredients); + } else { + P.p.errorLog("BCauldron " + id + " is missing Ingredient Section"); + } + } + } + } + } catch (Exception e) { + // Getting Material by id may not work in the future + P.p.errorLog("Error Converting Ingredient Section of Cauldrons, newer versions of Bukkit may not support the old Save File anymore:"); + e.printStackTrace(); + } + } + } +} diff --git a/src/com/dre/brewery/filedata/LanguageReader.java b/src/com/dre/brewery/filedata/LanguageReader.java index e1cb2f1..2f5e2a7 100644 --- a/src/com/dre/brewery/filedata/LanguageReader.java +++ b/src/com/dre/brewery/filedata/LanguageReader.java @@ -1,24 +1,26 @@ package com.dre.brewery.filedata; -import java.io.File; -import java.io.IOException; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; - +import com.dre.brewery.P; +import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + public class LanguageReader { - private Map entries = new TreeMap<>(); - private Map defaults = new TreeMap<>(); + private Map entries = new HashMap<>(128); private File file; - private boolean changed; - - public LanguageReader(File file) { - this.setDefaults(); + public LanguageReader(File file, String defaultPath) { /* Load */ this.file = file; @@ -30,149 +32,55 @@ public class LanguageReader { } /* Check */ - this.check(); + check(defaultPath); } - private void setDefaults() { - - /* Player */ - defaults.put("Player_BarrelCreated", "Barrel created"); - defaults.put("Player_BarrelFull", "&cThis barrel can''t hold any more drinks"); - defaults.put("Player_CauldronInfo1", "This cauldron has been boiling for &v1 minutes."); - defaults.put("Player_CauldronInfo2", "This cauldron has just started boiling."); - defaults.put("Player_CantDrink", "You can't drink any more."); - defaults.put("Player_DrunkPassOut", "You drank too much and passed out."); - defaults.put("Player_LoginDeny", "Your character tries to log in, but is too drunk to find the server. Try again!"); - defaults.put("Player_LoginDenyLong", "Your character is really drunk and has passed out. Try again in 10 minutes!"); - defaults.put("Player_Wake", "Ohh no! I cannot remember how I got here..."); - defaults.put("Player_WakeCreated", "&aWakeup Point with id: &6&v1 &awas created successfully!"); - defaults.put("Player_WakeNotExist", "&cThe Wakeup Point with the id: &6&v1 &cdoesn't exist!"); - defaults.put("Player_WakeDeleted", "&aThe Wakeup Point with the id: &6&v1 &awas successfully deleted!"); - defaults.put("Player_WakeAlreadyDeleted", "&cThe Wakeup Point with the id: &6&v1 &chas already been deleted!"); - defaults.put("Player_WakeFilled", "&cThe Wakeup Point with the id: &6&v1&c at position &6&v2 &v3, &v4, &v5&c is filled with Blocks!"); - defaults.put("Player_WakeNoPoints", "&cThere are no Wakeup Points!"); - defaults.put("Player_WakeLast", "&aThis was the last Wakeup Point"); - defaults.put("Player_WakeTeleport", "Teleport to Wakeup Point with the id: &6&v1&f At position: &6&v2 &v3, &v4, &v5"); - defaults.put("Player_WakeHint1", "To Next Wakeup Point: Punch your fist in the air"); - defaults.put("Player_WakeHint2", "To Cancel: &9/br wakeup cancel"); - defaults.put("Player_WakeCancel", "&6Wakeup Point Check was cancelled"); - defaults.put("Player_WakeNoCheck", "&cNo Wakeup Point Check is currently active"); - defaults.put("Player_TriedToSay", "&v1 tried to say: &0&v2"); - - /* Brew */ - defaults.put("Brew_Distilled", "Distilled"); - defaults.put("Brew_BarrelRiped", "Barrel aged"); - defaults.put("Brew_Undefined", "Indefinable Brew"); - defaults.put("Brew_DistillUndefined", "Indefinable Distillate"); - defaults.put("Brew_BadPotion", "Ruined Potion"); - defaults.put("Brew_Ingredients", "Ingredients"); - defaults.put("Brew_minute", "minute"); - defaults.put("Brew_MinutePluralPostfix", "s"); - defaults.put("Brew_fermented", "fermented"); - defaults.put("Brew_-times", "-times"); - defaults.put("Brew_OneYear", "One Year"); - defaults.put("Brew_Years", "Years"); - defaults.put("Brew_HundredsOfYears", "Hundreds of Years"); - defaults.put("Brew_Woodtype", "Woodtype"); - defaults.put("Brew_ThickBrew", "Muddy Brew"); - - /* Commands */ - defaults.put("CMD_Reload", "&aConfig was successfully reloaded"); - defaults.put("CMD_Configname", "&aName for the Config is: &f&v1"); - defaults.put("CMD_Configname_Error", "&cCould not find item in your hand"); - defaults.put("CMD_Player", "&a&v1 is now &6&v2% &adrunk, with a quality of &6&v3"); - defaults.put("CMD_Player_Error", "&cThe quality has to be between 1 and 10!"); - defaults.put("CMD_Info_NotDrunk", "&v1 is not drunk"); - defaults.put("CMD_Info_Drunk", "&v1 is &6&v2% &fdrunk, with a quality of &6&v3"); - defaults.put("CMD_UnLabel", "&aLabel removed!"); - defaults.put("CMD_Persistent", "&aPotion is now Persistent and Static and may now be copied like any other item. You can remove the persistence with the same command."); - defaults.put("CMD_PersistRemove", "&cPersistent Brews cannot be removed from the Database. It would render any copies of them useless!"); - defaults.put("CMD_UnPersist", "&aPersistence and static Removed. &eEvery Potential copy NOT made with '/brew copy' could become useless now!"); - defaults.put("CMD_Copy_Error", "&6&v1 &cPotions did not fit into your inventory"); - defaults.put("CMD_CopyNotPersistent", "&eThese copies of this Brew will not be persistent or static!"); - defaults.put("CMD_Static", "&aPotion is now static and will not change in barrels or brewing stands."); - defaults.put("CMD_NonStatic", "&ePotion is not static anymore and will normally age in barrels."); - - /* Error */ - defaults.put("Error_UnknownCommand", "Unknown Command"); - defaults.put("Error_ShowHelp", "Use &6/brew help &fto display the help"); - defaults.put("Error_PlayerCommand", "&cThis command can only be executed as a player!"); - defaults.put("Error_ItemNotPotion", "&cThe item in your hand could not be identified as a potion!"); - defaults.put("Error_NoBrewName", "&cNo Recipe with Name: '&v1&c' found!"); - defaults.put("Error_Recipeload", "&cNot all recipes could be restored: More information in the server log!"); - defaults.put("Error_ConfigUpdate", "Unknown Brewery config version: v&v1, config was not updated!"); - defaults.put("Error_PersistStatic", "&cPersistent potions are always static!"); - - /* Permissions */ - defaults.put("Error_NoPermissions", "&cYou don't have permissions to do this!"); - defaults.put("Error_NoBarrelAccess", "&cYou don't have permissions to access this barrel!"); - defaults.put("Perms_NoBarrelCreate", "&cYou don't have permissions to create barrels!"); - defaults.put("Perms_NoSmallBarrelCreate", "&cYou don't have permissions to create small barrels!"); - defaults.put("Perms_NoBigBarrelCreate", "&cYou don't have permissions to create big barrels!"); - defaults.put("Perms_NoCauldronInsert", "&cYou don't have permissions to put ingredients into cauldrons!"); - defaults.put("Perms_NoCauldronFill", "&cYou don't have permissions to fill bottles from this cauldron!"); - - /* Help */ - defaults.put("Help_Help", "&6/brew help [Page] &9Shows a specific help-page"); - defaults.put("Help_Player", "&6/brew <%Drunkeness> [Quality]&9 Sets Drunkeness (and Quality) of a Player"); - defaults.put("Help_Info", "&6/brew info&9 Displays your current Drunkeness and Quality"); - defaults.put("Help_UnLabel", "&6/brew unlabel &9Removes the detailled label of a potion"); - defaults.put("Help_Copy", "&6/brew copy [Quantity]>&9 Copies the potion in your hand"); - defaults.put("Help_Delete", "&6/brew delete &9Deletes the potion in your hand"); - defaults.put("Help_InfoOther", "&6/brew info [Player]&9 Displays the current Drunkeness and Quality of [Player]"); - defaults.put("Help_Wakeup", "&6/brew wakeup list &9 Lists all wakeup points"); - defaults.put("Help_WakeupList", "&6/brew wakeup list [World]&9 Lists all wakeup points of [world]"); - defaults.put("Help_WakeupCheck", "&6/brew wakeup check &9Teleports to all wakeup points"); - defaults.put("Help_WakeupCheckSpecific", "&6/brew wakeup check &9Teleports to the wakeup point with "); - defaults.put("Help_WakeupAdd", "&6/brew wakeup add &9Adds a wakeup point at your current position"); - defaults.put("Help_WakeupRemove", "&6/brew wakeup remove &9Removes the wakeup point with "); - defaults.put("Help_Reload", "&6/brew reload &9Reload config"); - defaults.put("Help_Configname", "&6/brew ItemName &9Display name of item in hand for the config"); - defaults.put("Help_Persist", "&6/brew persist &9Make Brew persistent -> copyable by any plugin and technique"); - defaults.put("Help_Static", "&6/brew static &9Make Brew static -> No further ageing or distilling"); - defaults.put("Help_Create", "&6/brew create [Quality] [Player] &9Create a Brew with optional quality (1-10)"); - - /* Etc. */ - defaults.put("Etc_Usage", "Usage:"); - defaults.put("Etc_Page", "Page"); - defaults.put("Etc_Barrel", "Barrel"); - } - - private void check() { - for (String defaultEntry : defaults.keySet()) { - if (!entries.containsKey(defaultEntry)) { - entries.put(defaultEntry, defaults.get(defaultEntry)); - changed = true; + private void check(String defaultPath) { + FileConfiguration defaults = null; + ConfigUpdater updater = null; + String line; + InputStream resource = P.p.getResource(defaultPath); + if (resource == null) return; + try (BufferedReader reader = new BufferedReader(new InputStreamReader(resource))) { + while ((line = reader.readLine()) != null) { + int index = line.indexOf(':'); + if (index != -1) { + String key = line.substring(0, index); + if (!entries.containsKey(key)) { + if (defaults == null) { + defaults = new YamlConfiguration(); + defaults.load(new BufferedReader(new InputStreamReader(Objects.requireNonNull(P.p.getResource(defaultPath))))); + updater = new ConfigUpdater(file); + updater.appendLines("", "# Updated"); + } + entries.put(key, defaults.getString(key)); + updater.appendLines(line); + } + } } + if (updater != null) { + createBackup(); + updater.saveConfig(); + P.p.log("Language file updated"); + } + } catch (IOException | InvalidConfigurationException e) { + e.printStackTrace(); + P.p.errorLog("Language File could not be updated"); } } @SuppressWarnings("ResultOfMethodCallIgnored") - public void save() { - if (changed) { - /* Copy old File */ - File source = new File(file.getPath()); - String filePath = file.getPath(); - File temp = new File(filePath.substring(0, filePath.length() - 4) + "_old.yml"); + private void createBackup() { + /* Copy old File */ + File source = new File(file.getPath()); + String filePath = file.getPath(); + File backup = new File(filePath.substring(0, filePath.length() - 4) + "_old.yml"); - if (temp.exists()) - temp.delete(); - - source.renameTo(temp); - - /* Save */ - FileConfiguration configFile = new YamlConfiguration(); - - for (String key : entries.keySet()) { - configFile.set(key, entries.get(key)); - } - - try { - configFile.save(file); - } catch (IOException e) { - e.printStackTrace(); - } + if (backup.exists()) { + backup.delete(); } + + source.renameTo(backup); } public String get(String key, String... args) { diff --git a/src/com/dre/brewery/filedata/ReadOldData.java b/src/com/dre/brewery/filedata/ReadOldData.java index ef04b7e..e24e065 100644 --- a/src/com/dre/brewery/filedata/ReadOldData.java +++ b/src/com/dre/brewery/filedata/ReadOldData.java @@ -18,6 +18,12 @@ public class ReadOldData extends BukkitRunnable { @Override public void run() { File datafile = new File(P.p.getDataFolder(), "data.yml"); + if (!datafile.exists()) { + data = new YamlConfiguration(); + done = true; + return; + } + data = YamlConfiguration.loadConfiguration(datafile); if (DataSave.lastBackup > 10) { diff --git a/src/com/dre/brewery/filedata/WriteData.java b/src/com/dre/brewery/filedata/WriteData.java index c624851..a1f5a44 100644 --- a/src/com/dre/brewery/filedata/WriteData.java +++ b/src/com/dre/brewery/filedata/WriteData.java @@ -8,6 +8,9 @@ import org.bukkit.configuration.file.FileConfiguration; import com.dre.brewery.P; +/** + * Writes the collected Data to file in Async Thread + */ public class WriteData implements Runnable { private FileConfiguration data; diff --git a/src/com/dre/brewery/integration/CitadelBarrel.java b/src/com/dre/brewery/integration/CitadelBarrel.java deleted file mode 100644 index cb36a80..0000000 --- a/src/com/dre/brewery/integration/CitadelBarrel.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.dre.brewery.integration; - -import org.bukkit.block.Block; -import org.bukkit.entity.Player; - -import com.dre.brewery.P; - -import vg.civcraft.mc.citadel.Citadel; -import vg.civcraft.mc.citadel.ReinforcementManager; -import vg.civcraft.mc.citadel.reinforcement.NullReinforcement; -import vg.civcraft.mc.citadel.reinforcement.PlayerReinforcement; -import vg.civcraft.mc.citadel.reinforcement.Reinforcement; - -/** - * Basic Citadel support to prevent randos from stealing your barrel aging brews - * - * @author ProgrammerDan - */ -public class CitadelBarrel { - static P brewery = P.p; - - public static boolean checkAccess(Player player, Block sign) { - ReinforcementManager manager = Citadel.getReinforcementManager(); - - Reinforcement rein = manager.getReinforcement(sign); - - if (rein == null) return true; // no protections in place. - - if (rein instanceof PlayerReinforcement) { - PlayerReinforcement prein = (PlayerReinforcement) rein; - if (prein.canAccessChests(player)) { - return true; - } - } else if (rein instanceof NullReinforcement) { - return true; - } - // no support for multiblock atm, would require namelayer support. - - // special locked, or no access. - brewery.msg(player, brewery.languageReader.get("Error_NoBarrelAccess")); - return false; - } -} diff --git a/src/com/dre/brewery/integration/IntegrationListener.java b/src/com/dre/brewery/integration/IntegrationListener.java new file mode 100644 index 0000000..c4c8a19 --- /dev/null +++ b/src/com/dre/brewery/integration/IntegrationListener.java @@ -0,0 +1,251 @@ +package com.dre.brewery.integration; + +import com.dre.brewery.Barrel; +import com.dre.brewery.P; +import com.dre.brewery.api.events.barrel.BarrelAccessEvent; +import com.dre.brewery.api.events.barrel.BarrelDestroyEvent; +import com.dre.brewery.api.events.barrel.BarrelRemoveEvent; +import com.dre.brewery.filedata.BConfig; +import com.dre.brewery.integration.barrel.GriefPreventionBarrel; +import com.dre.brewery.integration.barrel.LWCBarrel; +import com.dre.brewery.integration.barrel.LogBlockBarrel; +import com.dre.brewery.integration.item.MMOItemsPluginItem; +import com.dre.brewery.recipe.BCauldronRecipe; +import com.dre.brewery.recipe.RecipeItem; +import com.dre.brewery.utility.LegacyUtil; +import net.Indyuce.mmoitems.MMOItems; +import net.Indyuce.mmoitems.api.item.NBTItem; +import org.bukkit.GameMode; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.plugin.Plugin; + +public class IntegrationListener implements Listener { + + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + public void onBarrelAccessLowest(BarrelAccessEvent event) { + if (BConfig.useWG) { + Plugin plugin = P.p.getServer().getPluginManager().getPlugin("WorldGuard"); + if (plugin != null) { + try { + if (!BConfig.wg.checkAccess(event.getPlayer(), event.getSpigot(), plugin)) { + event.setCancelled(true); + P.p.msg(event.getPlayer(), P.p.languageReader.get("Error_NoBarrelAccess")); + } + } catch (Throwable e) { + event.setCancelled(true); + P.p.errorLog("Failed to Check WorldGuard for Barrel Open Permissions!"); + P.p.errorLog("Brewery was tested with version 5.8, 6.1 to 7.0 of WorldGuard!"); + P.p.errorLog("Disable the WorldGuard support in the config and do /brew reload"); + e.printStackTrace(); + Player player = event.getPlayer(); + if (player.hasPermission("brewery.admin") || player.hasPermission("brewery.mod")) { + P.p.msg(player, "&cWorldGuard check Error, Brewery was tested with up to v7.0 of Worldguard"); + P.p.msg(player, "&cSet &7useWorldGuard: false &cin the config and /brew reload"); + } else { + P.p.msg(player, "&cError opening Barrel, please report to an Admin!"); + } + } + } + } + } + + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onBarrelAccess(BarrelAccessEvent event) { + if (BConfig.useGMInventories) { + Plugin pl = P.p.getServer().getPluginManager().getPlugin("GameModeInventories"); + if (pl != null && pl.isEnabled()) { + try { + if (pl.getConfig().getBoolean("restrict_creative")) { + Player player = event.getPlayer(); + if (player.getGameMode() == GameMode.CREATIVE) { + if (!pl.getConfig().getBoolean("bypass.inventories") || (!player.hasPermission("gamemodeinventories.bypass") && !player.isOp())) { + event.setCancelled(true); + if (!pl.getConfig().getBoolean("dont_spam_chat")) { + P.p.msg(event.getPlayer(), P.p.languageReader.get("Error_NoBarrelAccess")); + } + return; + } + } + } + } catch (Throwable e) { + P.p.errorLog("Failed to Check GameModeInventories for Barrel Open Permissions!"); + P.p.errorLog("Players will be able to open Barrel with GameMode Creative"); + e.printStackTrace(); + BConfig.useGMInventories = false; + } + } else { + BConfig.useGMInventories = false; + } + } + if (BConfig.useGP) { + if (P.p.getServer().getPluginManager().isPluginEnabled("GriefPrevention")) { + try { + if (!GriefPreventionBarrel.checkAccess(event)) { + P.p.msg(event.getPlayer(), P.p.languageReader.get("Error_NoBarrelAccess")); + event.setCancelled(true); + return; + } + } catch (Throwable e) { + event.setCancelled(true); + P.p.errorLog("Failed to Check GriefPrevention for Barrel Open Permissions!"); + P.p.errorLog("Brewery was tested with GriefPrevention v14.5 - v16.9"); + P.p.errorLog("Disable the GriefPrevention support in the config and do /brew reload"); + e.printStackTrace(); + Player player = event.getPlayer(); + if (player.hasPermission("brewery.admin") || player.hasPermission("brewery.mod")) { + P.p.msg(player, "&cGriefPrevention check Error, Brewery was tested with up to v16.9 of GriefPrevention"); + P.p.msg(player, "&cSet &7useGriefPrevention: false &cin the config and /brew reload"); + } else { + P.p.msg(player, "&cError opening Barrel, please report to an Admin!"); + } + } + } + } + + if (BConfig.useLWC) { + Plugin plugin = P.p.getServer().getPluginManager().getPlugin("LWC"); + if (plugin != null) { + + // If the Clicked Block was the Sign, LWC already knows and we dont need to do anything here + if (!LegacyUtil.isSign(event.getClickedBlock().getType())) { + Block sign = event.getBarrel().getBody().getSignOfSpigot(); + // If the Barrel does not have a Sign, it cannot be locked + if (!sign.equals(event.getClickedBlock())) { + Player player = event.getPlayer(); + try { + if (!LWCBarrel.checkAccess(player, sign, plugin)) { + P.p.msg(event.getPlayer(), P.p.languageReader.get("Error_NoBarrelAccess")); + event.setCancelled(true); + } + } catch (Throwable e) { + event.setCancelled(true); + P.p.errorLog("Failed to Check LWC for Barrel Open Permissions!"); + P.p.errorLog("Brewery was tested with version 4.5.0 of LWC!"); + P.p.errorLog("Disable the LWC support in the config and do /brew reload"); + e.printStackTrace(); + if (player.hasPermission("brewery.admin") || player.hasPermission("brewery.mod")) { + P.p.msg(player, "&cLWC check Error, Brewery was tested with up to v4.5.0 of LWC"); + P.p.msg(player, "&cSet &7useLWC: false &cin the config and /brew reload"); + } else { + P.p.msg(player, "&cError opening Barrel, please report to an Admin!"); + } + } + } + } + } + } + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.LOW) + public void onBarrelDestroy(BarrelDestroyEvent event) { + if (!BConfig.useLWC) return; + + if (event.hasPlayer()) { + try { + if (LWCBarrel.denyDestroy(event.getPlayerOptional(), event.getBarrel())) { + event.setCancelled(true); + } + } catch (Throwable e) { + event.setCancelled(true); + P.p.errorLog("Failed to Check LWC for Barrel Break Permissions!"); + P.p.errorLog("Brewery was tested with version 4.5.0 of LWC!"); + P.p.errorLog("Disable the LWC support in the config and do /brew reload"); + e.printStackTrace(); + Player player = event.getPlayerOptional(); + if (player.hasPermission("brewery.admin") || player.hasPermission("brewery.mod")) { + P.p.msg(player, "&cLWC check Error, Brewery was tested with up to v4.5.0 of LWC"); + P.p.msg(player, "&cSet &7useLWC: false &cin the config and /brew reload"); + } else { + P.p.msg(player, "&cError breaking Barrel, please report to an Admin!"); + } + } + } else { + try { + if (event.getReason() == BarrelDestroyEvent.Reason.EXPLODED) { + if (LWCBarrel.denyExplosion(event.getBarrel())) { + event.setCancelled(true); + } + } else { + if (LWCBarrel.denyDestroyOther(event.getBarrel())) { + event.setCancelled(true); + } + } + } catch (Throwable e) { + event.setCancelled(true); + P.p.errorLog("Failed to Check LWC on Barrel Destruction!"); + P.p.errorLog("Brewery was tested with version 4.5.0 of LWC!"); + P.p.errorLog("Disable the LWC support in the config and do /brew reload"); + e.printStackTrace(); + } + } + } + + @EventHandler + public void onBarrelRemove(BarrelRemoveEvent event) { + if (!BConfig.useLWC) return; + + try { + LWCBarrel.remove(event.getBarrel()); + } catch (Throwable e) { + P.p.errorLog("Failed to Remove LWC Lock from Barrel!"); + P.p.errorLog("Brewery was tested with version 4.5.0 of LWC!"); + e.printStackTrace(); + } + } + + @EventHandler + public void onInventoryClose(InventoryCloseEvent event) { + if (BConfig.useLB) { + if (event.getInventory().getHolder() instanceof Barrel) { + try { + LogBlockBarrel.closeBarrel(event.getPlayer(), event.getInventory()); + } catch (Exception e) { + P.p.errorLog("Failed to Log Barrel to LogBlock!"); + P.p.errorLog("Brewery was tested with version 1.94 of LogBlock!"); + e.printStackTrace(); + } + } + } + } + + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onInteract(PlayerInteractEvent event) { + // Cancel the Interact Event early, so MMOItems does not act before us and consume the item when trying to add item to Cauldron + if (!P.use1_9) return; + if (BConfig.hasMMOItems == null) { + BConfig.hasMMOItems = P.p.getServer().getPluginManager().isPluginEnabled("MMOItems"); + } + if (!BConfig.hasMMOItems) return; + try { + if (event.getAction() == Action.RIGHT_CLICK_BLOCK && event.hasItem() && event.getHand() == EquipmentSlot.HAND) { + if (event.getClickedBlock() != null && event.getClickedBlock().getType() == Material.CAULDRON) { + NBTItem item = MMOItems.plugin.getNMS().getNBTItem(event.getItem()); + if (item.hasType()) { + for (RecipeItem rItem : BCauldronRecipe.acceptedCustom) { + if (rItem instanceof MMOItemsPluginItem) { + MMOItemsPluginItem mmo = ((MMOItemsPluginItem) rItem); + if (mmo.matches(event.getItem())) { + event.setCancelled(true); + P.p.playerListener.onPlayerInteract(event); + return; + } + } + } + } + } + } + } catch (Throwable e) { + P.p.errorLog("Could not check MMOItems for Item"); + e.printStackTrace(); + } + } +} diff --git a/src/com/dre/brewery/integration/GriefPreventionBarrel.java b/src/com/dre/brewery/integration/barrel/GriefPreventionBarrel.java similarity index 66% rename from src/com/dre/brewery/integration/GriefPreventionBarrel.java rename to src/com/dre/brewery/integration/barrel/GriefPreventionBarrel.java index 85edf76..9337bed 100644 --- a/src/com/dre/brewery/integration/GriefPreventionBarrel.java +++ b/src/com/dre/brewery/integration/barrel/GriefPreventionBarrel.java @@ -1,18 +1,19 @@ -package com.dre.brewery.integration; +package com.dre.brewery.integration.barrel; import com.dre.brewery.P; +import com.dre.brewery.api.events.barrel.BarrelAccessEvent; import me.ryanhamshire.GriefPrevention.Claim; import me.ryanhamshire.GriefPrevention.GriefPrevention; import me.ryanhamshire.GriefPrevention.PlayerData; -import org.bukkit.block.Block; import org.bukkit.entity.Player; public class GriefPreventionBarrel { - static P brewery = P.p; - static GriefPrevention griefPrevention = GriefPrevention.instance; + private static P brewery = P.p; - public static boolean checkAccess(Player player, Block sign) { + public static boolean checkAccess(BarrelAccessEvent event) { + GriefPrevention griefPrevention = GriefPrevention.instance; + Player player = event.getPlayer(); PlayerData playerData = griefPrevention.dataStore.getPlayerData(player.getUniqueId()); if (!griefPrevention.claimsEnabledForWorld(player.getWorld()) || playerData.ignoreClaims || !griefPrevention.config_claims_preventTheft) { @@ -21,17 +22,15 @@ public class GriefPreventionBarrel { // block container use during pvp combat if (playerData.inPvpCombat()) { - brewery.msg(player, brewery.languageReader.get("Error_NoBarrelAccess")); return false; } // check permissions for the claim the Barrel is in - Claim claim = griefPrevention.dataStore.getClaimAt(sign.getLocation(), false, playerData.lastClaim); + Claim claim = griefPrevention.dataStore.getClaimAt(event.getSpigot().getLocation(), false, playerData.lastClaim); if (claim != null) { playerData.lastClaim = claim; String noContainersReason = claim.allowContainers(player); if (noContainersReason != null) { - brewery.msg(player, brewery.languageReader.get("Error_NoBarrelAccess") + " " + noContainersReason); return false; } } diff --git a/src/com/dre/brewery/integration/LWCBarrel.java b/src/com/dre/brewery/integration/barrel/LWCBarrel.java similarity index 76% rename from src/com/dre/brewery/integration/LWCBarrel.java rename to src/com/dre/brewery/integration/barrel/LWCBarrel.java index b3ee6f5..cad18b5 100644 --- a/src/com/dre/brewery/integration/LWCBarrel.java +++ b/src/com/dre/brewery/integration/barrel/LWCBarrel.java @@ -1,12 +1,4 @@ -package com.dre.brewery.integration; - -import org.bukkit.block.Block; -import org.bukkit.entity.Player; -import org.bukkit.event.EventException; -import org.bukkit.event.HandlerList; -import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.plugin.Plugin; -import org.bukkit.plugin.RegisteredListener; +package com.dre.brewery.integration.barrel; import com.dre.brewery.Barrel; import com.dre.brewery.P; @@ -15,13 +7,24 @@ import com.griefcraft.lwc.LWC; import com.griefcraft.model.Flag; import com.griefcraft.model.Protection; import com.griefcraft.scripting.event.LWCProtectionDestroyEvent; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Player; +import org.bukkit.event.EventException; +import org.bukkit.event.HandlerList; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.RegisteredListener; public class LWCBarrel { - public static boolean checkDestroy(Player player, Barrel barrel) { + public static boolean denyDestroy(Player player, Barrel barrel) { LWC lwc = LWC.getInstance(); - Block sign = barrel.getSignOfSpigot(); + Block sign = barrel.getBody().getSignOfSpigot(); //if (!Boolean.parseBoolean(lwc.resolveProtectionConfiguration(sign, "ignoreBlockDestruction"))) { Protection protection = lwc.findProtection(sign); if (protection != null) { @@ -33,21 +36,21 @@ public class LWCBarrel { lwc.getModuleLoader().dispatchEvent(evt); if (evt.isCancelled()) { - return false; + return true; } } catch (Exception e) { lwc.sendLocale(player, "protection.internalerror", "id", "BLOCK_BREAK"); P.p.errorLog("Failed to dispatch LWCProtectionDestroyEvent"); e.printStackTrace(); - return false; + return true; } } //} - return true; + return false; } - public static boolean checkAccess(Player player, Block sign, PlayerInteractEvent event, Plugin plugin) { + public static boolean checkAccess(Player player, Block sign, Plugin plugin) { LWC lwc = LWC.getInstance(); // Disallow Chest Access with these permissions @@ -57,7 +60,7 @@ public class LWCBarrel { } // We just fake a BlockInteractEvent on the Sign for LWC, it handles it nicely. Otherwise we could copy LWCs listener in here... - PlayerInteractEvent lwcEvent = new PlayerInteractEvent(player, event.getAction(), event.getItem(), sign, event.getBlockFace()); + PlayerInteractEvent lwcEvent = new PlayerInteractEvent(player, Action.RIGHT_CLICK_BLOCK, new ItemStack(Material.AIR), sign, BlockFace.EAST); for (RegisteredListener listener : HandlerList.getRegisteredListeners(plugin)) { if (listener.getListener() instanceof LWCPlayerListener) { try { @@ -79,26 +82,21 @@ public class LWCBarrel { // If a Barrel is destroyed without player public static void remove(Barrel barrel) { - Protection protection = LWC.getInstance().findProtection(barrel.getSignOfSpigot()); + Protection protection = LWC.getInstance().findProtection(barrel.getBody().getSignOfSpigot()); if (protection != null) { protection.remove(); } } // Returns true if the block that exploded should not be removed - public static boolean blockExplosion(Barrel barrel, Block block) { - Protection protection = LWC.getInstance().findProtection(barrel.getSignOfSpigot()); + public static boolean denyExplosion(Barrel barrel) { + Protection protection = LWC.getInstance().findProtection(barrel.getBody().getSignOfSpigot()); - if (protection == null) { - barrel.remove(block, null); - return false; - } + return protection != null && !protection.hasFlag(Flag.Type.ALLOWEXPLOSIONS); + } - if (protection.hasFlag(Flag.Type.ALLOWEXPLOSIONS)) { - protection.remove(); - barrel.remove(block, null); - return false; - } - return true; + // Returns true if the block that was destroyed should not be removed + public static boolean denyDestroyOther(Barrel barrel) { + return LWC.getInstance().findProtection(barrel.getBody().getSignOfSpigot()) != null; } } diff --git a/src/com/dre/brewery/integration/LogBlockBarrel.java b/src/com/dre/brewery/integration/barrel/LogBlockBarrel.java similarity index 98% rename from src/com/dre/brewery/integration/LogBlockBarrel.java rename to src/com/dre/brewery/integration/barrel/LogBlockBarrel.java index 4d368a8..6602d66 100644 --- a/src/com/dre/brewery/integration/LogBlockBarrel.java +++ b/src/com/dre/brewery/integration/barrel/LogBlockBarrel.java @@ -1,6 +1,6 @@ -package com.dre.brewery.integration; +package com.dre.brewery.integration.barrel; -import com.dre.brewery.LegacyUtil; +import com.dre.brewery.utility.LegacyUtil; import com.dre.brewery.P; import de.diddiz.LogBlock.Actor; diff --git a/src/com/dre/brewery/integration/WGBarrel.java b/src/com/dre/brewery/integration/barrel/WGBarrel.java similarity index 81% rename from src/com/dre/brewery/integration/WGBarrel.java rename to src/com/dre/brewery/integration/barrel/WGBarrel.java index 25e29b9..033124b 100644 --- a/src/com/dre/brewery/integration/WGBarrel.java +++ b/src/com/dre/brewery/integration/barrel/WGBarrel.java @@ -1,4 +1,4 @@ -package com.dre.brewery.integration; +package com.dre.brewery.integration.barrel; import org.bukkit.block.Block; import org.bukkit.entity.Player; diff --git a/src/com/dre/brewery/integration/WGBarrelOld.java b/src/com/dre/brewery/integration/barrel/WGBarrel5.java similarity index 91% rename from src/com/dre/brewery/integration/WGBarrelOld.java rename to src/com/dre/brewery/integration/barrel/WGBarrel5.java index 8699a7a..0d0ebd1 100644 --- a/src/com/dre/brewery/integration/WGBarrelOld.java +++ b/src/com/dre/brewery/integration/barrel/WGBarrel5.java @@ -1,4 +1,4 @@ -package com.dre.brewery.integration; +package com.dre.brewery.integration.barrel; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -17,13 +17,13 @@ import com.sk89q.worldguard.protection.flags.StateFlag; import com.sk89q.worldguard.protection.managers.RegionManager; -public class WGBarrelOld implements WGBarrel { +public class WGBarrel5 implements WGBarrel { private Method allows; private Method canBuild; private Method getApplicableRegions; - public WGBarrelOld() { + public WGBarrel5() { try { allows = ApplicableRegionSet.class.getMethod("allows", StateFlag.class, LocalPlayer.class); canBuild = ApplicableRegionSet.class.getMethod("canBuild", LocalPlayer.class); @@ -48,7 +48,6 @@ public class WGBarrelOld implements WGBarrel { if (!(Boolean) allows.invoke(region, DefaultFlag.CHEST_ACCESS, localPlayer)) { if (!(Boolean) canBuild.invoke(region, localPlayer)) { - P.p.msg(player, P.p.languageReader.get("Error_NoBarrelAccess")); return false; } } diff --git a/src/com/dre/brewery/integration/WGBarrelNew.java b/src/com/dre/brewery/integration/barrel/WGBarrel6.java similarity index 70% rename from src/com/dre/brewery/integration/WGBarrelNew.java rename to src/com/dre/brewery/integration/barrel/WGBarrel6.java index 4503b3d..d3b243b 100644 --- a/src/com/dre/brewery/integration/WGBarrelNew.java +++ b/src/com/dre/brewery/integration/barrel/WGBarrel6.java @@ -1,17 +1,15 @@ -package com.dre.brewery.integration; +package com.dre.brewery.integration.barrel; -import org.bukkit.block.Block; -import org.bukkit.entity.Player; -import org.bukkit.plugin.Plugin; - -import com.dre.brewery.P; import com.sk89q.worldguard.bukkit.RegionQuery; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.bukkit.permission.RegionPermissionModel; import com.sk89q.worldguard.protection.flags.DefaultFlag; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; -public class WGBarrelNew implements WGBarrel { +public class WGBarrel6 implements WGBarrel { public boolean checkAccess(Player player, Block spigot, Plugin plugin) { WorldGuardPlugin wg = (WorldGuardPlugin) plugin; @@ -21,12 +19,8 @@ public class WGBarrelNew implements WGBarrel { RegionQuery query = wg.getRegionContainer().createQuery(); - if (!query.testBuild(spigot.getLocation(), player, DefaultFlag.USE, DefaultFlag.CHEST_ACCESS)) { - P.p.msg(player, P.p.languageReader.get("Error_NoBarrelAccess")); - return false; - } + return query.testBuild(spigot.getLocation(), player, DefaultFlag.USE, DefaultFlag.CHEST_ACCESS); - return true; } } diff --git a/src/com/dre/brewery/integration/WGBarrel7.java b/src/com/dre/brewery/integration/barrel/WGBarrel7.java similarity index 87% rename from src/com/dre/brewery/integration/WGBarrel7.java rename to src/com/dre/brewery/integration/barrel/WGBarrel7.java index 7c2ef25..e06dc29 100644 --- a/src/com/dre/brewery/integration/WGBarrel7.java +++ b/src/com/dre/brewery/integration/barrel/WGBarrel7.java @@ -1,4 +1,4 @@ -package com.dre.brewery.integration; +package com.dre.brewery.integration.barrel; import org.bukkit.block.Block; @@ -55,12 +55,7 @@ public class WGBarrel7 implements WGBarrel { RegionQuery query = platform.getRegionContainer().createQuery(); - if (!query.testBuild(new Location(world, spigot.getX(), spigot.getY(), spigot.getZ()), wg.wrapPlayer(player), Flags.USE, Flags.CHEST_ACCESS)) { - P.p.msg(player, P.p.languageReader.get("Error_NoBarrelAccess")); - return false; - } - - return true; + return query.testBuild(new Location(world, spigot.getX(), spigot.getY(), spigot.getZ()), wg.wrapPlayer(player), Flags.USE, Flags.CHEST_ACCESS); } } diff --git a/src/com/dre/brewery/integration/item/BreweryPluginItem.java b/src/com/dre/brewery/integration/item/BreweryPluginItem.java new file mode 100644 index 0000000..a815612 --- /dev/null +++ b/src/com/dre/brewery/integration/item/BreweryPluginItem.java @@ -0,0 +1,30 @@ +package com.dre.brewery.integration.item; + +import com.dre.brewery.Brew; +import com.dre.brewery.recipe.BRecipe; +import com.dre.brewery.recipe.PluginItem; +import org.bukkit.ChatColor; +import org.bukkit.inventory.ItemStack; + +/** + * For recipes that use Brewery Items as input + */ +public class BreweryPluginItem extends PluginItem { + +// When implementing this, put Brewery as softdepend in your plugin.yml! +// We're calling this as server start: +// PluginItem.registerForConfig("brewery", BreweryPluginItem::new); + + @Override + public boolean matches(ItemStack item) { + Brew brew = Brew.get(item); + if (brew != null) { + BRecipe recipe = brew.getCurrentRecipe(); + if (recipe != null) { + return recipe.getRecipeName().equalsIgnoreCase(getItemId()) || recipe.getName(10).equalsIgnoreCase(getItemId()); + } + return ChatColor.stripColor(item.getItemMeta().getDisplayName()).equalsIgnoreCase(getItemId()); + } + return false; + } +} diff --git a/src/com/dre/brewery/integration/item/MMOItemsPluginItem.java b/src/com/dre/brewery/integration/item/MMOItemsPluginItem.java new file mode 100644 index 0000000..d4df22e --- /dev/null +++ b/src/com/dre/brewery/integration/item/MMOItemsPluginItem.java @@ -0,0 +1,32 @@ +package com.dre.brewery.integration.item; + +import com.dre.brewery.P; +import com.dre.brewery.filedata.BConfig; +import com.dre.brewery.recipe.PluginItem; +import net.Indyuce.mmoitems.MMOItems; +import net.Indyuce.mmoitems.api.item.NBTItem; +import org.bukkit.inventory.ItemStack; + +public class MMOItemsPluginItem extends PluginItem { + +// When implementing this, put Brewery as softdepend in your plugin.yml! +// We're calling this as server start: +// PluginItem.registerForConfig("mmoitems", MMOItemsPluginItem::new); + + @Override + public boolean matches(ItemStack item) { + if (BConfig.hasMMOItems == null) { + BConfig.hasMMOItems = P.p.getServer().getPluginManager().isPluginEnabled("MMOItems"); + } + if (!BConfig.hasMMOItems) return false; + + try { + NBTItem nbtItem = MMOItems.plugin.getNMS().getNBTItem(item); + return nbtItem.hasType() && nbtItem.getString("MMOITEMS_ITEM_ID").equalsIgnoreCase(getItemId()); + } catch (Throwable e) { + e.printStackTrace(); + P.p.errorLog("Could not check MMOItems for Item ID"); + return false; + } + } +} diff --git a/src/com/dre/brewery/integration/item/SlimefunPluginItem.java b/src/com/dre/brewery/integration/item/SlimefunPluginItem.java new file mode 100644 index 0000000..fe33094 --- /dev/null +++ b/src/com/dre/brewery/integration/item/SlimefunPluginItem.java @@ -0,0 +1,35 @@ +package com.dre.brewery.integration.item; + +import com.dre.brewery.P; +import com.dre.brewery.filedata.BConfig; +import com.dre.brewery.recipe.PluginItem; +import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; +import org.bukkit.inventory.ItemStack; + +public class SlimefunPluginItem extends PluginItem { + +// When implementing this, put Brewery as softdepend in your plugin.yml! +// We're calling this as server start: +// PluginItem.registerForConfig("slimefun", SlimefunPluginItem::new); +// PluginItem.registerForConfig("exoticgarden", SlimefunPluginItem::new); + + @Override + public boolean matches(ItemStack item) { + if (BConfig.hasSlimefun == null) { + BConfig.hasSlimefun = P.p.getServer().getPluginManager().isPluginEnabled("Slimefun"); + } + if (!BConfig.hasSlimefun) return false; + + try { + SlimefunItem sfItem = SlimefunItem.getByItem(item); + if (sfItem != null) { + return sfItem.getID().equalsIgnoreCase(getItemId()); + } + } catch (Throwable e) { + e.printStackTrace(); + P.p.errorLog("Could not check Slimefun for Item ID"); + return false; + } + return false; + } +} diff --git a/src/com/dre/brewery/listeners/BlockListener.java b/src/com/dre/brewery/listeners/BlockListener.java index 8263d7b..37a5a4a 100644 --- a/src/com/dre/brewery/listeners/BlockListener.java +++ b/src/com/dre/brewery/listeners/BlockListener.java @@ -1,78 +1,81 @@ -package com.dre.brewery.listeners; - -import org.bukkit.block.Block; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.block.BlockBurnEvent; -import org.bukkit.event.block.BlockPistonExtendEvent; -import org.bukkit.event.block.BlockPistonRetractEvent; -import org.bukkit.event.block.SignChangeEvent; -import org.bukkit.event.block.BlockBreakEvent; - -import com.dre.brewery.Barrel; -import com.dre.brewery.BPlayer; -import com.dre.brewery.Words; -import com.dre.brewery.P; - -public class BlockListener implements Listener { - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onSignChange(SignChangeEvent event) { - String[] lines = event.getLines(); - - if (lines[0].equalsIgnoreCase("Barrel") || lines[0].equalsIgnoreCase(P.p.languageReader.get("Etc_Barrel"))) { - Player player = event.getPlayer(); - if (!player.hasPermission("brewery.createbarrel.small") && !player.hasPermission("brewery.createbarrel.big")) { - P.p.msg(player, P.p.languageReader.get("Perms_NoBarrelCreate")); - return; - } - if (Barrel.create(event.getBlock(), player)) { - P.p.msg(player, P.p.languageReader.get("Player_BarrelCreated")); - } - } - } - - @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) - public void onSignChangeLow(SignChangeEvent event) { - if (Words.doSigns) { - if (BPlayer.hasPlayer(event.getPlayer())) { - Words.signWrite(event); - } - } - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onBlockBreak(BlockBreakEvent event) { - if (!P.p.blockDestroy(event.getBlock(), event.getPlayer())) { - event.setCancelled(true); - } - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onBlockBurn(BlockBurnEvent event) { - P.p.blockDestroy(event.getBlock(), null); - } - - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onPistonRetract(BlockPistonRetractEvent event) { - if (event.isSticky()) { - Block block = event.getRetractLocation().getBlock(); - - if (Barrel.get(block) != null) { - event.setCancelled(true); - } - } - } - - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onPistonExtend(BlockPistonExtendEvent event) { - for (Block block : event.getBlocks()) { - if (Barrel.get(block) != null) { - event.setCancelled(true); - return; - } - } - } -} +package com.dre.brewery.listeners; + +import com.dre.brewery.BPlayer; +import com.dre.brewery.utility.BUtil; +import com.dre.brewery.Barrel; +import com.dre.brewery.P; +import com.dre.brewery.DistortChat; +import com.dre.brewery.api.events.barrel.BarrelDestroyEvent; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockBurnEvent; +import org.bukkit.event.block.BlockPistonExtendEvent; +import org.bukkit.event.block.BlockPistonRetractEvent; +import org.bukkit.event.block.SignChangeEvent; + +public class BlockListener implements Listener { + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onSignChange(SignChangeEvent event) { + String[] lines = event.getLines(); + + if (lines[0].equalsIgnoreCase("Barrel") || lines[0].equalsIgnoreCase(P.p.languageReader.get("Etc_Barrel"))) { + Player player = event.getPlayer(); + if (!player.hasPermission("brewery.createbarrel.small") && !player.hasPermission("brewery.createbarrel.big")) { + P.p.msg(player, P.p.languageReader.get("Perms_NoBarrelCreate")); + return; + } + if (Barrel.create(event.getBlock(), player)) { + P.p.msg(player, P.p.languageReader.get("Player_BarrelCreated")); + } + } + } + + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onSignChangeLow(SignChangeEvent event) { + if (DistortChat.doSigns) { + if (BPlayer.hasPlayer(event.getPlayer())) { + DistortChat.signWrite(event); + } + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onBlockBreak(BlockBreakEvent event) { + if (!BUtil.blockDestroy(event.getBlock(), event.getPlayer(), BarrelDestroyEvent.Reason.PLAYER)) { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onBlockBurn(BlockBurnEvent event) { + if (!BUtil.blockDestroy(event.getBlock(), null, BarrelDestroyEvent.Reason.BURNED)) { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onPistonRetract(BlockPistonRetractEvent event) { + if (event.isSticky()) { + Block block = event.getRetractLocation().getBlock(); + + if (Barrel.get(block) != null) { + event.setCancelled(true); + } + } + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onPistonExtend(BlockPistonExtendEvent event) { + for (Block block : event.getBlocks()) { + if (Barrel.get(block) != null) { + event.setCancelled(true); + return; + } + } + } +} diff --git a/src/com/dre/brewery/listeners/CommandListener.java b/src/com/dre/brewery/listeners/CommandListener.java index 60a6ece..9a3c3c7 100644 --- a/src/com/dre/brewery/listeners/CommandListener.java +++ b/src/com/dre/brewery/listeners/CommandListener.java @@ -1,29 +1,28 @@ package com.dre.brewery.listeners; -import java.util.ArrayList; -import java.util.Locale; - -import com.dre.brewery.Util; +import com.dre.brewery.*; +import com.dre.brewery.api.events.brew.BrewModifyEvent; +import com.dre.brewery.filedata.BConfig; +import com.dre.brewery.recipe.BRecipe; +import com.dre.brewery.utility.BUtil; import org.bukkit.Material; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; -import com.dre.brewery.BIngredients; -import com.dre.brewery.BRecipe; -import com.dre.brewery.P; -import com.dre.brewery.Wakeup; -import com.dre.brewery.BPlayer; -import com.dre.brewery.Brew; +import java.util.ArrayList; +import java.util.Locale; public class CommandListener implements CommandExecutor { public P p = P.p; @Override - public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { String cmd = "help"; if (args.length > 0) { @@ -38,7 +37,6 @@ public class CommandListener implements CommandExecutor { if (sender.hasPermission("brewery.cmd.reload")) { p.reload(sender); - p.msg(sender, p.languageReader.get("CMD_Reload")); } else { p.msg(sender, p.languageReader.get("Error_NoPermissions")); } @@ -103,14 +101,6 @@ public class CommandListener implements CommandExecutor { p.msg(sender, p.languageReader.get("Error_NoPermissions")); } - } else if (cmd.equalsIgnoreCase("persist") || cmd.equalsIgnoreCase("persistent")) { - - if (sender.hasPermission("brewery.cmd.persist")) { - cmdPersist(sender); - } else { - p.msg(sender, p.languageReader.get("Error_NoPermissions")); - } - } else if (cmd.equalsIgnoreCase("static")) { if (sender.hasPermission("brewery.cmd.static")) { @@ -167,7 +157,7 @@ public class CommandListener implements CommandExecutor { p.msg(sender, "&6" + p.getDescription().getName() + " v" + p.getDescription().getVersion()); } - Util.list(sender, commands, page); + BUtil.list(sender, commands, page); } @@ -188,18 +178,19 @@ public class CommandListener implements CommandExecutor { cmds.add (p.languageReader.get("Help_UnLabel")); } - if (sender.hasPermission("brewery.cmd.copy")) { - cmds.add (p.languageReader.get("Help_Copy")); - } - - if (sender.hasPermission("brewery.cmd.delete")) { - cmds.add (p.languageReader.get("Help_Delete")); - } - if (sender.hasPermission("brewery.cmd.infoOther")) { cmds.add (p.languageReader.get("Help_InfoOther")); } + if (sender.hasPermission("brewery.cmd.create")) { + cmds.add(p.languageReader.get("Help_Create")); + } + + if (sender.hasPermission("brewery.cmd.reload")) { + cmds.add(p.languageReader.get("Help_Configname")); + cmds.add(p.languageReader.get("Help_Reload")); + } + if (sender.hasPermission("brewery.cmd.wakeup")) { cmds.add(p.languageReader.get("Help_Wakeup")); cmds.add(p.languageReader.get("Help_WakeupList")); @@ -209,21 +200,16 @@ public class CommandListener implements CommandExecutor { cmds.add(p.languageReader.get("Help_WakeupRemove")); } - if (sender.hasPermission("brewery.cmd.reload")) { - cmds.add(p.languageReader.get("Help_Configname")); - cmds.add(p.languageReader.get("Help_Reload")); - } - - if (sender.hasPermission("brewery.cmd.persist")) { - cmds.add(p.languageReader.get("Help_Persist")); - } - if (sender.hasPermission("brewery.cmd.static")) { cmds.add(p.languageReader.get("Help_Static")); } - if (sender.hasPermission("brewery.cmd.create")) { - cmds.add(p.languageReader.get("Help_Create")); + if (sender.hasPermission("brewery.cmd.copy")) { + cmds.add (p.languageReader.get("Help_Copy")); + } + + if (sender.hasPermission("brewery.cmd.delete")) { + cmds.add (p.languageReader.get("Help_Delete")); } return cmds; @@ -328,7 +314,7 @@ public class CommandListener implements CommandExecutor { if (player != null) { bPlayer.drinkCap(player); } else { - if (!BPlayer.overdrinkKick) { + if (!BConfig.overdrinkKick) { bPlayer.setData(100, 0); } } @@ -365,162 +351,138 @@ public class CommandListener implements CommandExecutor { } public void cmdItemName(CommandSender sender) { - if (sender instanceof Player) { - - Player player = (Player) sender; - ItemStack hand = P.use1_9 ? player.getInventory().getItemInMainHand() : player.getItemInHand(); - if (hand != null) { - p.msg(sender, p.languageReader.get("CMD_Configname", hand.getType().name().toLowerCase(Locale.ENGLISH))); - } else { - p.msg(sender, p.languageReader.get("CMD_Configname_Error")); - } - - } else { + if (!(sender instanceof Player)) { p.msg(sender, p.languageReader.get("Error_PlayerCommand")); + return; } + + Player player = (Player) sender; + ItemStack hand = P.use1_9 ? player.getInventory().getItemInMainHand() : player.getItemInHand(); + if (hand != null) { + p.msg(sender, p.languageReader.get("CMD_Configname", hand.getType().name().toLowerCase(Locale.ENGLISH))); + } else { + p.msg(sender, p.languageReader.get("CMD_Configname_Error")); + } + } - @SuppressWarnings("deprecation") + @Deprecated public void cmdCopy(CommandSender sender, int count) { - if (sender instanceof Player) { - if (count < 1 || count > 36) { - p.msg(sender, p.languageReader.get("Etc_Usage")); - p.msg(sender, p.languageReader.get("Help_Copy")); + if (!(sender instanceof Player)) { + p.msg(sender, p.languageReader.get("Error_PlayerCommand")); + return; + } + if (count < 1 || count > 36) { + p.msg(sender, p.languageReader.get("Etc_Usage")); + p.msg(sender, p.languageReader.get("Help_Copy")); + return; + } + Player player = (Player) sender; + ItemStack hand = player.getItemInHand(); + if (hand != null) { + if (Brew.isBrew(hand)) { + while (count > 0) { + ItemStack item = hand.clone(); + if (!(player.getInventory().addItem(item)).isEmpty()) { + p.msg(sender, p.languageReader.get("CMD_Copy_Error", "" + count)); + return; + } + count--; + } return; } - Player player = (Player) sender; - ItemStack hand = player.getItemInHand(); - if (hand != null) { - Brew brew = Brew.get(hand); - if (brew != null) { - while (count > 0) { - ItemStack item = brew.copy(hand); - if (!(player.getInventory().addItem(item)).isEmpty()) { - p.msg(sender, p.languageReader.get("CMD_Copy_Error", "" + count)); - return; - } - count--; - } - if (brew.isPersistent()) { - p.msg(sender, p.languageReader.get("CMD_CopyNotPersistent")); - } - return; - } - } - - p.msg(sender, p.languageReader.get("Error_ItemNotPotion")); - - } else { - p.msg(sender, p.languageReader.get("Error_PlayerCommand")); } + p.msg(sender, p.languageReader.get("Error_ItemNotPotion")); + } - @SuppressWarnings("deprecation") + @Deprecated public void cmdDelete(CommandSender sender) { - if (sender instanceof Player) { - Player player = (Player) sender; - ItemStack hand = player.getItemInHand(); - if (hand != null) { - Brew brew = Brew.get(hand); - if (brew != null) { - if (brew.isPersistent()) { - p.msg(sender, p.languageReader.get("CMD_PersistRemove")); - } else { - brew.remove(hand); - player.setItemInHand(new ItemStack(Material.AIR)); - } - return; - } - } - p.msg(sender, p.languageReader.get("Error_ItemNotPotion")); - } else { + if (!(sender instanceof Player)) { p.msg(sender, p.languageReader.get("Error_PlayerCommand")); + return; } - - } - - @SuppressWarnings("deprecation") - public void cmdPersist(CommandSender sender) { - - if (sender instanceof Player) { - Player player = (Player) sender; - ItemStack hand = player.getItemInHand(); - if (hand != null) { - Brew brew = Brew.get(hand); - if (brew != null) { - if (brew.isPersistent()) { - brew.removePersistence(); - brew.setStatic(false, hand); - p.msg(sender, p.languageReader.get("CMD_UnPersist")); - } else { - brew.makePersistent(); - brew.setStatic(true, hand); - p.msg(sender, p.languageReader.get("CMD_Persistent")); - } - brew.touch(); - return; - } + Player player = (Player) sender; + ItemStack hand = player.getItemInHand(); + if (hand != null) { + if (Brew.isBrew(hand)) { + player.setItemInHand(new ItemStack(Material.AIR)); + return; } - p.msg(sender, p.languageReader.get("Error_ItemNotPotion")); - } else { - p.msg(sender, p.languageReader.get("Error_PlayerCommand")); } + p.msg(sender, p.languageReader.get("Error_ItemNotPotion")); } @SuppressWarnings("deprecation") public void cmdStatic(CommandSender sender) { - if (sender instanceof Player) { - Player player = (Player) sender; - ItemStack hand = player.getItemInHand(); - if (hand != null) { - Brew brew = Brew.get(hand); - if (brew != null) { - if (brew.isStatic()) { - if (!brew.isPersistent()) { - brew.setStatic(false, hand); - p.msg(sender, p.languageReader.get("CMD_NonStatic")); - } else { - p.msg(sender, p.languageReader.get("Error_PersistStatic")); - } - } else { - brew.setStatic(true, hand); - p.msg(sender, p.languageReader.get("CMD_Static")); - } - brew.touch(); + if (!(sender instanceof Player)) { + p.msg(sender, p.languageReader.get("Error_PlayerCommand")); + return; + } + Player player = (Player) sender; + ItemStack hand = player.getItemInHand(); + if (hand != null) { + Brew brew = Brew.get(hand); + if (brew != null) { + if (brew.isStatic()) { + brew.setStatic(false, hand); + p.msg(sender, p.languageReader.get("CMD_NonStatic")); + } else { + brew.setStatic(true, hand); + p.msg(sender, p.languageReader.get("CMD_Static")); + } + brew.touch(); + ItemMeta meta = hand.getItemMeta(); + assert meta != null; + BrewModifyEvent modifyEvent = new BrewModifyEvent(brew, meta, BrewModifyEvent.Type.STATIC); + P.p.getServer().getPluginManager().callEvent(modifyEvent); + if (modifyEvent.isCancelled()) { return; } + brew.save(meta); + hand.setItemMeta(meta); + return; } - p.msg(sender, p.languageReader.get("Error_ItemNotPotion")); - } else { - p.msg(sender, p.languageReader.get("Error_PlayerCommand")); } + p.msg(sender, p.languageReader.get("Error_ItemNotPotion")); } @SuppressWarnings("deprecation") public void cmdUnlabel(CommandSender sender) { - if (sender instanceof Player) { - Player player = (Player) sender; - ItemStack hand = player.getItemInHand(); - if (hand != null) { - Brew brew = Brew.get(hand); - if (brew != null) { - brew.unLabel(hand); - brew.touch(); - p.msg(sender, p.languageReader.get("CMD_UnLabel")); + if (!(sender instanceof Player)) { + p.msg(sender, p.languageReader.get("Error_PlayerCommand")); + return; + } + Player player = (Player) sender; + ItemStack hand = player.getItemInHand(); + if (hand != null) { + Brew brew = Brew.get(hand); + if (brew != null) { + ItemMeta origMeta = hand.getItemMeta(); + brew.unLabel(hand); + brew.touch(); + ItemMeta meta = hand.getItemMeta(); + assert meta != null; + BrewModifyEvent modifyEvent = new BrewModifyEvent(brew, meta, BrewModifyEvent.Type.UNLABEL); + P.p.getServer().getPluginManager().callEvent(modifyEvent); + if (modifyEvent.isCancelled()) { + hand.setItemMeta(origMeta); return; } + brew.save(meta); + hand.setItemMeta(meta); + p.msg(sender, p.languageReader.get("CMD_UnLabel")); + return; } - p.msg(sender, p.languageReader.get("Error_ItemNotPotion")); - } else { - p.msg(sender, p.languageReader.get("Error_PlayerCommand")); } + p.msg(sender, p.languageReader.get("Error_ItemNotPotion")); } @@ -555,51 +517,56 @@ public class CommandListener implements CommandExecutor { player = p.getServer().getPlayer(pName); } - if (sender instanceof Player || player != null) { - if (player == null) { - player = ((Player) sender); - } - int stringLength = args.length - 1; - if (pName != null) { - stringLength--; - } - if (hasQuality) { - stringLength--; - } - - String name; - if (stringLength > 1) { - StringBuilder builder = new StringBuilder(args[1]); - - for (int i = 2; i < stringLength + 1; i++) { - builder.append(" ").append(args[i]); - } - name = builder.toString(); - } else { - name = args[1]; - } - - if (player.getInventory().firstEmpty() == -1) { - p.msg(sender, p.languageReader.get("CMD_Copy_Error", "1")); - return; - } - - BRecipe recipe = null; - for (BRecipe r : BIngredients.recipes) { - if (r.hasName(name)) { - recipe = r; - break; - } - } - if (recipe != null) { - player.getInventory().addItem(recipe.create(quality)); - } else { - p.msg(sender, p.languageReader.get("Error_NoBrewName", name)); - } - - } else { + if (!(sender instanceof Player) && player == null) { p.msg(sender, p.languageReader.get("Error_PlayerCommand")); + return; } + + if (player == null) { + player = ((Player) sender); + } + int stringLength = args.length - 1; + if (pName != null) { + stringLength--; + } + if (hasQuality) { + stringLength--; + } + + String name; + if (stringLength > 1) { + StringBuilder builder = new StringBuilder(args[1]); + + for (int i = 2; i < stringLength + 1; i++) { + builder.append(" ").append(args[i]); + } + name = builder.toString(); + } else { + name = args[1]; + } + + if (player.getInventory().firstEmpty() == -1) { + p.msg(sender, p.languageReader.get("CMD_Copy_Error", "1")); + return; + } + + BRecipe recipe = null; + for (BRecipe r : BRecipe.getAllRecipes()) { + if (r.hasName(name)) { + recipe = r; + break; + } + } + if (recipe != null) { + ItemStack item = recipe.create(quality); + if (item != null) { + player.getInventory().addItem(item); + p.msg(sender, p.languageReader.get("CMD_Created")); + } + } else { + p.msg(sender, p.languageReader.get("Error_NoBrewName", name)); + } + } } diff --git a/src/com/dre/brewery/listeners/EntityListener.java b/src/com/dre/brewery/listeners/EntityListener.java index 0947802..6fd00b9 100644 --- a/src/com/dre/brewery/listeners/EntityListener.java +++ b/src/com/dre/brewery/listeners/EntityListener.java @@ -1,51 +1,48 @@ package com.dre.brewery.listeners; -import java.util.ListIterator; - -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.entity.EntityChangeBlockEvent; -import org.bukkit.event.entity.EntityExplodeEvent; -import org.bukkit.event.entity.ItemDespawnEvent; -import org.bukkit.event.entity.EntityCombustEvent; -import org.bukkit.entity.Entity; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.Item; -import org.bukkit.inventory.ItemStack; - import com.dre.brewery.Barrel; import com.dre.brewery.Brew; import com.dre.brewery.P; -import com.dre.brewery.integration.LWCBarrel; +import com.dre.brewery.api.events.barrel.BarrelDestroyEvent; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Item; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityChangeBlockEvent; +import org.bukkit.event.entity.EntityCombustEvent; +import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.event.entity.ItemDespawnEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.List; +import java.util.ListIterator; public class EntityListener implements Listener { - // Remove the Potion from Brew when it despawns + // Legacy Brew removal @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onItemDespawn(ItemDespawnEvent event) { + if (Brew.noLegacy()) return; ItemStack item = event.getEntity().getItemStack(); if (item.getType() == Material.POTION) { - Brew brew = Brew.get(item); - if (brew != null) { - brew.remove(item); - } + Brew.removeLegacy(item); } } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onEntityCombust(EntityCombustEvent event) { + if (Brew.noLegacy()) return; Entity entity = event.getEntity(); if (entity.getType() == EntityType.DROPPED_ITEM) { if (entity instanceof Item) { ItemStack item = ((Item) entity).getItemStack(); if (item.getType() == Material.POTION) { - Brew brew = Brew.get(item); - if (brew != null) { - brew.remove(item); - } + Brew.removeLegacy(item); } } } @@ -56,31 +53,33 @@ public class EntityListener implements Listener { @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void onExplode(EntityExplodeEvent event) { ListIterator iter = event.blockList().listIterator(); - Barrel barrel = null; - boolean removedBarrel = false; - while (iter.hasNext()) { - Block block = iter.next(); - if (barrel == null || !barrel.hasBlock(block)) { - barrel = Barrel.get(block); - removedBarrel = false; - } - if (!removedBarrel) { - if (barrel != null) { - if (P.p.useLWC) { - try { - if (LWCBarrel.blockExplosion(barrel, block)) { - iter.remove(); - } else { - removedBarrel = true; - } - } catch (Exception e) { - P.p.errorLog("Failed to Check LWC on Barrel Explosion!"); - e.printStackTrace(); - removedBarrel = true; + if (!iter.hasNext()) return; + List breakEvents = new ArrayList<>(6); + Block block; + blocks: while (iter.hasNext()) { + block = iter.next(); + if (!breakEvents.isEmpty()) { + for (BarrelDestroyEvent breakEvent : breakEvents) { + if (breakEvent.getBarrel().hasBlock(block)) { + if (breakEvent.isCancelled()) { + iter.remove(); } + continue blocks; } } } + Barrel barrel = Barrel.get(block); + if (barrel != null) { + BarrelDestroyEvent breakEvent = new BarrelDestroyEvent(barrel, block, BarrelDestroyEvent.Reason.EXPLODED, null); + // Listened to by LWCBarrel (IntegrationListener) + P.p.getServer().getPluginManager().callEvent(breakEvent); + breakEvents.add(breakEvent); + if (breakEvent.isCancelled()) { + iter.remove(); + } else { + barrel.remove(block, null, true); + } + } } } diff --git a/src/com/dre/brewery/listeners/InventoryListener.java b/src/com/dre/brewery/listeners/InventoryListener.java index 7df7504..a27283f 100644 --- a/src/com/dre/brewery/listeners/InventoryListener.java +++ b/src/com/dre/brewery/listeners/InventoryListener.java @@ -1,63 +1,36 @@ package com.dre.brewery.listeners; -import com.dre.brewery.BPlayer; -import com.dre.brewery.BRecipe; +import com.dre.brewery.BDistiller; import com.dre.brewery.Barrel; import com.dre.brewery.Brew; import com.dre.brewery.MCBarrel; import com.dre.brewery.P; -import com.dre.brewery.integration.LogBlockBarrel; -import org.bukkit.Bukkit; +import com.dre.brewery.filedata.BConfig; +import com.dre.brewery.lore.BrewLore; import org.bukkit.Material; -import org.bukkit.Sound; -import org.bukkit.SoundCategory; -import org.bukkit.block.Block; -import org.bukkit.block.BlockState; -import org.bukkit.block.BrewingStand; import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; -import org.bukkit.event.inventory.BrewEvent; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryAction; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.inventory.InventoryCloseEvent; -import org.bukkit.event.inventory.InventoryDragEvent; -import org.bukkit.event.inventory.InventoryOpenEvent; -import org.bukkit.event.inventory.InventoryPickupItemEvent; -import org.bukkit.event.inventory.InventoryType; +import org.bukkit.event.inventory.*; import org.bukkit.inventory.BrewerInventory; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemFlag; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.PotionMeta; -import org.bukkit.scheduler.BukkitRunnable; -import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.UUID; -/** - * Updated for 1.9 to replicate the "Brewing" process for distilling. - * Because of how metadata has changed, the brewer no longer triggers as previously described. - * So, I've added some event tracking and manual forcing of the brewing "animation" if the - * set of ingredients in the brewer can be distilled. - * Nothing here should interfere with vanilla brewing. - * - * @author ProgrammerDan (1.9 distillation update only) - */ public class InventoryListener implements Listener { /* === Recreating manually the prior BrewEvent behavior. === */ private HashSet trackedBrewmen = new HashSet<>(); - private HashMap trackedBrewers = new HashMap<>(); - private static final int DISTILLTIME = 400; /** * Start tracking distillation for a person when they open the brewer window. - * @param event */ @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void onBrewerOpen(InventoryOpenEvent event) { @@ -72,7 +45,6 @@ public class InventoryListener implements Listener { /** * Stop tracking distillation for a person when they close the brewer window. - * @param event */ @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void onBrewerClose(InventoryCloseEvent event) { @@ -96,14 +68,13 @@ public class InventoryListener implements Listener { /** * Clicking can either start or stop the new brew distillation tracking. - * Note that server restart will halt any ongoing brewing processes and + *

Note that server restart will halt any ongoing brewing processes and * they will _not_ restart until a new click event. - * - * @param event the Click event. */ @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void onBrewerClick(InventoryClickEvent event) { if (!P.use1_9) return; + HumanEntity player = event.getWhoClicked(); Inventory inv = event.getInventory(); if (player == null || !(inv instanceof BrewerInventory)) return; @@ -114,171 +85,22 @@ public class InventoryListener implements Listener { if (InventoryType.BREWING != inv.getType()) return; if (event.getAction() == InventoryAction.NOTHING) return; // Ignore clicks that do nothing - BrewerInventory brewer = (BrewerInventory) inv; - final Block brewery = brewer.getHolder().getBlock(); - - // If we were already tracking the brewer, cancel any ongoing event due to the click. - Integer curTask = trackedBrewers.get(brewery); - if (curTask != null) { - Bukkit.getScheduler().cancelTask(curTask); // cancel prior - brewer.getHolder().setBrewingTime(0); // Fixes brewing continuing without fuel for normal potions - brewer.getHolder().update(); - } - final int fuel = brewer.getHolder().getFuelLevel(); - - // Now check if we should bother to track it. - trackedBrewers.put(brewery, new BukkitRunnable() { - private int runTime = -1; - private int brewTime = -1; - @Override - public void run() { - BlockState now = brewery.getState(); - if (now instanceof BrewingStand) { - BrewingStand stand = (BrewingStand) now; - if (brewTime == -1) { // only check at the beginning (and end) for distillables - switch (hasCustom(stand.getInventory())) { - case 1: - // Custom potion but not for distilling. Stop any brewing and cancel this task - if (stand.getBrewingTime() > 0) { - if (P.use1_11) { - // The trick below doesnt work in 1.11, but we dont need it anymore - // This should only happen with older Brews that have been made with the old Potion Color System - stand.setBrewingTime(Short.MAX_VALUE); - } else { - // Brewing time is sent and stored as short - // This sends a negative short value to the Client - // In the client the Brewer will look like it is not doing anything - stand.setBrewingTime(Short.MAX_VALUE << 1); - } - stand.setFuelLevel(fuel); - stand.update(); - } - case 0: - // No custom potion, cancel and ignore - this.cancel(); - trackedBrewers.remove(brewery); - P.p.debugLog("nothing to distill"); - return; - default: - runTime = getLongestDistillTime(stand.getInventory()); - brewTime = runTime; - P.p.debugLog("using brewtime: " + runTime); - - } - } - - brewTime--; // count down. - stand.setBrewingTime((int) ((float) brewTime / ((float) runTime / (float) DISTILLTIME)) + 1); - - if (brewTime <= 1) { // Done! - stand.setBrewingTime(0); - stand.update(); - BrewerInventory brewer = stand.getInventory(); - if (!runDistill(brewer)) { - this.cancel(); - trackedBrewers.remove(brewery); - P.p.debugLog("All done distilling"); - } else { - brewTime = -1; // go again. - P.p.debugLog("Can distill more! Continuing."); - } - } else { - stand.update(); - } - } else { - this.cancel(); - trackedBrewers.remove(brewery); - P.p.debugLog("The block was replaced; not a brewing stand."); - } - } - }.runTaskTimer(P.p, 2L, 1L).getTaskId()); - } - - // Returns a Brew or null for every Slot in the BrewerInventory - private Brew[] getDistillContents(BrewerInventory inv) { - ItemStack item; - Brew[] contents = new Brew[3]; - for (int slot = 0; slot < 3; slot++) { - item = inv.getItem(slot); - if (item != null) { - contents[slot] = Brew.get(item); - } - } - return contents; - } - - private byte hasCustom(BrewerInventory brewer) { - ItemStack item = brewer.getItem(3); // ingredient - boolean glowstone = (item != null && Material.GLOWSTONE_DUST == item.getType()); // need dust in the top slot. - byte customFound = 0; - for (Brew brew : getDistillContents(brewer)) { - if (brew != null) { - if (!glowstone) { - return 1; - } - if (brew.canDistill()) { - return 2; - } else { - customFound = 1; - } - } - } - return customFound; + BDistiller.distillerClick(event); } @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) public void onBrew(BrewEvent event) { if (P.use1_9) { - if (hasCustom(event.getContents()) != 0) { + if (BDistiller.hasBrew(event.getContents(), BDistiller.getDistillContents(event.getContents())) != 0) { event.setCancelled(true); } return; } - if (runDistill(event.getContents())) { + if (BDistiller.runDistill(event.getContents(), BDistiller.getDistillContents(event.getContents()))) { event.setCancelled(true); } } - private boolean runDistill(BrewerInventory inv) { - boolean custom = false; - Brew[] contents = getDistillContents(inv); - for (int slot = 0; slot < 3; slot++) { - if (contents[slot] == null) continue; - if (contents[slot].canDistill()) { - // is further distillable - custom = true; - } else { - contents[slot] = null; - } - } - if (custom) { - Brew.distillAll(inv, contents); - return true; - } - return false; - } - - private int getLongestDistillTime(BrewerInventory inv) { - int bestTime = 0; - int time; - Brew[] contents = getDistillContents(inv); - for (int slot = 0; slot < 3; slot++) { - if (contents[slot] == null) continue; - time = contents[slot].getDistillTimeNextRun(); - if (time == 0) { - // Undefined Potion needs 40 seconds - time = 800; - } - if (time > bestTime) { - bestTime = time; - } - } - if (bestTime > 0) { - return bestTime; - } - return 800; - } - // Clicked a Brew somewhere, do some updating @EventHandler(priority = EventPriority.LOW, ignoreCancelled = false) public void onInventoryClickLow(InventoryClickEvent event) { @@ -286,18 +108,33 @@ public class InventoryListener implements Listener { ItemStack item = event.getCurrentItem(); if (item.hasItemMeta()) { PotionMeta potion = ((PotionMeta) item.getItemMeta()); - Brew brew = Brew.get(potion); - if (brew != null) { - // convert potions from 1.8 to 1.9 for color and to remove effect descriptions - if (P.use1_9 && !potion.hasItemFlag(ItemFlag.HIDE_POTION_EFFECTS)) { - BRecipe recipe = brew.getCurrentRecipe(); - if (recipe != null) { - Brew.removeEffects(potion); - Brew.PotionColor.fromString(recipe.getColor()).colorBrew(potion, item, brew.canDistill()); - item.setItemMeta(potion); + assert potion != null; + if (P.use1_11) { + // Convert potions from 1.10 to 1.11 for new color + if (potion.getColor() == null) { + Brew brew = Brew.get(potion); + if (brew != null) { + brew.convertPre1_11(item); } } - brew.touch(); + } else { + // convert potions from 1.8 to 1.9 for color and to remove effect descriptions + if (P.use1_9 && !potion.hasItemFlag(ItemFlag.HIDE_POTION_EFFECTS)) { + Brew brew = Brew.get(potion); + if (brew != null) { + brew.convertPre1_9(item); + } + } + } + //long t1 = System.nanoTime(); + Brew brew = Brew.get(item); + //long t2 = System.nanoTime(); + if (brew != null) { + P.p.log(brew.toString()); + + //P.p.log("Brew.get(): " + (t2 - t1) / 1000000.0 + "ms"); + + //brew.touch(); } } } @@ -315,15 +152,28 @@ public class InventoryListener implements Listener { } ItemStack item = event.getCurrentItem(); - if (item != null) { - if (item.getType() == Material.POTION) { - if (item.hasItemMeta()) { - PotionMeta meta = (PotionMeta) item.getItemMeta(); - Brew brew = Brew.get(meta); - if (brew != null) { - if (Brew.hasColorLore(meta)) { - brew.convertLore(meta, false); - item.setItemMeta(meta); + if (item != null && item.getType() == Material.POTION && item.hasItemMeta()) { + PotionMeta meta = (PotionMeta) item.getItemMeta(); + assert meta != null; + Brew brew = Brew.get(meta); + if (brew != null) { + BrewLore lore = null; + if (BrewLore.hasColorLore(meta)) { + lore = new BrewLore(brew, meta); + lore.convertLore(false); + } else if (!BConfig.alwaysShowAlc && event.getInventory().getType() == InventoryType.BREWING) { + lore = new BrewLore(brew, meta); + lore.updateAlc(false); + } + if (lore != null) { + lore.write(); + item.setItemMeta(meta); + if (event.getWhoClicked() instanceof Player) { + switch (event.getAction()) { + case MOVE_TO_OTHER_INVENTORY: + case HOTBAR_SWAP: + // Fix a Graphical glitch of item still showing colors until clicking it + P.p.getServer().getScheduler().runTask(P.p, () -> ((Player) event.getWhoClicked()).updateInventory()); } } } @@ -332,7 +182,7 @@ public class InventoryListener implements Listener { } // Check if the player tries to add more than the allowed amount of brews into an mc-barrel - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void onInventoryClickMCBarrel(InventoryClickEvent event) { if (!P.use1_14) return; if (event.getInventory().getType() != InventoryType.BARREL) return; @@ -351,6 +201,27 @@ public class InventoryListener implements Listener { //public static boolean opening = false; + @SuppressWarnings("deprecation") + @EventHandler(ignoreCancelled = false) + public void onInventoryOpenLegacyConvert(InventoryOpenEvent event) { + if (Brew.noLegacy()) { + return; + } + if (event.getInventory().getType() == InventoryType.PLAYER) { + return; + } + for (ItemStack item : event.getInventory().getContents()) { + if (item != null && item.getType() == Material.POTION) { + int uid = Brew.getUID(item); + // Check if the uid exists first, otherwise it will log that it can't find the id + if (uid < 0 && Brew.legacyPotions.containsKey(uid)) { + // This will convert the Brew + Brew.get(item); + } + } + } + } + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onInventoryOpen(InventoryOpenEvent event) { if (!P.use1_14) return; @@ -383,38 +254,53 @@ public class InventoryListener implements Listener { // block the pickup of items where getPickupDelay is > 1000 (puke) @EventHandler(ignoreCancelled = true) - public void onInventoryPickupItem(InventoryPickupItemEvent event){ - if (event.getItem().getPickupDelay() > 1000 && event.getItem().getItemStack().getType() == BPlayer.pukeItem) { + public void onHopperPickupPuke(InventoryPickupItemEvent event){ + if (event.getItem().getPickupDelay() > 1000 && event.getItem().getItemStack().getType() == BConfig.pukeItem) { event.setCancelled(true); } } + // Block taking out items from running distillers, + // Convert Color Lore from MC Barrels back into normal color on taking out + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH) + public void onHopperMove(InventoryMoveItemEvent event){ + if (event.getSource() instanceof BrewerInventory) { + if (BDistiller.isTrackingDistiller(((BrewerInventory) event.getSource()).getHolder().getBlock())) { + event.setCancelled(true); + } + return; + } + + if (!P.use1_14) return; + + if (event.getSource().getType() == InventoryType.BARREL) { + ItemStack item = event.getItem(); + if (item.getType() == Material.POTION && Brew.isBrew(item)) { + PotionMeta meta = (PotionMeta) item.getItemMeta(); + assert meta != null; + if (BrewLore.hasColorLore(meta)) { + // has color lore, convert lore back to normal + Brew brew = Brew.get(meta); + if (brew != null) { + BrewLore lore = new BrewLore(brew, meta); + lore.convertLore(false); + lore.write(); + item.setItemMeta(meta); + event.setItem(item); + } + } + } + } + } + @EventHandler public void onInventoryClose(InventoryCloseEvent event) { - if (P.p.useLB) { - if (event.getInventory().getHolder() instanceof Barrel) { - try { - LogBlockBarrel.closeBarrel(event.getPlayer(), event.getInventory()); - } catch (Exception e) { - P.p.errorLog("Failed to Log Barrel to LogBlock!"); - P.p.errorLog("Brewery was tested with version 1.94 of LogBlock!"); - e.printStackTrace(); - } - } - } - if (!P.use1_14) return; // Barrel Closing Sound if (event.getInventory().getHolder() instanceof Barrel) { Barrel barrel = ((Barrel) event.getInventory().getHolder()); - float randPitch = (float) (Math.random() * 0.1); - if (barrel.isLarge()) { - barrel.getSpigot().getWorld().playSound(barrel.getSpigot().getLocation(), Sound.BLOCK_BARREL_CLOSE, SoundCategory.BLOCKS, 0.5f, 0.5f + randPitch); - barrel.getSpigot().getWorld().playSound(barrel.getSpigot().getLocation(), Sound.ITEM_BUCKET_EMPTY, SoundCategory.BLOCKS, 0.2f, 0.6f + randPitch); - } else { - barrel.getSpigot().getWorld().playSound(barrel.getSpigot().getLocation(), Sound.BLOCK_BARREL_CLOSE, SoundCategory.BLOCKS, 0.5f, 0.8f + randPitch); - } + barrel.playClosingSound(); } // Check for MC Barrel diff --git a/src/com/dre/brewery/listeners/PlayerListener.java b/src/com/dre/brewery/listeners/PlayerListener.java index e902122..ab1d99c 100644 --- a/src/com/dre/brewery/listeners/PlayerListener.java +++ b/src/com/dre/brewery/listeners/PlayerListener.java @@ -1,13 +1,12 @@ package com.dre.brewery.listeners; import com.dre.brewery.*; +import com.dre.brewery.filedata.BConfig; import com.dre.brewery.filedata.UpdateChecker; +import com.dre.brewery.utility.LegacyUtil; import org.bukkit.GameMode; import org.bukkit.Material; -import org.bukkit.Sound; -import org.bukkit.SoundCategory; import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -18,219 +17,93 @@ import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; - public class PlayerListener implements Listener { - public static boolean openEverywhere; - private static Set interacted = new HashSet<>(); @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void onPlayerInteract(PlayerInteractEvent event) { Block clickedBlock = event.getClickedBlock(); + if (clickedBlock == null) return; - if (clickedBlock != null) { - if (event.getAction() == Action.RIGHT_CLICK_BLOCK) { - Player player = event.getPlayer(); - if (!player.isSneaking()) { - Material type = clickedBlock.getType(); + if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return; - // Interacting with a Cauldron - if (type == Material.CAULDRON) { - Material materialInHand = event.getMaterial(); - ItemStack item = event.getItem(); + Player player = event.getPlayer(); + if (player.isSneaking()) return; - if (materialInHand == null || materialInHand == Material.BUCKET) { - return; + Material type = clickedBlock.getType(); - } else if (materialInHand == LegacyUtil.CLOCK) { - BCauldron.printTime(player, clickedBlock); - return; + // Interacting with a Cauldron + if (type == Material.CAULDRON) { + // Handle the Cauldron Interact + // The Event might get cancelled in here + BCauldron.clickCauldron(event); + return; + } - // fill a glass bottle with potion - } else if (materialInHand == Material.GLASS_BOTTLE) { - if (player.getInventory().firstEmpty() != -1 || item.getAmount() == 1) { - if (BCauldron.fill(player, clickedBlock)) { - event.setCancelled(true); - if (player.hasPermission("brewery.cauldron.fill")) { - if (item.getAmount() > 1) { - item.setAmount(item.getAmount() - 1); - } else { - setItemInHand(event, Material.AIR, false); - } - } - } - } else { - event.setCancelled(true); - } - return; + // Do not process Off Hand for Barrel interaction + if (P.use1_9 && event.getHand() != EquipmentSlot.HAND) { + return; + } - // reset cauldron when refilling to prevent unlimited source of potions - } else if (materialInHand == Material.WATER_BUCKET) { - if (!P.use1_9) { - // We catch >=1.9 cases in the Cauldron Listener - if (LegacyUtil.getFillLevel(clickedBlock) == 1) { - // will only remove when existing - BCauldron.remove(clickedBlock); - } - } - return; - } - - // Check if fire alive below cauldron when adding ingredients - Block down = clickedBlock.getRelative(BlockFace.DOWN); - if (LegacyUtil.isFireForCauldron(down)) { - - event.setCancelled(true); - boolean handSwap = false; - - // Interact event is called twice!!!?? in 1.9, once for each hand. - // Certain Items in Hand cause one of them to be cancelled or not called at all sometimes. - // We mark if a player had the event for the main hand - // If not, we handle the main hand in the event for the off hand - if (P.use1_9) { - if (event.getHand() == EquipmentSlot.HAND) { - final UUID id = player.getUniqueId(); - interacted.add(id); - P.p.getServer().getScheduler().runTask(P.p, new Runnable() { - @Override - public void run() { - interacted.remove(id); - } - }); - } else if (event.getHand() == EquipmentSlot.OFF_HAND) { - if (!interacted.remove(player.getUniqueId())) { - item = player.getInventory().getItemInMainHand(); - if (item != null && item.getType() != Material.AIR) { - materialInHand = item.getType(); - handSwap = true; - } else { - item = event.getItem(); - } - } - } - } - if (item == null) return; - - // add ingredient to cauldron that meet the previous conditions - if (BIngredients.possibleIngredients.contains(materialInHand)) { - - if (player.hasPermission("brewery.cauldron.insert")) { - if (BCauldron.ingredientAdd(clickedBlock, item)) { - boolean isBucket = item.getType().equals(Material.WATER_BUCKET) - || item.getType().equals(Material.LAVA_BUCKET) - || item.getType().equals(Material.MILK_BUCKET); - if (item.getAmount() > 1) { - item.setAmount(item.getAmount() - 1); - - if (isBucket) { - BCauldron.giveItem(player, new ItemStack(Material.BUCKET)); - } - } else { - if (isBucket) { - setItemInHand(event, Material.BUCKET, handSwap); - } else { - setItemInHand(event, Material.AIR, handSwap); - } - } - } - } else { - P.p.msg(player, P.p.languageReader.get("Perms_NoCauldronInsert")); - } - } - } - return; - } - - if (P.use1_9 && event.getHand() != EquipmentSlot.HAND) { - return; - } - - // Access a Barrel - Barrel barrel = null; - if (LegacyUtil.isWoodPlanks(type)) { - if (openEverywhere) { - barrel = Barrel.get(clickedBlock); - } - } else if (LegacyUtil.isWoodStairs(type)) { - for (Barrel barrel2 : Barrel.barrels) { - if (barrel2.hasStairsBlock(clickedBlock)) { - if (openEverywhere || !barrel2.isLarge()) { - barrel = barrel2; - } - break; - } - } - } else if (LegacyUtil.isFence(type) || LegacyUtil.isSign(type)) { - barrel = Barrel.getBySpigot(clickedBlock); - } - - if (barrel != null) { - event.setCancelled(true); - - if (!barrel.hasPermsOpen(player, event)) { - return; - } - - barrel.open(player); - - if (P.use1_14) { - - // When right clicking a normal Block in 1.14 with a potion or any edible item in hand, - // even when cancelled the consume animation will continue playing while opening the Barrel inventory. - // The Animation and sound will play endlessly while the inventory is open, though no item is consumed. - // This seems to be a client bug. - // This workaround switches the currently selected slot to another for a short time, it needs to be a slot with a different item in it. - // This seems to make the client stop animating a consumption - // If there is a better way to do this please let me know - Material hand = event.getMaterial(); - if ((hand == Material.POTION || hand.isEdible()) && !LegacyUtil.isSign(type)) { - PlayerInventory inv = player.getInventory(); - final int held = inv.getHeldItemSlot(); - int useSlot = -1; - for (int i = 0; i < 9; i++) { - ItemStack item = inv.getItem(i); - if (item == null || item.getType() == Material.AIR) { - useSlot = i; - break; - } else if (useSlot == -1 && item.getType() != hand) { - useSlot = i; - } - } - if (useSlot != -1) { - inv.setHeldItemSlot(useSlot); - P.p.getServer().getScheduler().scheduleSyncDelayedTask(P.p, () -> player.getInventory().setHeldItemSlot(held), 2); - } - } - - // Barrel opening Sound - float randPitch = (float) (Math.random() * 0.1); - if (barrel.isLarge()) { - barrel.getSpigot().getWorld().playSound(barrel.getSpigot().getLocation(), Sound.BLOCK_CHEST_OPEN, SoundCategory.BLOCKS, 0.4f, 0.55f + randPitch); - //barrel.getSpigot().getWorld().playSound(barrel.getSpigot().getLocation(), Sound.ITEM_BUCKET_EMPTY, SoundCategory.BLOCKS, 0.5f, 0.6f + randPitch); - barrel.getSpigot().getWorld().playSound(barrel.getSpigot().getLocation(), Sound.BLOCK_BREWING_STAND_BREW, SoundCategory.BLOCKS, 0.4f, 0.45f + randPitch); - } else { - barrel.getSpigot().getWorld().playSound(barrel.getSpigot().getLocation(), Sound.BLOCK_BARREL_OPEN, SoundCategory.BLOCKS, 0.5f, 0.8f + randPitch); - } - } - } + // Access a Barrel + Barrel barrel = null; + if (LegacyUtil.isWoodPlanks(type)) { + if (BConfig.openEverywhere) { + barrel = Barrel.getByWood(clickedBlock); + } + } else if (LegacyUtil.isWoodStairs(type)) { + barrel = Barrel.getByWood(clickedBlock); + if (barrel != null) { + if (!BConfig.openEverywhere && barrel.isLarge()) { + barrel = null; } } + } else if (LegacyUtil.isFence(type) || LegacyUtil.isSign(type)) { + barrel = Barrel.getBySpigot(clickedBlock); } - } - @SuppressWarnings("deprecation") - public void setItemInHand(PlayerInteractEvent event, Material mat, boolean swapped) { - if (P.use1_9) { - if ((event.getHand() == EquipmentSlot.OFF_HAND) != swapped) { - event.getPlayer().getInventory().setItemInOffHand(new ItemStack(mat)); - } else { - event.getPlayer().getInventory().setItemInMainHand(new ItemStack(mat)); + if (barrel != null) { + event.setCancelled(true); + + P.p.debugLog("Barrel has area of: " + barrel.getBody().getBounds().area()); + + if (!barrel.hasPermsOpen(player, event)) { + return; + } + + barrel.open(player); + + if (P.use1_14) { + + // When right clicking a normal Block in 1.14 with a potion or any edible item in hand, + // even when cancelled, the consume animation will continue playing while opening the Barrel inventory. + // The Animation and sound will play endlessly while the inventory is open, though no item is consumed. + // This seems to be a client bug. + // This workaround switches the currently selected slot to another for a short time, it needs to be a slot with a different item in it. + // This seems to make the client stop animating a consumption + // If there is a better way to do this please let me know + Material hand = event.getMaterial(); + if ((hand == Material.POTION || hand.isEdible()) && !LegacyUtil.isSign(type)) { + PlayerInventory inv = player.getInventory(); + final int held = inv.getHeldItemSlot(); + int useSlot = -1; + for (int i = 0; i < 9; i++) { + ItemStack item = inv.getItem(i); + if (item == null || item.getType() == Material.AIR) { + useSlot = i; + break; + } else if (useSlot == -1 && item.getType() != hand) { + useSlot = i; + } + } + if (useSlot != -1) { + inv.setHeldItemSlot(useSlot); + P.p.getServer().getScheduler().scheduleSyncDelayedTask(P.p, () -> player.getInventory().setHeldItemSlot(held), 2); + } + } + + barrel.playOpeningSound(); } - } else { - event.getPlayer().setItemInHand(new ItemStack(mat)); } } @@ -256,10 +129,13 @@ public class PlayerListener implements Listener { if (item.getType() == Material.POTION) { Brew brew = Brew.get(item); if (brew != null) { - BPlayer.drink(brew, player); - if (player.getGameMode() != GameMode.CREATIVE) { - brew.remove(item); + if (!BPlayer.drink(brew, item.getItemMeta(), player)) { + event.setCancelled(true); + return; } + /*if (player.getGameMode() != org.bukkit.GameMode.CREATIVE) { + brew.remove(item); + }*/ if (P.use1_9) { if (player.getGameMode() != GameMode.CREATIVE) { // replace the potion with an empty potion to avoid effects @@ -270,7 +146,7 @@ public class PlayerListener implements Listener { } } } - } else if (BPlayer.drainItems.containsKey(item.getType())) { + } else if (BConfig.drainItems.containsKey(item.getType())) { BPlayer bplayer = BPlayer.get(player); if (bplayer != null) { bplayer.drainByItem(player, item.getType()); @@ -303,13 +179,13 @@ public class PlayerListener implements Listener { // player talks while drunk, but he cant speak very well @EventHandler(priority = EventPriority.LOWEST) public void onPlayerChat(AsyncPlayerChatEvent event) { - Words.playerChat(event); + DistortChat.playerChat(event); } // player commands while drunk, distort chat commands @EventHandler(priority = EventPriority.LOWEST) public void onCommandPreProcess(PlayerCommandPreprocessEvent event) { - Words.playerCommand(event); + DistortChat.playerCommand(event); } // player joins while passed out diff --git a/src/com/dre/brewery/listeners/WorldListener.java b/src/com/dre/brewery/listeners/WorldListener.java index fde093c..b19b03c 100644 --- a/src/com/dre/brewery/listeners/WorldListener.java +++ b/src/com/dre/brewery/listeners/WorldListener.java @@ -2,8 +2,8 @@ package com.dre.brewery.listeners; import com.dre.brewery.BCauldron; import com.dre.brewery.Barrel; -import com.dre.brewery.P; -import com.dre.brewery.Util; +import com.dre.brewery.utility.BUtil; +import com.dre.brewery.filedata.BData; import com.dre.brewery.filedata.DataSave; import org.bukkit.World; import org.bukkit.event.EventHandler; @@ -19,9 +19,9 @@ public class WorldListener implements Listener { World world = event.getWorld(); if (world.getName().startsWith("DXL_")) { - P.p.loadWorldData(Util.getDxlName(world.getName()), world); + BData.loadWorldData(BUtil.getDxlName(world.getName()), world, null); } else { - P.p.loadWorldData(world.getUID().toString(), world); + BData.loadWorldData(world.getUID().toString(), world, null); } } diff --git a/src/com/dre/brewery/lore/Base91DecoderStream.java b/src/com/dre/brewery/lore/Base91DecoderStream.java new file mode 100644 index 0000000..7dbbb1f --- /dev/null +++ b/src/com/dre/brewery/lore/Base91DecoderStream.java @@ -0,0 +1,147 @@ +package com.dre.brewery.lore; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class Base91DecoderStream extends FilterInputStream { + + private final basE91 decoder = new basE91(); + private byte[] decbuf = new byte[32]; + private byte[] buf = new byte[32]; + private int reader = 0; + private int count = 0; + private byte[] markBuf = null; + + public Base91DecoderStream(InputStream in) { + super(in); + } + + private void decode() throws IOException { + reader = 0; + count = in.read(decbuf); + if (count < 1) { + count = decoder.decEnd(buf); + if (count < 1) { + count = -1; + } + return; + } + count = decoder.decode(decbuf, count, buf); + } + + @Override + public int read() throws IOException { + if (count == -1) return -1; + if (count == 0 || reader == count) { + decode(); + return read(); + } + return buf[reader++] & 0xFF; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + if (b == null) throw new NullPointerException(); + if (off < 0 || len < 0 || len > b.length - off) throw new IndexOutOfBoundsException(); + if (len == 0) return 0; + + if (count == -1) return -1; + if (count == 0 || reader == count) { + decode(); + if (count == -1) return -1; + } + + if (count > 0 && count - reader >= len) { + // enough data in buffer, copy it out directly + System.arraycopy(buf, reader, b, off, len); + reader += len; + return len; + } + + int out = 0; + int writeSize; + while (count > 0) { + // Not enough data in buffer, write all out, decode and repeat + writeSize = Math.min(len, count - reader); + System.arraycopy(buf, reader, b, off + out, writeSize); + out += writeSize; + len -= writeSize; + if (len > 0) { + decode(); + } else { + reader += writeSize; + break; + } + } + return out; + } + + @Override + public long skip(long n) throws IOException { + if (count == -1) return 0; + if (count > 0 && count - reader >= n) { + reader += n; + return n; + } + long skipped = count - reader; + decode(); + + while (count > 0) { + if (count > n - skipped) { + reader = (int) (n - skipped); + return n; + } + skipped += count; + decode(); + } + return skipped; + } + + @Override + public int available() throws IOException { + if (count == -1) return 0; + return (int) (in.available() * 0.813F) + count - reader; // Ratio encoded to decoded with random data + } + + @Override + public void close() throws IOException { + in.close(); + count = -1; + decoder.decReset(); + buf = null; + decbuf = null; + } + + @Override + public synchronized void mark(int readlimit) { + if (!markSupported()) return; + if (count == -1) return; + in.mark(readlimit); + decoder.decMark(); + if (count > 0 && reader < count) { + markBuf = new byte[count - reader]; + System.arraycopy(buf, reader, markBuf, 0, markBuf.length); + } else { + markBuf = null; + } + } + + @Override + public synchronized void reset() throws IOException { + if (!markSupported()) throw new IOException("mark and reset not supported by underlying Stream"); + in.reset(); + decoder.decUnmark(); + reader = 0; + count = 0; + if (markBuf != null) { + System.arraycopy(markBuf, 0, buf, 0, markBuf.length); + count = markBuf.length; + } + } + + @Override + public boolean markSupported() { + return in.markSupported(); + } +} diff --git a/src/com/dre/brewery/lore/Base91EncoderStream.java b/src/com/dre/brewery/lore/Base91EncoderStream.java new file mode 100644 index 0000000..0de1354 --- /dev/null +++ b/src/com/dre/brewery/lore/Base91EncoderStream.java @@ -0,0 +1,92 @@ +package com.dre.brewery.lore; + +import java.io.ByteArrayInputStream; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +public class Base91EncoderStream extends FilterOutputStream { + + private final basE91 encoder = new basE91(); + private byte[] buf = new byte[32]; + private byte[] encBuf = new byte[48]; + private int writer = 0; + private int encoded = 0; + + public Base91EncoderStream(OutputStream out) { + super(out); + } + + private void encFlush() throws IOException { + encoded = encoder.encode(buf, writer, encBuf); + out.write(encBuf, 0, encoded); + writer = 0; + } + + @Override + public void write(int b) throws IOException { + buf[writer++] = (byte) b; + if (writer >= buf.length) { + encFlush(); + } + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + if (len == 0) return; + if (b == null) throw new NullPointerException(); + if (len < 0 || off < 0 || (off + len) > b.length || off > b.length || (off + len) < 0) { + throw new IndexOutOfBoundsException(); + } + + if (buf.length - writer >= len) { + // Enough space in the buffer, copy it in + System.arraycopy(b, off, buf, writer, len); + writer += len; + if (writer >= buf.length) { + encFlush(); + } + return; + } + + if (off == 0 && buf.length >= len) { + // Buffer is too full but it would fit, so flush and encode data directly + encFlush(); + encoded = encoder.encode(b, len, encBuf); + out.write(encBuf, 0, encoded); + return; + } + + // More data than space in the Buffer + ByteArrayInputStream in = new ByteArrayInputStream(b, off, len); + while (true) { + writer += in.read(buf, writer, buf.length - writer); + if (writer >= buf.length) { + encFlush(); + } else { + break; + } + } + } + + @Override + public void flush() throws IOException { + if (writer > 0) { + encFlush(); + } + + encoded = encoder.encEnd(encBuf); + if (encoded > 0) { + out.write(encBuf, 0, encoded); + } + super.flush(); + } + + @Override + public void close() throws IOException { + super.close(); + encoder.encReset(); + buf = null; + encBuf = null; + } +} diff --git a/src/com/dre/brewery/lore/BrewLore.java b/src/com/dre/brewery/lore/BrewLore.java new file mode 100644 index 0000000..c81fa94 --- /dev/null +++ b/src/com/dre/brewery/lore/BrewLore.java @@ -0,0 +1,549 @@ +package com.dre.brewery.lore; + +import com.dre.brewery.recipe.BEffect; +import com.dre.brewery.BIngredients; +import com.dre.brewery.recipe.BRecipe; +import com.dre.brewery.Brew; +import com.dre.brewery.P; +import com.dre.brewery.filedata.BConfig; +import com.dre.brewery.utility.BUtil; +import org.bukkit.inventory.meta.PotionMeta; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents the Lore on a Brew under Modification. + *

Can efficiently replace certain lines of lore, to update brew information on an item. + */ +public class BrewLore { + private Brew brew; + private PotionMeta meta; + private List lore; + private boolean lineAddedOrRem = false; + + public BrewLore(Brew brew, PotionMeta meta) { + this.brew = brew; + this.meta = meta; + if (meta.hasLore()) { + lore = meta.getLore(); + } else { + lore = new ArrayList<>(); + } + } + + /** + * Write the new lore into the Meta. + *

Should be called at the end of operation on this Brew Lore + */ + public PotionMeta write() { + if (lineAddedOrRem) { + updateSpacer(); + } + meta.setLore(lore); + return meta; + } + + /** + * adds or removes an empty line in lore to space out the text a bit + */ + public void updateSpacer() { + boolean hasCustom = false; + boolean hasSpace = false; + for (int i = 0; i < lore.size(); i++) { + Type t = Type.get(lore.get(i)); + if (t == Type.CUSTOM) { + hasCustom = true; + } else if (t == Type.SPACE) { + hasSpace = true; + } else if (t != null && t.isAfter(Type.SPACE)) { + if (hasSpace) return; + + if (hasCustom || P.useNBT) { + // We want to add the spacer if we have Custom Lore, to have a space between custom and brew lore. + // Also add a space if there is no Custom Lore but we don't already have a invisible data line + lore.add(i, Type.SPACE.id); + } + return; + } + } + if (hasSpace) { + // There was a space but nothing after the space + removeLore(Type.SPACE); + } + } + + /*private void addSpacer() { + if (!P.useNBT) return; + + for (int i = 0; i < lore.size(); i++) { + if (Type.get(lore.get(i)) != null) { + if (i == 0 || !lore.get(i - 1).equals("")) { + lore.add(i, ""); + } + break; + } + } + }*/ + + /** + * Add the list of strings as custom lore for the base potion coming out of the cauldron + */ + public void addCauldronLore(List l) { + int index = -1; + for (String line : l) { + if (index == -1) { + index = addLore(Type.CUSTOM, "", line); + index++; + } else { + lore.add(index, Type.CUSTOM.id + line); + index++; + } + } + } + + /** + * updates the IngredientLore + * + * @param qualityColor If the lore should have colors according to quality + */ + public void updateIngredientLore(boolean qualityColor) { + if (qualityColor && brew.hasRecipe()) { + String prefix = getQualityColor(brew.getIngredients().getIngredientQuality(brew.getCurrentRecipe())); + addOrReplaceLore(Type.INGR, prefix, P.p.languageReader.get("Brew_Ingredients")); + } else { + removeLore(Type.INGR, P.p.languageReader.get("Brew_Ingredients")); + } + } + + /** + * updates the CookLore + * + * @param qualityColor If the lore should have colors according to quality + */ + public void updateCookLore(boolean qualityColor) { + if (qualityColor && brew.hasRecipe() && brew.getDistillRuns() > 0 == brew.getCurrentRecipe().needsDistilling()) { + BIngredients ingredients = brew.getIngredients(); + int quality = ingredients.getCookingQuality(brew.getCurrentRecipe(), brew.getDistillRuns() > 0); + String prefix = getQualityColor(quality) + ingredients.getCookedTime() + " " + P.p.languageReader.get("Brew_minute"); + if (ingredients.getCookedTime() > 1) { + prefix = prefix + P.p.languageReader.get("Brew_MinutePluralPostfix"); + } + addOrReplaceLore(Type.COOK, prefix, " " + P.p.languageReader.get("Brew_fermented")); + } else { + removeLore(Type.COOK, P.p.languageReader.get("Brew_fermented")); + } + } + + /** + * updates the DistillLore + * + * @param qualityColor If the lore should have colors according to quality + */ + public void updateDistillLore(boolean qualityColor) { + if (brew.getDistillRuns() <= 0) return; + String prefix; + byte distillRuns = brew.getDistillRuns(); + if (qualityColor && !brew.isUnlabeled() && brew.hasRecipe()) { + prefix = getQualityColor(brew.getIngredients().getDistillQuality(brew.getCurrentRecipe(), distillRuns)); + } else { + prefix = "§7"; + } + if (!brew.isUnlabeled()) { + if (distillRuns > 1) { + prefix = prefix + distillRuns + P.p.languageReader.get("Brew_-times") + " "; + } + } + addOrReplaceLore(Type.DISTILL, prefix, P.p.languageReader.get("Brew_Distilled")); + } + + /** + * updates the AgeLore + * + * @param qualityColor If the lore should have colors according to quality + */ + public void updateAgeLore(boolean qualityColor) { + String prefix; + float age = brew.getAgeTime(); + if (qualityColor && !brew.isUnlabeled() && brew.hasRecipe()) { + prefix = getQualityColor(brew.getIngredients().getAgeQuality(brew.getCurrentRecipe(), age)); + } else { + prefix = "§7"; + } + if (!brew.isUnlabeled()) { + if (age >= 1 && age < 2) { + prefix = prefix + P.p.languageReader.get("Brew_OneYear") + " "; + } else if (age < 201) { + prefix = prefix + (int) Math.floor(age) + " " + P.p.languageReader.get("Brew_Years") + " "; + } else { + prefix = prefix + P.p.languageReader.get("Brew_HundredsOfYears") + " "; + } + } + addOrReplaceLore(Type.AGE, prefix, P.p.languageReader.get("Brew_BarrelRiped")); + } + + /** + * updates the WoodLore + * + * @param qualityColor If the lore should have colors according to quality + */ + public void updateWoodLore(boolean qualityColor) { + if (qualityColor && brew.hasRecipe() && !brew.isUnlabeled()) { + int quality = brew.getIngredients().getWoodQuality(brew.getCurrentRecipe(), brew.getWood()); + addOrReplaceLore(Type.WOOD, getQualityColor(quality), P.p.languageReader.get("Brew_Woodtype")); + } else { + removeLore(Type.WOOD, P.p.languageReader.get("Brew_Woodtype")); + } + } + + /** + * updates the Custom Lore + */ + public void updateCustomLore() { + removeLore(Type.CUSTOM); + + BRecipe recipe = brew.getCurrentRecipe(); + if (recipe != null && recipe.hasLore()) { + int index = -1; + for (String line : recipe.getLoreForQuality(brew.getQuality())) { + if (index == -1) { + index = addLore(Type.CUSTOM, "", line); + index++; + } else { + lore.add(index, Type.CUSTOM.id + line); + index++; + } + } + } + } + + public void updateQualityStars(boolean qualityColor) { + if (brew.hasRecipe() && brew.getCurrentRecipe().needsToAge() && brew.getAgeTime() < 0.5) { + return; + } + if (!brew.isUnlabeled() && brew.getQuality() > 0 && (qualityColor || BConfig.alwaysShowQuality)) { + int stars = (brew.getQuality() + 1) / 2; + StringBuilder b = new StringBuilder(stars); + for (; stars > 0; stars--) { + b.append("⭑"); + } + String color; + if (qualityColor) { + color = getQualityColor(brew.getQuality()); + } else { + color = brew.getQuality() >= 10 ? "§6" : "§8"; + } + addOrReplaceLore(Type.STARS, color, b.toString()); + } else { + removeLore(Type.STARS); + } + } + + public void updateAlc(boolean inDistiller) { + if (!brew.isUnlabeled() && (inDistiller || BConfig.alwaysShowAlc) && (!brew.hasRecipe() || brew.getCurrentRecipe().getAlcohol() > 0)) { + int alc = brew.getOrCalcAlc(); + addOrReplaceLore(Type.ALC, "§8", P.p.languageReader.get("Brew_Alc", alc + "")); + } else { + removeLore(Type.ALC); + } + } + + /** + * Converts to/from qualitycolored Lore + */ + public void convertLore(boolean toQuality) { + if (!brew.hasRecipe()) { + return; + } + + updateCustomLore(); + if (toQuality && brew.isUnlabeled()) { + return; + } + updateQualityStars(toQuality); + + // Ingredients + updateIngredientLore(toQuality); + + // Cooking + updateCookLore(toQuality); + + // Distilling + updateDistillLore(toQuality); + + // Ageing + if (brew.getAgeTime() >= 1) { + updateAgeLore(toQuality); + } + + // WoodType + if (brew.getAgeTime() > 0.5) { + updateWoodLore(toQuality); + } + + updateAlc(false); + } + + /** + * Adds or replaces a line of Lore. + *

Searches for type and if not found for Substring lore and replaces it + * + * @param type The Type of BrewLore to replace + * @param prefix The Prefix to add to the line of lore + * @param line The Line of Lore to add or replace + */ + public int addOrReplaceLore(Type type, String prefix, String line) { + int index = type.findInLore(lore); + if (index == -1) { + index = BUtil.indexOfSubstring(lore, line); + } + if (index > -1) { + lore.set(index, type.id + prefix + line); + return index; + } else { + return addLore(type, prefix, line); + } + } + + /** + * Adds a line of Lore in the correct ordering + * + * @param type The Type of BrewLore to add + * @param prefix The Prefix to add to the line of lore + * @param line The Line of Lore to add or add + */ + public int addLore(Type type, String prefix, String line) { + lineAddedOrRem = true; + for (int i = 0; i < lore.size(); i++) { + Type existing = Type.get(lore.get(i)); + if (existing != null && existing.isAfter(type)) { + lore.add(i, type.id + prefix + line); + return i; + } + } + lore.add(type.id + prefix + line); + return lore.size() - 1; + } + + /** + * Searches for type and if not found for Substring lore and removes it + */ + public void removeLore(Type type, String line) { + int index = type.findInLore(lore); + if (index == -1) { + index = BUtil.indexOfSubstring(lore, line); + } + if (index > -1) { + lineAddedOrRem = true; + lore.remove(index); + } + } + + /** + * Searches for type and removes it + */ + public void removeLore(Type type) { + if (type != Type.CUSTOM) { + int index = type.findInLore(lore); + if (index > -1) { + lineAddedOrRem = true; + lore.remove(index); + } + } else { + // Lore could have multiple lines of this type + for (int i = lore.size() - 1; i >= 0; i--) { + if (Type.get(lore.get(i)) == type) { + lore.remove(i); + lineAddedOrRem = true; + } + } + } + } + + /** + * Removes all Brew Lore lines + */ + public void removeAll() { + for (int i = lore.size() - 1; i >= 0; i--) { + if (Type.get(lore.get(i)) != null) { + lore.remove(i); + lineAddedOrRem = true; + } + } + } + + /** + * Adds the Effect names to the Items description + */ + public void addOrReplaceEffects(List effects, int quality) { + if (!P.use1_9 && effects != null) { + for (BEffect effect : effects) { + if (!effect.isHidden()) { + effect.writeInto(meta, quality); + } + } + } + } + + /** + * If the Lore Line at index is a Brew Lore line + * + * @param index the index in lore to check + * @return true if the line at index is of any Brew Lore type + */ + public boolean isBrewLore(int index) { + return index < lore.size() && Type.get(lore.get(index)) != null; + } + + /** + * Removes all effects + */ + public void removeEffects() { + if (meta.hasCustomEffects()) { + for (PotionEffect effect : new ArrayList<>(meta.getCustomEffects())) { + PotionEffectType type = effect.getType(); + //if (!type.equals(PotionEffectType.REGENERATION)) { + meta.removeCustomEffect(type); + //} + } + } + } + + /** + * Remove the Old Spacer from the legacy potion data system + */ + public void removeLegacySpacing() { + if (P.useNBT) { + // Using NBT we don't get the invisible line, so we keep our spacing + return; + } + if (lore.size() > 0 && lore.get(0).equals("")) { + lore.remove(0); + write(); + } + } + + /** + * Remove any Brew Data from Lore + */ + public void removeLoreData() { + int index = BUtil.indexOfStart(lore, LoreSaveStream.IDENTIFIER); + if (index != -1) { + lore.set(index, ""); + write(); + } + } + + /** + * True if the PotionMeta has Lore in quality color + */ + public static boolean hasColorLore(PotionMeta meta) { + if (!meta.hasLore()) return false; + List lore = meta.getLore(); + if (lore.size() < 2) { + return false; + } + if (Type.INGR.findInLore(lore) != -1) { + // Ingredient lore present, must be quality colored + return true; + } + return false; + //!meta.getLore().get(1).startsWith("§7"); + } + + /** + * gets the Color that represents a quality in Lore + * + * @param quality The Quality for which to find the color code + * @return Color Code for given Quality + */ + public static String getQualityColor(int quality) { + String color; + if (quality > 8) { + color = "&a"; + } else if (quality > 6) { + color = "&e"; + } else if (quality > 4) { + color = "&6"; + } else if (quality > 2) { + color = "&c"; + } else { + color = "&4"; + } + return P.p.color(color); + } + + /** + * Type of Lore Line + */ + public enum Type { + STARS("§s"), + CUSTOM("§t"), + SPACE("§u"), + + INGR("§v"), + COOK("§w"), + DISTILL("§x"), + AGE("§y"), + WOOD("§z"), + ALC("§k"); + + public final String id; + + /** + * @param id Identifier as Prefix of the Loreline + */ + Type(String id) { + this.id = id; + } + + /** + * Find this type in the Lore + * + * @param lore The lore to search in + * @return index of this type in the lore, -1 if not found + */ + public int findInLore(List lore) { + return BUtil.indexOfStart(lore, id); + } + + /** + * Is this type after the other in lore + * + * @param other the other type + * @return true if this type should be after the other type in lore + */ + public boolean isAfter(Type other) { + return other.ordinal() <= ordinal(); + } + + /** + * Get the Type of the given line of Lore + */ + @Nullable + public static Type get(String loreLine) { + if (loreLine.length() >= 2) { + return getById(loreLine.substring(0, 2)); + } else { + return null; + } + } + + /** + * Get the Type of the given Identifier, prefix of a line of lore + */ + @Nullable + public static Type getById(String id) { + for (Type t : values()) { + if (t.id.equals(id)) { + return t; + } + } + return null; + } + + } +} diff --git a/src/com/dre/brewery/lore/LoreLoadStream.java b/src/com/dre/brewery/lore/LoreLoadStream.java new file mode 100644 index 0000000..4ad7ab2 --- /dev/null +++ b/src/com/dre/brewery/lore/LoreLoadStream.java @@ -0,0 +1,52 @@ +package com.dre.brewery.lore; + +import org.bukkit.inventory.meta.ItemMeta; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.List; + +public class LoreLoadStream extends ByteArrayInputStream { + + public static final String IDENTIFIER = "§%"; + + public LoreLoadStream(ItemMeta meta) throws IllegalArgumentException { + this(meta, -1); + } + + public LoreLoadStream(ItemMeta meta, int line) throws IllegalArgumentException { + super(loreToBytes(meta, line)); + } + + private static byte[] loreToBytes(ItemMeta meta, int lineNum) throws IllegalArgumentException { + if (meta.hasLore()) { + List lore = meta.getLore(); + if (lineNum >= 0) { + String line = lore.get(lineNum); + if (line.startsWith(IDENTIFIER)) { + return loreLineToBytes(line); + } + } + for (String line : lore) { + if (line.startsWith(IDENTIFIER)) { + return loreLineToBytes(line); + } + } + } + throw new IllegalArgumentException("Meta has no data in lore"); + } + + private static byte[] loreLineToBytes(String line) { + StringBuilder build = new StringBuilder((int) (line.length() / 2F)); + byte skip = 2; + for (char c : line.toCharArray()) { + if (skip > 0) { + skip--; + continue; + } + if (c == '§') continue; + build.append(c); + } + return build.toString().getBytes(); + } +} diff --git a/src/com/dre/brewery/lore/LoreSaveStream.java b/src/com/dre/brewery/lore/LoreSaveStream.java new file mode 100644 index 0000000..de127d3 --- /dev/null +++ b/src/com/dre/brewery/lore/LoreSaveStream.java @@ -0,0 +1,80 @@ +package com.dre.brewery.lore; + +import org.bukkit.inventory.meta.ItemMeta; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class LoreSaveStream extends ByteArrayOutputStream { + + public static final String IDENTIFIER = "§%"; + + private ItemMeta meta; + private int line; + private boolean flushed = false; + + public LoreSaveStream(ItemMeta meta) { + this(meta, -1); + } + + public LoreSaveStream(ItemMeta meta, int line) { + super(128); + this.meta = meta; + this.line = line; + } + + // Writes to the Lore + // Without calling this, the ItemMeta remains unchanged + @Override + public void flush() throws IOException { + super.flush(); + if (size() <= 0) return; + if (flushed || meta == null) { + // Dont write twice + return; + } + flushed = true; + String s = toString(); + + StringBuilder loreLineBuilder = new StringBuilder((s.length() * 2) + 6); + loreLineBuilder.append(IDENTIFIER); + for (char c : s.toCharArray()) { + loreLineBuilder.append('§').append(c); + } + List lore; + if (meta.hasLore()) { + lore = meta.getLore(); + } else { + lore = new ArrayList<>(); + } + int prev = 0; + for (Iterator iterator = lore.iterator(); iterator.hasNext(); ) { + if (iterator.next().startsWith(IDENTIFIER)) { + iterator.remove(); + break; + } + prev++; + } + if (line < 0) { + if (prev >= 0) { + line = prev; + } else { + line = lore.size(); + } + } + while (lore.size() < line) { + lore.add(""); + } + lore.add(line, loreLineBuilder.toString()); + meta.setLore(lore); + } + + @Override + public void close() throws IOException { + super.close(); + meta = null; + } +} diff --git a/src/com/dre/brewery/lore/NBTLoadStream.java b/src/com/dre/brewery/lore/NBTLoadStream.java new file mode 100644 index 0000000..46dccd0 --- /dev/null +++ b/src/com/dre/brewery/lore/NBTLoadStream.java @@ -0,0 +1,33 @@ +package com.dre.brewery.lore; + +import com.dre.brewery.P; +import com.dre.brewery.utility.LegacyUtil; +import org.bukkit.NamespacedKey; +import org.bukkit.inventory.meta.ItemMeta; + +import java.io.ByteArrayInputStream; + +public class NBTLoadStream extends ByteArrayInputStream { + private static final String TAG = "brewdata"; + private static final NamespacedKey KEY = new NamespacedKey(P.p, TAG); + + public NBTLoadStream(ItemMeta meta) { + super(getNBTBytes(meta)); + } + + private static byte[] getNBTBytes(ItemMeta meta) { + byte[] bytes = LegacyUtil.readBytesItem(meta, KEY); + if (bytes == null) { + return new byte[0]; + } + return bytes; + } + + public boolean hasData() { + return count > 0; + } + + public static boolean hasDataInMeta(ItemMeta meta) { + return LegacyUtil.hasBytesItem(meta, KEY); + } +} diff --git a/src/com/dre/brewery/lore/NBTSaveStream.java b/src/com/dre/brewery/lore/NBTSaveStream.java new file mode 100644 index 0000000..457018d --- /dev/null +++ b/src/com/dre/brewery/lore/NBTSaveStream.java @@ -0,0 +1,28 @@ +package com.dre.brewery.lore; + +import com.dre.brewery.P; +import com.dre.brewery.utility.LegacyUtil; +import org.bukkit.NamespacedKey; +import org.bukkit.inventory.meta.ItemMeta; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +public class NBTSaveStream extends ByteArrayOutputStream { + private static final String TAG = "brewdata"; + private static final NamespacedKey KEY = new NamespacedKey(P.p, TAG); + + private final ItemMeta meta; + + public NBTSaveStream(ItemMeta meta) { + super(128); + this.meta = meta; + } + + @Override + public void flush() throws IOException { + super.flush(); + if (size() <= 0) return; + LegacyUtil.writeBytesItem(toByteArray(), meta, KEY); + } +} diff --git a/src/com/dre/brewery/lore/SeedInputStream.java b/src/com/dre/brewery/lore/SeedInputStream.java new file mode 100644 index 0000000..2d892c1 --- /dev/null +++ b/src/com/dre/brewery/lore/SeedInputStream.java @@ -0,0 +1,96 @@ +package com.dre.brewery.lore; + +import org.jetbrains.annotations.NotNull; + +import java.io.InputStream; +import java.util.Arrays; + +public class SeedInputStream extends InputStream { + // From java.util.Random + private static final long multiplier = 0x5DEECE66DL; + private static final long addend = 0xBL; + private static final long mask = (1L << 48) - 1; + + private long seed; + private byte[] buf = new byte[4]; + private byte reader = 4; + private long markSeed; + private byte[] markbuf; + + public SeedInputStream(long seed) { + this.seed = (seed ^ multiplier) & mask; + } + + private void calcSeed() { + seed = (seed * multiplier + addend) & mask; + } + + private void genNext() { + calcSeed(); + int next = (int)(seed >>> 16); + buf[0] = (byte) (next >> 24); + buf[1] = (byte) (next >> 16); + buf[2] = (byte) (next >> 8); + buf[3] = (byte) next; + reader = 0; + } + + @Override + public int read(@NotNull byte[] b, int off, int len) { + for (int i = off; i < len; i++) { + if (reader >= 4) { + genNext(); + } + b[i] = buf[reader++]; + } + return len; + } + + @Override + public int read() { + if (reader == 4) { + genNext(); + } + return buf[reader++]; + } + + @Override + public long skip(long toSkip) { + long n = toSkip; + while (n > 0) { + if (reader < 4) { + reader++; + n--; + } else if (n >= 4) { + calcSeed(); + n -= 4; + } else { + genNext(); + } + } + return toSkip; + } + + @Override + public void close() { + buf = null; + } + + @Override + public boolean markSupported() { + return true; + } + + @Override + public synchronized void mark(int readlimit) { + markbuf = new byte[] {buf[0], buf[1], buf[2], buf[3], reader}; + markSeed = seed; + } + + @Override + public synchronized void reset() { + seed = markSeed; + buf = Arrays.copyOfRange(markbuf, 0, 4); + reader = markbuf[4]; + } +} diff --git a/src/com/dre/brewery/lore/XORScrambleStream.java b/src/com/dre/brewery/lore/XORScrambleStream.java new file mode 100644 index 0000000..0072375 --- /dev/null +++ b/src/com/dre/brewery/lore/XORScrambleStream.java @@ -0,0 +1,103 @@ +package com.dre.brewery.lore; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Random; + +/** + * A Scramble Stream that uses XOR operations to scramble an outputstream. + *

a byte generator feeded with the seed is used as xor source + *

The resulting data can be unscrambled by the XORUnscrambleStream + */ +public class XORScrambleStream extends FilterOutputStream { + + private final long seed; + private SeedInputStream xorStream; + private boolean running; + + /** + * Create a new instance of an XORScrambler, scrambling the given outputstream + * + * @param out The Outputstream to be scrambled + * @param seed The seed used for scrambling + */ + public XORScrambleStream(OutputStream out, long seed) { + super(out); + this.seed = seed; + } + + /** + * To start the scrambling process this has to be called before writing any data to this stream. + *
Before starting the scrambler, any data will just be passed through unscrambled to the underlying stream. + *
The Scrambling can be started and stopped arbitrarily at any point, allowing for parts of unscrambled data in the stream. + * + * @throws IOException IOException + */ + public void start() throws IOException { + running = true; + if (xorStream == null) { + short id = 0; + while (id == 0) { + id = (short) new Random().nextInt(); + } + xorStream = new SeedInputStream(seed ^ id); + out.write((byte) (id >> 8)); + out.write((byte) id); + write((int) (seed >> 48) & 0xFF); // parity/sanity + } + } + + /** + * Stop the scrambling, any following data will be passed through unscrambled. + *
The scrambling can be started again at any point after calling this + */ + public void stop() { + running = false; + } + + /** + * Mark the stream as unscrambled, any effort of unscrambing the data later will automatically read the already unscrambled data. + *

Useful if a stream may be scrambled or unscrambled, the unscrambler will automatically identify either way. + * + * @throws IOException IOException + * @throws IllegalStateException If the Scrambler was started in normal scrambling mode before + */ + public void startUnscrambled() throws IOException, IllegalStateException { + if (xorStream != null) throw new IllegalStateException("The Scrambler was started in scrambling mode before"); + short id = 0; + out.write((byte) (id >> 8)); + out.write((byte) id); + } + + @Override + public void write(int b) throws IOException { + if (!running) { + out.write(b); + return; + } + out.write(b ^ xorStream.read()); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + if (!running) { + out.write(b, off, len); + return; + } + byte[] xored = new byte[len]; + xorStream.read(xored); + int j = off; + for (int i = 0; i < len; i++) { + xored[i] ^= b[j++]; + } + out.write(xored); + } + + @Override + public void close() throws IOException { + running = false; + xorStream = null; + super.close(); + } +} diff --git a/src/com/dre/brewery/lore/XORUnscrambleStream.java b/src/com/dre/brewery/lore/XORUnscrambleStream.java new file mode 100644 index 0000000..38a7123 --- /dev/null +++ b/src/com/dre/brewery/lore/XORUnscrambleStream.java @@ -0,0 +1,208 @@ +package com.dre.brewery.lore; + +import com.dre.brewery.P; +import org.jetbrains.annotations.NotNull; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.InvalidKeyException; +import java.util.List; + +/** + * A Scramble Stream that uses XOR operations to unscramble an inputstream. + *

a byte generator feeded with the seed is used as xor source + *

Used to unscramble data generated by the XORScrambleStream + */ +public class XORUnscrambleStream extends FilterInputStream { + + private long seed; + private final List prevSeeds; + private SeedInputStream xorStream; + private boolean running; + private boolean markRunning; + private boolean markxor; + + private SuccessType successType = SuccessType.NONE; + + /** + * Create a new instance of an XORUnscrambler, unscrambling the given inputstream. + * + * @param in The Inputstream to be unscrambled + * @param seed The seed used for unscrambling + */ + public XORUnscrambleStream(InputStream in, long seed) { + super(in); + this.seed = seed; + prevSeeds = null; + } + + /** + * Create a new instance of an XORUnscrambler, unscrambling the given inputstream. + *

If given a List of previous Seeds, the unscrambler will try all of them for unscrambling the stream in case the seed fails. + * + * @param in The Inputstream to be unscrambled + * @param seed The seed used for unscrambling + * @param prevSeeds List of previously used seeds + */ + public XORUnscrambleStream(InputStream in, long seed, List prevSeeds) { + super(in); + this.seed = seed; + this.prevSeeds = prevSeeds; + } + + /** + * Before unscrambling, this has to be called to tell the unscrambler that scrambled data will follow. + *
Before starting the unscrambler, any data will just be passed through unmodified to the underlying stream. + *
The Unscrambling can be started and stopped arbitrarily at any point, allowing for parts of already unscrambled data in the stream. + * + * @throws IOException IOException + * @throws InvalidKeyException If the scrambled data could not be read, very likely caused by a wrong seed. Thrown after checking all previous seeds. + */ + public void start() throws IOException, InvalidKeyException { + running = true; + if (xorStream == null) { + short id = (short) (in.read() << 8 | in.read()); + if (id == 0) { + running = false; + successType = SuccessType.UNSCRAMBLED; + P.p.debugLog("Unscrambled data"); + return; + } + int parity = in.read(); + xorStream = new SeedInputStream(seed ^ id); + boolean success = checkParity(parity); + if (success) { + successType = SuccessType.MAIN_SEED; + P.p.debugLog("Using main Seed to unscramble"); + } + + if (!success && prevSeeds != null) { + for (int i = prevSeeds.size() - 1; i >= 0; i--) { + seed = prevSeeds.get(i); + xorStream = new SeedInputStream(seed ^ id); + if (success = checkParity(parity)) { + successType = SuccessType.PREV_SEED; + P.p.debugLog("Had to use prevSeed to unscramble"); + break; + } + } + } + if (!success) { + throw new InvalidKeyException("Could not read scrambled data, is the seed wrong?"); + } + } + } + + private boolean checkParity(int parity) { + return ((parity ^ xorStream.read()) & 0xFF) == ((int) (seed >> 48) & 0xFF); // Parity/Sanity + } + + /** + * Stop the unscrambling, any following data will be passed through unmodified. + *

The unscrambling can be started again at any point after calling this + */ + public void stop() { + running = false; + } + + @Override + public int read() throws IOException { + if (!running) { + return in.read(); + } + return (in.read() ^ xorStream.read()) & 0xFF; + } + + @Override + public int read(@NotNull byte[] b, int off, int len) throws IOException { + if (!running) { + return in.read(b, off, len); + } + len = in.read(b, off, len); + for (int i = off; i < len + off; i++) { + b[i] ^= xorStream.read(); + } + return len; + } + + /** + * What was used to unscramble the stream: it was already unscrambled | Main Seed | Prev Seed + * + * @return The Type of Seed used to unscramble this, if any + */ + public SuccessType getSuccessType() { + return successType; + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + @Override + public long skip(long n) throws IOException { + long skipped = in.skip(n); + if (running && skipped > 0) { + xorStream.skip(skipped); + } + return skipped; + } + + @Override + public void close() throws IOException { + if (xorStream != null) { + xorStream.close(); + xorStream = null; + } + running = false; + super.close(); + } + + @Override + public boolean markSupported() { + return in.markSupported(); + } + + @Override + public synchronized void reset() throws IOException { + in.reset(); + if (markxor) { + xorStream.reset(); + } else { + xorStream = null; + } + running = markRunning; + } + + @Override + public synchronized void mark(int readlimit) { + in.mark(readlimit); + if (xorStream != null) { + xorStream.mark(readlimit); + markxor = true; + } + markRunning = running; + } + + /** + * What succeeded in unscrambling the Stream. + */ + public static enum SuccessType { + /** + * The Stream was already unscrambled. + */ + UNSCRAMBLED, + + /** + * The Main Seed was used to unscramble the Stream. + */ + MAIN_SEED, + + /** + * One of the Previous Seeds was used to unscramble the Stream. + */ + PREV_SEED, + + /** + * It was not successful. + */ + NONE; + } +} diff --git a/src/com/dre/brewery/lore/basE91.java b/src/com/dre/brewery/lore/basE91.java new file mode 100644 index 0000000..c564c71 --- /dev/null +++ b/src/com/dre/brewery/lore/basE91.java @@ -0,0 +1,155 @@ +package com.dre.brewery.lore; + +/* + * basE91 encoding/decoding routines + * + * Copyright (c) 2000-2006 Joachim Henke + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of Joachim Henke nor the names of his contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +public class basE91 +{ + public static final byte[] enctab; + private static final byte[] dectab; + + private int ebq, en, dbq, dn, dv; + private int[] marker = null; + + public int encode(byte[] ib, int n, byte[] ob) + { + int i, c = 0; + + for (i = 0; i < n; ++i) { + ebq |= (ib[i] & 255) << en; + en += 8; + if (en > 13) { + int ev = ebq & 8191; + + if (ev > 88) { + ebq >>= 13; + en -= 13; + } else { + ev = ebq & 16383; + ebq >>= 14; + en -= 14; + } + ob[c++] = enctab[ev % 91]; + ob[c++] = enctab[ev / 91]; + } + } + return c; + } + + public int encEnd(byte[] ob) + { + int c = 0; + + if (en > 0) { + ob[c++] = enctab[ebq % 91]; + if (en > 7 || ebq > 90) + ob[c++] = enctab[ebq / 91]; + } + encReset(); + return c; + } + + public void encReset() + { + ebq = 0; + en = 0; + } + + public int decode(byte[] ib, int n, byte[] ob) + { + int i, c = 0; + + for (i = 0; i < n; ++i) { + if (dectab[ib[i]] == -1) + continue; + if (dv == -1) + dv = dectab[ib[i]]; + else { + dv += dectab[ib[i]] * 91; + dbq |= dv << dn; + dn += (dv & 8191) > 88 ? 13 : 14; + do { + ob[c++] = (byte) dbq; + dbq >>= 8; + dn -= 8; + } while (dn > 7); + dv = -1; + } + } + return c; + } + + public int decEnd(byte[] ob) + { + int c = 0; + + if (dv != -1) + ob[c++] = (byte) (dbq | dv << dn); + decReset(); + return c; + } + + public void decReset() + { + dbq = 0; + dn = 0; + dv = -1; + } + + public void decMark() { + marker = new int[] {dbq, dn, dv}; + } + + public void decUnmark() { + if (marker == null) return; + dbq = marker[0]; + dn = marker[1]; + dv = marker[2]; + } + + public basE91() + { + encReset(); + decReset(); + } + + static { + int i; + String ts = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!$%&()*+,-./:;<=>?@[]^_`{|}~\""; // Added '-' removed '#' + + enctab = ts.getBytes(); + dectab = new byte[256]; + for (i = 0; i < 256; ++i) + dectab[i] = -1; + for (i = 0; i < 91; ++i) + dectab[enctab[i]] = (byte) i; + } +} diff --git a/src/com/dre/brewery/recipe/BCauldronRecipe.java b/src/com/dre/brewery/recipe/BCauldronRecipe.java new file mode 100644 index 0000000..c3143e6 --- /dev/null +++ b/src/com/dre/brewery/recipe/BCauldronRecipe.java @@ -0,0 +1,346 @@ +package com.dre.brewery.recipe; + +import com.dre.brewery.P; +import com.dre.brewery.utility.Tuple; +import org.bukkit.Color; +import org.bukkit.Material; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * A Recipe for the Base Potion coming out of the Cauldron. + */ +public class BCauldronRecipe { + public static List recipes = new ArrayList<>(); + public static int numConfigRecipes; + public static List acceptedCustom = new ArrayList<>(); // All accepted custom and other items + public static Set acceptedSimple = EnumSet.noneOf(Material.class); // All accepted simple items + public static Set acceptedMaterials = EnumSet.noneOf(Material.class); // Fast cache for all accepted Materials + + private String name; + private List ingredients; + //private List particles + private PotionColor color; + private List lore; + private boolean saveInData; // If this recipe should be saved in data and loaded again when the server restarts. Applicable to non-config recipes + + + /** + * A New Cauldron Recipe with the given name. + *

Use new BCauldronRecipe.Builder() for easier Cauldron Recipe Creation + * + * @param name Name of the Cauldron Recipe + */ + public BCauldronRecipe(String name) { + this.name = name; + color = PotionColor.CYAN; + } + + @Nullable + public static BCauldronRecipe fromConfig(ConfigurationSection cfg, String id) { + + String name = cfg.getString(id + ".name"); + if (name != null) { + name = P.p.color(name); + } else { + P.p.errorLog("Missing name for Cauldron-Recipe: " + id); + return null; + } + + BCauldronRecipe recipe = new BCauldronRecipe(name); + + recipe.ingredients = BRecipe.loadIngredients(cfg, id); + if (recipe.ingredients == null || recipe.ingredients.isEmpty()) { + P.p.errorLog("No ingredients for Cauldron-Recipe: " + recipe.name); + return null; + } + + String col = cfg.getString(id + ".color"); + if (col != null) { + recipe.color = PotionColor.fromString(col); + } else { + recipe.color = PotionColor.CYAN; + } + if (recipe.color == PotionColor.WATER && !col.equals("WATER")) { + recipe.color = PotionColor.CYAN; + // Don't throw error here as old mc versions will not know even the default colors + //P.p.errorLog("Invalid Color '" + col + "' in Cauldron-Recipe: " + recipe.name); + //return null; + } + + + List> lore = BRecipe.loadLore(cfg, id + ".lore"); + if (lore != null && !lore.isEmpty()) { + recipe.lore = lore.stream().map(Tuple::second).collect(Collectors.toList()); + } + + return recipe; + } + + + // Getter + + @NotNull + public String getName() { + return name; + } + + @NotNull + public List getIngredients() { + return ingredients; + } + + @NotNull + public PotionColor getColor() { + return color; + } + + @Nullable + public List getLore() { + return lore; + } + + public boolean isSaveInData() { + return saveInData; + } + + + // Setter + + /** + * When Changing ingredients, Accepted Lists have to be updated in BCauldronRecipe + */ + public void setIngredients(@NotNull List ingredients) { + this.ingredients = ingredients; + } + + public void setColor(@NotNull PotionColor color) { + this.color = color; + } + + public void setLore(List lore) { + this.lore = lore; + } + + public void setSaveInData(boolean saveInData) { + this.saveInData = saveInData; + } + + /** + * Find how much these ingredients match the given ones from 0-10. + *

If any ingredient is missing, returns 0 + *
If all Ingredients and their amounts are equal, returns 10 + *
Returns something between 0 and 10 if all ingredients present, but differing amounts, depending on how much the amount differs. + */ + public float getIngredientMatch(List items) { + if (items.size() < ingredients.size()) { + return 0; + } + float match = 10; + search: for (RecipeItem recipeIng : ingredients) { + for (Ingredient ing : items) { + if (recipeIng.matches(ing)) { + double difference = Math.abs(recipeIng.getAmount() - ing.getAmount()); + if (difference >= 1000) { + return 0; + } + // The Item Amount is the determining part here, the higher the better. + // But let the difference in amount to what the recipe expects have a tiny factor as well. + // This way for the same amount, the recipe with the lower difference wins. + double factor = ing.getAmount() * (1.0 - (difference / 1000.0)) ; + //double mod = 0.1 + (0.9 * Math.exp(-0.03 * difference)); // logarithmic curve from 1 to 0.1 + double mod = 1 + (0.9 * -Math.exp(-0.03 * factor)); // logarithmic curve from 0.1 to 1, small for a low factor + + P.p.debugLog("Mod for " + recipeIng + ": " + mod); + + + + + match *= mod; + continue search; + } + } + return 0; + } + if (items.size() > ingredients.size()) { + // If there are too many items in the List, multiply the match by 0.1 per Item thats too much + float tooMuch = items.size() - ingredients.size(); + float mod = 0.1f / tooMuch; + match *= mod; + } + P.p.debugLog("Match for Cauldron Recipe " + name + ": " + match); + return match; + } + + public void updateAcceptedLists() { + for (RecipeItem ingredient : getIngredients()) { + if (ingredient.hasMaterials()) { + BCauldronRecipe.acceptedMaterials.addAll(ingredient.getMaterials()); + } + if (ingredient instanceof SimpleItem) { + BCauldronRecipe.acceptedSimple.add(((SimpleItem) ingredient).getMaterial()); + } else { + // Add it as acceptedCustom + if (!BCauldronRecipe.acceptedCustom.contains(ingredient)) { + BCauldronRecipe.acceptedCustom.add(ingredient); + } + } + } + } + + @Override + public String toString() { + return "BCauldronRecipe{" + name + '}'; + } + + @Nullable + public static BCauldronRecipe get(String name) { + for (BCauldronRecipe recipe : recipes) { + if (recipe.name.equalsIgnoreCase(name)) { + return recipe; + } + } + return null; + } + + /** + * Gets a Modifiable Sublist of the CauldronRecipes that are loaded by config. + *

Changes are directly reflected by the main list of all recipes + *
Changes to the main List of all CauldronRecipes will make the reference to this sublist invalid + * + *

After adding or removing elements, CauldronRecipes.numConfigRecipes MUST be updated! + */ + public static List getConfigRecipes() { + return recipes.subList(0, numConfigRecipes); + } + + /** + * Gets a Modifiable Sublist of the CauldronRecipes that are added by plugins. + *

Changes are directly reflected by the main list of all recipes + *
Changes to the main List of all CauldronRecipes will make the reference to this sublist invalid + */ + public static List getAddedRecipes() { + return recipes.subList(numConfigRecipes, recipes.size()); + } + + /** + * Gets the main List of all CauldronRecipes. + */ + public static List getAllRecipes() { + return recipes; + } + + /*public static boolean acceptItem(ItemStack item) { + if (acceptedMaterials.contains(item.getType())) { + // Extremely fast way to check for most items + return true; + } + if (!item.hasItemMeta()) { + return false; + } + // If the Item is not on the list, but customized, we have to do more checks + ItemMeta meta = item.getItemMeta(); + assert meta != null; + if (meta.hasDisplayName() || meta.hasLore()) { + for (BItem bItem : acceptedCustom) { + if (bItem.matches(item)) { + return true; + } + } + } + return false; + } + + @Nullable + public static RecipeItem acceptItem(ItemStack item) { + if (!acceptedMaterials.contains(item.getType()) && !item.hasItemMeta()) { + // Extremely fast way to check for most items + return null; + } + // If the Item is on the list, or customized, we have to do more checks + for (RecipeItem rItem : acceptedItems) { + if (rItem.matches(item)) { + return rItem; + } + } + return null; + }*/ + + /** + * Builder to easily create BCauldron recipes. + */ + public static class Builder { + private BCauldronRecipe recipe; + + public Builder(String name) { + recipe = new BCauldronRecipe(name); + } + + + public Builder addIngredient(RecipeItem... item) { + if (recipe.ingredients == null) { + recipe.ingredients = new ArrayList<>(); + } + Collections.addAll(recipe.ingredients, item); + return this; + } + + public Builder addIngredient(ItemStack... item) { + if (recipe.ingredients == null) { + recipe.ingredients = new ArrayList<>(); + } + for (ItemStack i : item) { + recipe.ingredients.add(new CustomItem(i)); + } + return this; + } + + public Builder color(String colorString) { + recipe.color = PotionColor.fromString(colorString); + return this; + } + + public Builder color(PotionColor color) { + recipe.color = color; + return this; + } + + public Builder color(Color color) { + recipe.color = PotionColor.fromColor(color); + return this; + } + + public Builder addLore(String line) { + if (recipe.lore == null) { + recipe.lore = new ArrayList<>(); + } + recipe.lore.add(line); + return this; + } + + public BCauldronRecipe get() { + if (recipe.name == null) { + throw new IllegalArgumentException("CauldronRecipe name is null"); + } + if (BCauldronRecipe.get(recipe.getName()) != null) { + throw new IllegalArgumentException("CauldronRecipe with name " + recipe.getName() + " already exists"); + } + if (recipe.color == null) { + throw new IllegalArgumentException("CauldronRecipe has no color"); + } + if (recipe.ingredients == null || recipe.ingredients.isEmpty()) { + throw new IllegalArgumentException("CauldronRecipe has no ingredients"); + } + return recipe; + } + } +} diff --git a/src/com/dre/brewery/BEffect.java b/src/com/dre/brewery/recipe/BEffect.java similarity index 81% rename from src/com/dre/brewery/BEffect.java rename to src/com/dre/brewery/recipe/BEffect.java index 7339f49..edb99cc 100644 --- a/src/com/dre/brewery/BEffect.java +++ b/src/com/dre/brewery/recipe/BEffect.java @@ -1,7 +1,10 @@ -package com.dre.brewery; +package com.dre.brewery.recipe; +import com.dre.brewery.P; +import com.dre.brewery.utility.BUtil; import org.bukkit.entity.Player; import org.bukkit.inventory.meta.PotionMeta; +import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; public class BEffect { @@ -14,6 +17,15 @@ public class BEffect { private boolean hidden = false; + public BEffect(PotionEffectType type, short minlvl, short maxlvl, short minduration, short maxduration, boolean hidden) { + this.type = type; + this.minlvl = minlvl; + this.maxlvl = maxlvl; + this.minduration = minduration; + this.maxduration = maxduration; + this.hidden = hidden; + } + public BEffect(String effectString) { String[] effectSplit = effectString.split("/"); String effect = effectSplit[0]; @@ -81,19 +93,26 @@ public class BEffect { } } - public void apply(int quality, Player player) { + public PotionEffect generateEffect(int quality) { int duration = calcDuration(quality); int lvl = calcLvl(quality); if (lvl < 1 || (duration < 1 && !type.isInstant())) { - return; + return null; } duration *= 20; if (!P.use1_14) { duration /= type.getDurationModifier(); } - Util.reapplyPotionEffect(player, type.createEffect(duration, lvl - 1), true); + return type.createEffect(duration, lvl - 1); + } + + public void apply(int quality, Player player) { + PotionEffect effect = generateEffect(quality); + if (effect != null) { + BUtil.reapplyPotionEffect(player, effect, true); + } } public int calcDuration(float quality) { diff --git a/src/com/dre/brewery/recipe/BRecipe.java b/src/com/dre/brewery/recipe/BRecipe.java new file mode 100644 index 0000000..753ce66 --- /dev/null +++ b/src/com/dre/brewery/recipe/BRecipe.java @@ -0,0 +1,916 @@ +package com.dre.brewery.recipe; + +import com.dre.brewery.BIngredients; +import com.dre.brewery.Brew; +import com.dre.brewery.P; +import com.dre.brewery.filedata.BConfig; +import com.dre.brewery.utility.BUtil; +import com.dre.brewery.utility.Tuple; +import org.apache.commons.lang.NotImplementedException; +import org.bukkit.Color; +import org.bukkit.Material; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.ListIterator; + +/** + * A Recipe used to Brew a Brewery Potion. + */ +public class BRecipe { + + private static List recipes = new ArrayList<>(); + public static int numConfigRecipes; // The number of recipes in the list that are from config + + // info + private String[] name; + private boolean saveInData; // If this recipe should be saved in data and loaded again when the server restarts. Applicable to non-config recipes + + // brewing + private List ingredients = new ArrayList<>(); // Items and amounts + private int difficulty; // difficulty to brew the potion, how exact the instruction has to be followed + private int cookingTime; // time to cook in cauldron + private byte distillruns; // runs through the brewer + private int distillTime; // time for one distill run in seconds + private byte wood; // type of wood the barrel has to consist of + private int age; // time in minecraft days for the potions to age in barrels + + // outcome + private PotionColor color; // color of the distilled/finished potion + private int alcohol; // Alcohol in perfect potion + private List> lore; // Custom Lore on the Potion. The int is for Quality Lore, 0 = any, 1,2,3 = Bad,Middle,Good + + // drinking + private List effects = new ArrayList<>(); // Special Effects when drinking + private List playercmds; // Commands executed as the player when drinking + private List servercmds; // Commands executed as the server when drinking + private String drinkMsg; // Message when drinking + private String drinkTitle; // Title to show when drinking + + private BRecipe() { + } + + /** + * New BRecipe with Name. + *

Use new BRecipe.Builder() for easier Recipe Creation + * + * @param name The name for all qualities + */ + public BRecipe(String name, @NotNull PotionColor color) { + this.name = new String[] {name}; + this.color = color; + difficulty = 5; + } + + /** + * New BRecipe with Names. + *

Use new BRecipe.Builder() for easier Recipe Creation + * + * @param names {name bad, name normal, name good} + */ + public BRecipe(String[] names, @NotNull PotionColor color) { + this.name = names; + this.color = color; + difficulty = 5; + } + + @Nullable + public static BRecipe fromConfig(ConfigurationSection configSectionRecipes, String recipeId) { + BRecipe recipe = new BRecipe(); + String nameList = configSectionRecipes.getString(recipeId + ".name"); + if (nameList != null) { + String[] name = nameList.split("/"); + if (name.length > 2) { + recipe.name = name; + } else { + recipe.name = new String[1]; + recipe.name[0] = name[0]; + } + } else { + P.p.errorLog(recipeId + ": Recipe Name missing or invalid!"); + return null; + } + if (recipe.getRecipeName() == null || recipe.getRecipeName().length() < 1) { + P.p.errorLog(recipeId + ": Recipe Name invalid"); + return null; + } + + recipe.ingredients = loadIngredients(configSectionRecipes, recipeId); + if (recipe.ingredients == null || recipe.ingredients.isEmpty()) { + P.p.errorLog("No ingredients for: " + recipe.getRecipeName()); + return null; + } + recipe.cookingTime = configSectionRecipes.getInt(recipeId + ".cookingtime", 1); + int dis = configSectionRecipes.getInt(recipeId + ".distillruns", 0); + if (dis > Byte.MAX_VALUE) { + recipe.distillruns = Byte.MAX_VALUE; + } else { + recipe.distillruns = (byte) dis; + } + recipe.distillTime = configSectionRecipes.getInt(recipeId + ".distilltime", 0) * 20; + recipe.wood = (byte) configSectionRecipes.getInt(recipeId + ".wood", 0); + recipe.age = configSectionRecipes.getInt(recipeId + ".age", 0); + recipe.difficulty = configSectionRecipes.getInt(recipeId + ".difficulty", 0); + recipe.alcohol = configSectionRecipes.getInt(recipeId + ".alcohol", 0); + + String col = configSectionRecipes.getString(recipeId + ".color", "BLUE"); + recipe.color = PotionColor.fromString(col); + if (recipe.color == PotionColor.WATER && !col.equals("WATER")) { + P.p.errorLog("Invalid Color '" + col + "' in Recipe: " + recipe.getRecipeName()); + return null; + } + + recipe.lore = loadLore(configSectionRecipes, recipeId + ".lore"); + + recipe.servercmds = BUtil.loadCfgStringList(configSectionRecipes, recipeId + ".servercommands"); + recipe.playercmds = BUtil.loadCfgStringList(configSectionRecipes, recipeId + ".playercommands"); + + if (recipe.servercmds != null && !recipe.servercmds.isEmpty()) { + for (ListIterator iter = recipe.servercmds.listIterator(); iter.hasNext(); ) { + String cmd = iter.next(); + if (cmd.startsWith("/")) { + iter.set(cmd.substring(1)); + } + } + } + if (recipe.playercmds != null && !recipe.playercmds.isEmpty()) { + for (ListIterator iter = recipe.playercmds.listIterator(); iter.hasNext(); ) { + String cmd = iter.next(); + if (cmd.startsWith("/")) { + iter.set(cmd.substring(1)); + } + } + } + + recipe.drinkMsg = P.p.color(BUtil.loadCfgString(configSectionRecipes, recipeId + ".drinkmessage")); + recipe.drinkTitle = P.p.color(BUtil.loadCfgString(configSectionRecipes, recipeId + ".drinktitle")); + + List effectStringList = configSectionRecipes.getStringList(recipeId + ".effects"); + if (effectStringList != null) { + for (String effectString : effectStringList) { + BEffect effect = new BEffect(effectString); + if (effect.isValid()) { + recipe.effects.add(effect); + } else { + P.p.errorLog("Error adding Effect to Recipe: " + recipe.getRecipeName()); + } + } + } + return recipe; + } + + public static List loadIngredients(ConfigurationSection cfg, String recipeId) { + List ingredientsList; + if (cfg.isString(recipeId + ".ingredients")) { + ingredientsList = new ArrayList<>(1); + ingredientsList.add(cfg.getString(recipeId + ".ingredients", "x")); + } else { + ingredientsList = cfg.getStringList(recipeId + ".ingredients"); + } + if (ingredientsList == null) { + return null; + } + List ingredients = new ArrayList<>(ingredientsList.size()); + listLoop: for (String item : ingredientsList) { + String[] ingredParts = item.split("/"); + int amount = 1; + if (ingredParts.length == 2) { + amount = P.p.parseInt(ingredParts[1]); + if (amount < 1) { + P.p.errorLog(recipeId + ": Invalid Item Amount: " + ingredParts[1]); + return null; + } + } + String[] matParts; + if (ingredParts[0].contains(",")) { + matParts = ingredParts[0].split(","); + } else if (ingredParts[0].contains(";")) { + matParts = ingredParts[0].split(";"); + } else { + matParts = ingredParts[0].split("\\."); + } + + // Check if this is a Plugin Item + String[] pluginItem = matParts[0].split(":"); + if (pluginItem.length > 1) { + RecipeItem custom = PluginItem.fromConfig(pluginItem[0], pluginItem[1]); + if (custom != null) { + custom.setAmount(amount); + custom.makeImmutable(); + ingredients.add(custom); + BCauldronRecipe.acceptedCustom.add(custom); + continue; + } else { + // TODO Maybe load later ie on first use of recipe? + P.p.errorLog(recipeId + ": Could not Find Plugin: " + ingredParts[1]); + return null; + } + } + + // Try to find this Ingredient as Custom Item + for (RecipeItem custom : BConfig.customItems) { + if (custom.getConfigId().equalsIgnoreCase(matParts[0])) { + custom = custom.getMutableCopy(); + custom.setAmount(amount); + custom.makeImmutable(); + ingredients.add(custom); + if (custom.hasMaterials()) { + BCauldronRecipe.acceptedMaterials.addAll(custom.getMaterials()); + } + // Add it as acceptedCustom + if (!BCauldronRecipe.acceptedCustom.contains(custom)) { + BCauldronRecipe.acceptedCustom.add(custom); + /*if (custom instanceof PluginItem || !custom.hasMaterials()) { + BCauldronRecipe.acceptedCustom.add(custom); + } else if (custom instanceof CustomMatchAnyItem) { + CustomMatchAnyItem ma = (CustomMatchAnyItem) custom; + if (ma.hasNames() || ma.hasLore()) { + BCauldronRecipe.acceptedCustom.add(ma); + } + }*/ + } + continue listLoop; + } + } + + Material mat = Material.matchMaterial(matParts[0]); + short durability = -1; + if (matParts.length == 2) { + durability = (short) P.p.parseInt(matParts[1]); + } + if (mat == null && BConfig.hasVault) { + try { + net.milkbowl.vault.item.ItemInfo vaultItem = net.milkbowl.vault.item.Items.itemByString(matParts[0]); + if (vaultItem != null) { + mat = vaultItem.getType(); + if (durability == -1 && vaultItem.getSubTypeId() != 0) { + durability = vaultItem.getSubTypeId(); + } + if (mat.name().contains("LEAVES")) { + if (durability > 3) { + durability -= 4; // Vault has leaves with higher durability + } + } + } + } catch (Exception e) { + P.p.errorLog("Could not check vault for Item Name"); + e.printStackTrace(); + } + } + if (mat != null) { + RecipeItem rItem; + if (durability > -1) { + rItem = new SimpleItem(mat, durability); + } else { + rItem = new SimpleItem(mat); + } + rItem.setAmount(amount); + rItem.makeImmutable(); + ingredients.add(rItem); + BCauldronRecipe.acceptedMaterials.add(mat); + BCauldronRecipe.acceptedSimple.add(mat); + } else { + P.p.errorLog(recipeId + ": Unknown Material: " + ingredParts[0]); + return null; + } + } + return ingredients; + } + + @Nullable + public static List> loadLore(ConfigurationSection cfg, String path) { + List load = BUtil.loadCfgStringList(cfg, path); + if (load != null) { + List> lore = new ArrayList<>(load.size()); + for (String line : load) { + line = P.p.color(line); + int plus = 0; + if (line.startsWith("+++")) { + plus = 3; + line = line.substring(3); + } else if (line.startsWith("++")) { + plus = 2; + line = line.substring(2); + } else if (line.startsWith("+")) { + plus = 1; + line = line.substring(1); + } + if (line.startsWith(" ")) { + line = line.substring(1); + } + if (!line.startsWith("§")) { + line = "§9" + line; + } + lore.add(new Tuple<>(plus, line)); + } + return lore; + } + return null; + } + + /** + * check every part of the recipe for validity. + */ + public boolean isValid() { + if (ingredients == null || ingredients.isEmpty()) { + P.p.errorLog("No ingredients could be loaded for Recipe: " + getRecipeName()); + return false; + } + if (cookingTime < 1) { + P.p.errorLog("Invalid cooking time '" + cookingTime + "' in Recipe: " + getRecipeName()); + return false; + } + if (distillruns < 0) { + P.p.errorLog("Invalid distillruns '" + distillruns + "' in Recipe: " + getRecipeName()); + return false; + } + if (distillTime < 0) { + P.p.errorLog("Invalid distilltime '" + distillTime + "' in Recipe: " + getRecipeName()); + return false; + } + if (wood < 0 || wood > 6) { + P.p.errorLog("Invalid wood type '" + wood + "' in Recipe: " + getRecipeName()); + return false; + } + if (age < 0) { + P.p.errorLog("Invalid age time '" + age + "' in Recipe: " + getRecipeName()); + return false; + } + if (difficulty < 0 || difficulty > 10) { + P.p.errorLog("Invalid difficulty '" + difficulty + "' in Recipe: " + getRecipeName()); + return false; + } + if (alcohol < 0) { + P.p.errorLog("Invalid alcohol '" + alcohol + "' in Recipe: " + getRecipeName()); + return false; + } + return true; + } + + /** + * allowed deviation to the recipes count of ingredients at the given difficulty + */ + public int allowedCountDiff(int count) { + if (count < 8) { + count = 8; + } + int allowedCountDiff = Math.round((float) ((11.0 - difficulty) * (count / 10.0))); + + if (allowedCountDiff == 0) { + return 1; + } + return allowedCountDiff; + } + + /** + * allowed deviation to the recipes cooking-time at the given difficulty + */ + public int allowedTimeDiff(int time) { + if (time < 8) { + time = 8; + } + int allowedTimeDiff = Math.round((float) ((11.0 - difficulty) * (time / 10.0))); + + if (allowedTimeDiff == 0) { + return 1; + } + return allowedTimeDiff; + } + + /** + * difference between given and recipe-wanted woodtype + */ + public float getWoodDiff(float wood) { + return Math.abs(wood - this.wood); + } + + public boolean isCookingOnly() { + return age == 0 && distillruns == 0; + } + + public boolean needsDistilling() { + return distillruns != 0; + } + + public boolean needsToAge() { + return age != 0; + } + + /** + * true if given list misses an ingredient + */ + public boolean isMissingIngredients(List list) { + if (list.size() < ingredients.size()) { + return true; + } + for (RecipeItem rItem : ingredients) { + boolean matches = false; + for (Ingredient used : list) { + if (rItem.matches(used)) { + matches = true; + break; + } + } + if (!matches) { + return true; + } + } + return false; + } + + public void applyDrinkFeatures(Player player) { + if (playercmds != null && !playercmds.isEmpty()) { + for (String cmd : playercmds) { + player.performCommand(cmd.replaceAll("%player_name%", player.getName())); + } + } + if (servercmds != null && !servercmds.isEmpty()) { + for (String cmd : servercmds) { + P.p.getServer().dispatchCommand(P.p.getServer().getConsoleSender(), cmd.replaceAll("%player_name%", player.getName())); + } + } + if (drinkMsg != null) { + player.sendMessage(drinkMsg.replaceAll("%player_name%", player.getName())); + } + if (drinkTitle != null) { + player.sendTitle("", drinkTitle.replaceAll("%player_name%", player.getName()), 10, 90, 30); + } + } + + /** + * Create a Potion from this Recipe with best values. + * Quality can be set, but will reset to 10 if unset immutable and put in a barrel + * + * @param quality The Quality of the Brew + * @return The Created Item + */ + public ItemStack create(int quality) { + return createBrew(quality).createItem(this); + } + + /** + * Create a Brew from this Recipe with best values. + * Quality can be set, but will reset to 10 if unset immutable and put in a barrel + * + * @param quality The Quality of the Brew + * @return The created Brew + */ + public Brew createBrew(int quality) { + List list = new ArrayList<>(ingredients.size()); + for (RecipeItem rItem : ingredients) { + Ingredient ing = rItem.toIngredientGeneric(); + ing.setAmount(rItem.getAmount()); + list.add(ing); + } + + BIngredients bIngredients = new BIngredients(list, cookingTime); + + return new Brew(bIngredients, quality, 0, distillruns, getAge(), wood, getRecipeName(), false, true, 0); + } + + public void updateAcceptedLists() { + for (RecipeItem ingredient : getIngredients()) { + if (ingredient.hasMaterials()) { + BCauldronRecipe.acceptedMaterials.addAll(ingredient.getMaterials()); + } + if (ingredient instanceof SimpleItem) { + BCauldronRecipe.acceptedSimple.add(((SimpleItem) ingredient).getMaterial()); + } else { + // Add it as acceptedCustom + if (!BCauldronRecipe.acceptedCustom.contains(ingredient)) { + BCauldronRecipe.acceptedCustom.add(ingredient); + } + } + } + } + + + // Getter + + /** + * how many of a specific ingredient in the recipe + */ + public int amountOf(Ingredient ing) { + for (RecipeItem rItem : ingredients) { + if (rItem.matches(ing)) { + return rItem.getAmount(); + } + } + return 0; + } + + /** + * how many of a specific ingredient in the recipe + */ + public int amountOf(ItemStack item) { + for (RecipeItem rItem : ingredients) { + if (rItem.matches(item)) { + return rItem.getAmount(); + } + } + return 0; + } + + /** + * Same as getName(5) + */ + public String getRecipeName() { + return getName(5); + } + + /** + * name that fits the quality + */ + public String getName(int quality) { + if (name.length > 2) { + if (quality <= 3) { + return name[0]; + } else if (quality <= 7) { + return name[1]; + } else { + return name[2]; + } + } else { + return name[0]; + } + } + + /** + * If one of the quality names equalIgnoreCase given name + */ + public boolean hasName(String name) { + for (String test : this.name) { + if (test.equalsIgnoreCase(name)) { + return true; + } + } + return false; + } + + public List getIngredients() { + return ingredients; + } + + public int getCookingTime() { + return cookingTime; + } + + public byte getDistillRuns() { + return distillruns; + } + + public int getDistillTime() { + return distillTime; + } + + @NotNull + public PotionColor getColor() { + return color; + } + + /** + * get the woodtype + */ + public byte getWood() { + return wood; + } + + public float getAge() { + return (float) age; + } + + public int getDifficulty() { + return difficulty; + } + + public int getAlcohol() { + return alcohol; + } + + public boolean hasLore() { + return lore != null && !lore.isEmpty(); + } + + @Nullable + public List> getLore() { + return lore; + } + + @Nullable + public List getLoreForQuality(int quality) { + if (lore == null) return null; + int plus; + if (quality <= 3) { + plus = 1; + } else if (quality <= 7) { + plus = 2; + } else { + plus = 3; + } + List list = new ArrayList<>(lore.size()); + for (Tuple line : lore) { + if (line.first() == 0 || line.first() == plus) { + list.add(line.second()); + } + } + return list; + } + + public List getPlayercmds() { + return playercmds; + } + + public List getServercmds() { + return servercmds; + } + + public String getDrinkMsg() { + return drinkMsg; + } + + public String getDrinkTitle() { + return drinkTitle; + } + + public List getEffects() { + return effects; + } + + public boolean isSaveInData() { + return saveInData; + } + + // Setters + + /** + * When Changing ingredients, Accepted Lists have to be updated in BCauldronRecipe + */ + public void setIngredients(List ingredients) { + this.ingredients = ingredients; + } + + public void setCookingTime(int cookingTime) { + this.cookingTime = cookingTime; + } + + public void setDistillruns(byte distillruns) { + this.distillruns = distillruns; + } + + public void setDistillTime(int distillTime) { + this.distillTime = distillTime; + } + + public void setWood(byte wood) { + this.wood = wood; + } + + public void setAge(int age) { + this.age = age; + } + + public void setColor(@NotNull PotionColor color) { + this.color = color; + } + + public void setDifficulty(int difficulty) { + this.difficulty = difficulty; + } + + public void setAlcohol(int alcohol) { + this.alcohol = alcohol; + } + + public void setLore(List> lore) { + this.lore = lore; + } + + public void setEffects(List effects) { + this.effects = effects; + } + + public void setSaveInData(boolean saveInData) { + throw new NotImplementedException(); + //this.saveInData = saveInData; + } + + + @Override + public String toString() { + return "BRecipe{" + getRecipeName() + '}'; + } + + /** + * Gets a Modifiable Sublist of the Recipes that are loaded by config. + *

Changes are directly reflected by the main list of all recipes + *
Changes to the main List of all recipes will make the reference to this sublist invalid + * + *

After adding or removing elements, BRecipe.numConfigRecipes MUST be updated! + */ + public static List getConfigRecipes() { + return recipes.subList(0, numConfigRecipes); + } + + /** + * Gets a Modifiable Sublist of the Recipes that are added by plugins. + *

Changes are directly reflected by the main list of all recipes + *
Changes to the main List of all recipes will make the reference to this sublist invalid + */ + public static List getAddedRecipes() { + return recipes.subList(numConfigRecipes, recipes.size()); + } + + /** + * Gets the main List of all recipes. + */ + public static List getAllRecipes() { + return recipes; + } + + public static BRecipe get(String name) { + for (BRecipe recipe : recipes) { + if (recipe.getRecipeName().equalsIgnoreCase(name)) { + return recipe; + } + } + return null; + } + + /*public static void saveAddedRecipes(ConfigurationSection cfg) { + int i = 0; + for (BRecipe recipe : getAddedRecipes()) { + if (recipe.isSaveInData()) { + cfg.set(i + ".name", recipe.name); + } + } + }*/ + + + /** + * Builder to easily create Recipes + */ + public static class Builder { + private BRecipe recipe; + + public Builder(String name) { + recipe = new BRecipe(name, PotionColor.WATER); + } + + public Builder(String... names) { + recipe = new BRecipe(names, PotionColor.WATER); + } + + + public Builder addIngredient(RecipeItem... item) { + Collections.addAll(recipe.ingredients, item); + return this; + } + + public Builder addIngredient(ItemStack... item) { + for (ItemStack i : item) { + CustomItem customItem = new CustomItem(i); + customItem.setAmount(i.getAmount()); + recipe.ingredients.add(customItem); + } + return this; + } + + public Builder difficulty(int difficulty) { + recipe.difficulty = difficulty; + return this; + } + + public Builder color(String colorString) { + recipe.color = PotionColor.fromString(colorString); + return this; + } + + public Builder color(PotionColor color) { + recipe.color = color; + return this; + } + + public Builder color(Color color) { + recipe.color = PotionColor.fromColor(color); + return this; + } + + public Builder cook(int cookTime) { + recipe.cookingTime = cookTime; + return this; + } + + public Builder distill(byte distillRuns, int distillTime) { + recipe.distillruns = distillRuns; + recipe.distillTime = distillTime; + return this; + } + + public Builder age(int age, byte wood) { + recipe.age = age; + recipe.wood = wood; + return this; + } + + public Builder alcohol(int alcohol) { + recipe.alcohol = alcohol; + return this; + } + + public Builder addLore(String line) { + return addLore(0, line); + } + + /** + * Add a Line of Lore + * + * @param quality 0 for any quality, 1: bad, 2: normal, 3: good + * @param line The Line for custom lore to add + * @return this + */ + public Builder addLore(int quality, String line) { + if (quality < 0 || quality > 3) { + throw new IllegalArgumentException("Lore Quality must be 0 - 3"); + } + if (recipe.lore == null) { + recipe.lore = new ArrayList<>(); + } + recipe.lore.add(new Tuple<>(quality, line)); + return this; + } + + /** + * Add Commands that are executed by the player on drinking + */ + public Builder addPlayerCmds(String... cmds) { + if (recipe.playercmds == null) { + recipe.playercmds = new ArrayList<>(cmds.length); + } + Collections.addAll(recipe.playercmds, cmds); + return this; + } + + /** + * Add Commands that are executed by the server on drinking + */ + public Builder addServerCmds(String... cmds) { + if (recipe.servercmds == null) { + recipe.servercmds = new ArrayList<>(cmds.length); + } + Collections.addAll(recipe.servercmds, cmds); + return this; + } + + /** + * Add Message that is sent to the player in chat when he drinks the brew + */ + public Builder drinkMsg(String msg) { + recipe.drinkMsg = msg; + return this; + } + + /** + * Add Message that is sent to the player as a small title when he drinks the brew + */ + public Builder drinkTitle(String title) { + recipe.drinkTitle = title; + return this; + } + + public Builder addEffects(BEffect... effects) { + Collections.addAll(recipe.effects, effects); + return this; + } + + public BRecipe get() { + if (recipe.name == null) { + throw new IllegalArgumentException("Recipe name is null"); + } + if (recipe.name.length != 1 && recipe.name.length != 3) { + throw new IllegalArgumentException("Recipe name neither 1 nor 3"); + } + if (BRecipe.get(recipe.getRecipeName()) != null) { + throw new IllegalArgumentException("Recipe with name " + recipe.getRecipeName() + " already exists"); + } + if (recipe.color == null) { + throw new IllegalArgumentException("Recipe has no color"); + } + if (recipe.ingredients == null || recipe.ingredients.isEmpty()) { + throw new IllegalArgumentException("Recipe has no ingredients"); + } + if (!recipe.isValid()) { + throw new IllegalArgumentException("Recipe has not valid"); + } + for (RecipeItem ingredient : recipe.ingredients) { + ingredient.makeImmutable(); + } + return recipe; + } + } +} diff --git a/src/com/dre/brewery/recipe/CustomItem.java b/src/com/dre/brewery/recipe/CustomItem.java new file mode 100644 index 0000000..9ec3d30 --- /dev/null +++ b/src/com/dre/brewery/recipe/CustomItem.java @@ -0,0 +1,291 @@ +package com.dre.brewery.recipe; + +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Minecraft Item with custon name and lore. + *

Mostly used for Custom Items of the Config, but also for general custom items + */ +public class CustomItem extends RecipeItem implements Ingredient { + + private Material mat; + private String name; + private List lore; + + public CustomItem() { + } + + public CustomItem(Material mat) { + this.mat = mat; + } + + public CustomItem(Material mat, String name, List lore) { + this.mat = mat; + this.name = name; + this.lore = lore; + } + + public CustomItem(ItemStack item) { + mat = item.getType(); + if (!item.hasItemMeta()) { + return; + } + ItemMeta itemMeta = item.getItemMeta(); + assert itemMeta != null; + if (itemMeta.hasDisplayName()) { + name = itemMeta.getDisplayName(); + } + if (itemMeta.hasLore()) { + lore = itemMeta.getLore(); + } + } + + @Override + public boolean hasMaterials() { + return mat != null; + } + + public boolean hasName() { + return name != null; + } + + public boolean hasLore() { + return lore != null && !lore.isEmpty(); + } + + @Override + public List getMaterials() { + List l = new ArrayList<>(1); + l.add(mat); + return l; + } + + @Nullable + public Material getMaterial() { + return mat; + } + + protected void setMat(Material mat) { + this.mat = mat; + } + + @Nullable + public String getName() { + return name; + } + + protected void setName(String name) { + this.name = name; + } + + @Nullable + public List getLore() { + return lore; + } + + protected void setLore(List lore) { + this.lore = lore; + } + + @NotNull + @Override + public Ingredient toIngredient(ItemStack forItem) { + return ((CustomItem) getMutableCopy()); + } + + @NotNull + @Override + public Ingredient toIngredientGeneric() { + return ((CustomItem) getMutableCopy()); + } + + @Override + public boolean matches(Ingredient ingredient) { + if (isSimilar(ingredient)) { + return true; + } + if (ingredient instanceof RecipeItem) { + RecipeItem rItem = ((RecipeItem) ingredient); + if (rItem instanceof SimpleItem) { + // If the recipe item is just a simple item, only match if we also only define material + // If this is a custom item with more info, we don't want to match a simple item + return hasMaterials() && !hasLore() && !hasName() && getMaterial() == ((SimpleItem) rItem).getMaterial(); + } else if (rItem instanceof CustomItem) { + // If the other is a CustomItem as well and not Similar to ours, it might have more data and we still match + CustomItem other = ((CustomItem) rItem); + if (mat == null || mat == other.mat) { + if (!hasName() || (other.name != null && name.equalsIgnoreCase(other.name))) { + return !hasLore() || lore == other.lore || (other.hasLore() && matchLore(other.lore)); + } + } + } + } + return false; + } + + @Override + public boolean matches(ItemStack item) { + if (mat != null) { + if (item.getType() != mat) { + return false; + } + } + if (name == null && !hasLore()) { + return true; + } + if (!item.hasItemMeta()) { + return false; + } + ItemMeta meta = item.getItemMeta(); + assert meta != null; + if (name != null) { + if (!meta.hasDisplayName() || !name.equalsIgnoreCase(meta.getDisplayName())) { + return false; + } + } + + if (hasLore()) { + if (!meta.hasLore()) { + return false; + } + return matchLore(meta.getLore()); + } + return true; + } + + /** + * If this item has lore that matches the given lore. + *

It matches if our lore is contained in the given lore consecutively, ignoring color of the given lore. + * + * @param usedLore The given lore to match + * @return True if the given lore contains our lore consecutively + */ + public boolean matchLore(List usedLore) { + if (lore == null) return true; + int lastIndex = 0; + boolean foundFirst = false; + for (String line : lore) { + do { + if (lastIndex == usedLore.size()) { + // There is more in lore than in usedLore, bad + return false; + } + String usedLine = usedLore.get(lastIndex); + if (line.equalsIgnoreCase(usedLine) || line.equalsIgnoreCase(ChatColor.stripColor(usedLine))) { + // If the line is correct, we have found our first and we want all consecutive lines to also equal + foundFirst = true; + } else if (foundFirst) { + // If a consecutive line is not equal, thats bad + return false; + } + lastIndex++; + // If we once found one correct line, iterate over 'lore' consecutively + } while (!foundFirst); + } + return true; + } + + // We don't compare id here + @Override + public boolean isSimilar(Ingredient item) { + if (this == item) { + return true; + } + if (item instanceof CustomItem) { + CustomItem ci = ((CustomItem) item); + return mat == ci.mat && Objects.equals(name, ci.name) && Objects.equals(lore, ci.lore); + } + return false; + } + + @Override + public boolean equals(Object obj) { + if (!super.equals(obj)) return false; + if (obj instanceof CustomItem) { + return isSimilar(((CustomItem) obj)); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), mat, name, lore); + } + + @Override + public String toString() { + return "CustomItem{" + + "id=" + getConfigId() + + ", mat=" + (mat != null ? mat.name().toLowerCase() : "null") + + ", name='" + name + '\'' + + ", loresize: " + (lore != null ? lore.size() : 0) + + '}'; + } + + @Override + public void saveTo(DataOutputStream out) throws IOException { + out.writeUTF("CI"); + if (mat != null) { + out.writeBoolean(true); + out.writeUTF(mat.name()); + } else { + out.writeBoolean(false); + } + if (name != null) { + out.writeBoolean(true); + out.writeUTF(name); + } else { + out.writeBoolean(false); + } + if (lore != null) { + short size = (short) Math.min(lore.size(), Short.MAX_VALUE); + out.writeShort(size); + for (int i = 0; i < size; i++) { + out.writeUTF(lore.get(i)); + } + } else { + out.writeShort(0); + } + } + + public static CustomItem loadFrom(ItemLoader loader) { + try { + DataInputStream in = loader.getInputStream(); + CustomItem item = new CustomItem(); + if (in.readBoolean()) { + item.mat = Material.getMaterial(in.readUTF()); + } + if (in.readBoolean()) { + item.name = in.readUTF(); + } + short size = in.readShort(); + if (size > 0) { + item.lore = new ArrayList<>(size); + for (short i = 0; i < size; i++) { + item.lore.add(in.readUTF()); + } + } + return item; + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + // Needs to be called at Server start + public static void registerItemLoader() { + Ingredient.registerForItemLoader("CI", CustomItem::loadFrom); + } +} diff --git a/src/com/dre/brewery/recipe/CustomMatchAnyItem.java b/src/com/dre/brewery/recipe/CustomMatchAnyItem.java new file mode 100644 index 0000000..99dc570 --- /dev/null +++ b/src/com/dre/brewery/recipe/CustomMatchAnyItem.java @@ -0,0 +1,231 @@ +package com.dre.brewery.recipe; + +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Custom Item that matches any one of the given info. + *

Does not implement Ingredient, as it can not directly be added to an ingredient + */ +public class CustomMatchAnyItem extends RecipeItem { + + private List materials; + private List names; + private List lore; + + + @Override + public boolean hasMaterials() { + return materials != null && !materials.isEmpty(); + } + + public boolean hasNames() { + return names != null && !names.isEmpty(); + } + + public boolean hasLore() { + return lore != null && !lore.isEmpty(); + } + + @Override + @Nullable + public List getMaterials() { + return materials; + } + + protected void setMaterials(List materials) { + this.materials = materials; + } + + @Nullable + public List getNames() { + return names; + } + + protected void setNames(List names) { + this.names = names; + } + + @Nullable + public List getLore() { + return lore; + } + + protected void setLore(List lore) { + this.lore = lore; + } + + @NotNull + @Override + public Ingredient toIngredient(ItemStack forItem) { + // We only use the one part of this item that actually matched the given item to add to ingredients + Material mat = getMaterialMatch(forItem); + if (mat != null) { + return new CustomItem(mat); + } + String name = getNameMatch(forItem); + if (name != null) { + return new CustomItem(null, name, null); + } + String l = getLoreMatch(forItem); + if (l != null) { + List lore = new ArrayList<>(1); + lore.add(l); + return new CustomItem(null, null, lore); + } + + // Shouldnt happen + return new SimpleItem(Material.GOLDEN_HOE); + } + + @NotNull + @Override + public Ingredient toIngredientGeneric() { + if (hasMaterials()) { + return new CustomItem(materials.get(0)); + } + if (hasNames()) { + return new CustomItem(null, names.get(0), null); + } + if (hasLore()) { + List l = new ArrayList<>(1); + l.add(lore.get(0)); + return new CustomItem(null, null, l); + } + + // Shouldnt happen + return new SimpleItem(Material.GOLDEN_HOE); + } + + public Material getMaterialMatch(ItemStack item) { + if (!hasMaterials()) return null; + + Material usedMat = item.getType(); + for (Material mat : materials) { + if (usedMat == mat) { + return mat; + } + } + return null; + } + + public String getNameMatch(ItemStack item) { + if (!item.hasItemMeta() || !hasNames()) { + return null; + } + ItemMeta meta = item.getItemMeta(); + assert meta != null; + if (meta.hasDisplayName()) { + return getNameMatch(meta.getDisplayName()); + } + return null; + } + + public String getNameMatch(String usedName) { + if (!hasNames()) return null; + + for (String name : names) { + if (name.equalsIgnoreCase(usedName)) { + return name; + } + } + return null; + } + + public String getLoreMatch(ItemStack item) { + if (!item.hasItemMeta() || !hasLore()) { + return null; + } + ItemMeta meta = item.getItemMeta(); + assert meta != null; + if (meta.hasLore()) { + return getLoreMatch(meta.getLore()); + } + return null; + } + + public String getLoreMatch(List usedLore) { + if (!hasLore()) return null; + + for (String line : this.lore) { + for (String usedLine : usedLore) { + if (line.equalsIgnoreCase(usedLine) || line.equalsIgnoreCase(ChatColor.stripColor(usedLine))) { + return line; + } + } + } + return null; + } + + @Override + public boolean matches(ItemStack item) { + if (getMaterialMatch(item) != null) { + return true; + } + if (getNameMatch(item) != null) { + return true; + } + return getLoreMatch(item) != null; + } + + @Override + public boolean matches(Ingredient ingredient) { + // Ingredient can not be CustomMatchAnyItem, so we don't need to/can't check for similarity. + if (ingredient instanceof CustomItem) { + // If the custom item has any of our data, we match + CustomItem ci = ((CustomItem) ingredient); + if (hasMaterials() && ci.hasMaterials()) { + if (materials.contains(ci.getMaterial())) { + return true; + } + } + if (hasNames() && ci.hasName()) { + if (getNameMatch(ci.getName()) != null) { + return true; + } + } + if (hasLore() && ci.hasLore()) { + return getLoreMatch(ci.getLore()) != null; + } + } else if (ingredient instanceof SimpleItem) { + // If we contain the Material of the Simple Item, we match + SimpleItem si = (SimpleItem) ingredient; + return hasMaterials() && materials.contains(si.getMaterial()); + } + return false; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + CustomMatchAnyItem that = (CustomMatchAnyItem) o; + return Objects.equals(materials, that.materials) && + Objects.equals(names, that.names) && + Objects.equals(lore, that.lore); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), materials, names, lore); + } + + @Override + public String toString() { + return "CustomMatchAnyItem{" + + "id=" + getConfigId() + + ", materials: " + (materials != null ? materials.size() : 0) + + ", names:" + (names != null ? names.size() : 0) + + ", loresize: " + (lore != null ? lore.size() : 0) + + '}'; + } +} diff --git a/src/com/dre/brewery/recipe/Ingredient.java b/src/com/dre/brewery/recipe/Ingredient.java new file mode 100644 index 0000000..3d27118 --- /dev/null +++ b/src/com/dre/brewery/recipe/Ingredient.java @@ -0,0 +1,87 @@ +package com.dre.brewery.recipe; + +import org.bukkit.inventory.ItemStack; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +/** + * Item used in a BIngredients, inside BCauldron or Brew, + * Represents the Items used as ingredients in the Brewing process. + *

Can be a copy of a recipe item + *

Will be saved and loaded with a DataStream + *

Each implementing class needs to register a static function as Item Loader + */ +public interface Ingredient { + + Map> LOADERS = new HashMap<>(); + + /** + * Register a Static function as function that takes an ItemLoader, containing a DataInputStream. + *

Using the Stream it constructs a corresponding Ingredient for the chosen SaveID + * + * @param saveID The SaveID should be a small identifier like "AB" + * @param loadFct The Static Function that loads the Item, i.e. + * public static AItem loadFrom(ItemLoader loader) + */ + static void registerForItemLoader(String saveID, Function loadFct) { + LOADERS.put(saveID, loadFct); + } + + /** + * Unregister the ItemLoader + * + * @param saveID the chosen SaveID + */ + static void unRegisterItemLoader(String saveID) { + LOADERS.remove(saveID); + } + + + /** + * Saves this Ingredient to the DataOutputStream. + *

The first data HAS to be storing the SaveID like: + * out.writeUTF("AB"); + *

Amount will be saved automatically and does not have to be saved here. + *

Saving is done to Brew or for BCauldron into data.yml + * + * @param out The outputstream to write to + * @throws IOException Any IOException + */ + void saveTo(DataOutputStream out) throws IOException; + + int getAmount(); + + void setAmount(int amount); + + /** + * Does this Ingredient match the given ItemStack + * + * @param item The given ItemStack to match + * @return true if all required data is contained on the item + */ + boolean matches(ItemStack item); + + /* + * Does this Item match the given RecipeItem. + *

An IngredientItem matches a RecipeItem if all required info of the RecipeItem are fulfilled on this IngredientItem + *

This does not imply that the same holds the other way round, as this item might have more info than needed + * + * @param recipeItem The recipeItem whose requirements need to be fulfilled + * @return True if this matches the required info of the recipeItem + */ + //boolean matches(RecipeItem recipeItem); + + /** + * The other Ingredient is Similar if it is equal except amount. + * + * @param item The item to check similarity with + * @return True if this is equal to item except for amount + */ + boolean isSimilar(Ingredient item); + + +} diff --git a/src/com/dre/brewery/recipe/ItemLoader.java b/src/com/dre/brewery/recipe/ItemLoader.java new file mode 100644 index 0000000..720a63e --- /dev/null +++ b/src/com/dre/brewery/recipe/ItemLoader.java @@ -0,0 +1,28 @@ +package com.dre.brewery.recipe; + +import java.io.DataInputStream; + +public class ItemLoader { + + private final int version; + private final DataInputStream in; + private final String saveID; + + public ItemLoader(int version, DataInputStream in, String saveID) { + this.version = version; + this.in = in; + this.saveID = saveID; + } + + public int getVersion() { + return version; + } + + public DataInputStream getInputStream() { + return in; + } + + public String getSaveID() { + return saveID; + } +} diff --git a/src/com/dre/brewery/recipe/PluginItem.java b/src/com/dre/brewery/recipe/PluginItem.java new file mode 100644 index 0000000..3a02a1a --- /dev/null +++ b/src/com/dre/brewery/recipe/PluginItem.java @@ -0,0 +1,213 @@ +package com.dre.brewery.recipe; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Supplier; + +/** + * An Item of a Recipe or as Ingredient in a Brew that corresponds to an item from another plugin. + *

See /integration/item for examples on how to extend this class. + *

This class stores items as name of the plugin and item id + */ +public abstract class PluginItem extends RecipeItem implements Ingredient { + + private static Map> constructors = new HashMap<>(); + + private String plugin; + private String itemId; + + /** + * New Empty PluginItem + */ + public PluginItem() { + } + + /** + * New PluginItem with both fields already set + * + * @param plugin The name of the Plugin + * @param itemId The ItemID + */ + public PluginItem(String plugin, String itemId) { + this.plugin = plugin; + this.itemId = itemId; + } + + + @Override + public boolean hasMaterials() { + return false; + } + + @Override + public List getMaterials() { + return null; + } + + public String getPlugin() { + return plugin; + } + + public String getItemId() { + return itemId; + } + + protected void setPlugin(String plugin) { + this.plugin = plugin; + } + + protected void setItemId(String itemId) { + this.itemId = itemId; + } + + /** + * Called after Loading this Plugin Item from Config, or (by default) from Ingredients. + *

Allows Override to define custom actions after an Item was constructed + */ + protected void onConstruct() { + } + + /** + * Does this PluginItem Match the other Ingredient. + *

By default it matches exactly when they are similar, i.e. also a PluginItem with same parameters + * + * @param ingredient The ingredient that needs to fulfill the requirements + * @return True if the ingredient matches the required info of this + */ + @Override + public boolean matches(Ingredient ingredient) { + return isSimilar(ingredient); + } + + @NotNull + @Override + public Ingredient toIngredient(ItemStack forItem) { + return ((PluginItem) getMutableCopy()); + } + + @NotNull + @Override + public Ingredient toIngredientGeneric() { + return ((PluginItem) getMutableCopy()); + } + + @Override + public boolean isSimilar(Ingredient item) { + if (item instanceof PluginItem) { + return Objects.equals(plugin, ((PluginItem) item).plugin) && Objects.equals(itemId, ((PluginItem) item).itemId); + } + return false; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + PluginItem item = (PluginItem) o; + return Objects.equals(plugin, item.plugin) && + Objects.equals(itemId, item.itemId); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), plugin, itemId); + } + + @Override + public void saveTo(DataOutputStream out) throws IOException { + out.writeUTF("PI"); + out.writeUTF(plugin); + out.writeUTF(itemId); + } + + /** + * Called when loading this Plugin Item from Ingredients (of a Brew). + *

The default loading is the same as loading from Config + * + * @param loader The ItemLoader from which to load the data, use loader.getInputStream() + * @return The constructed PluginItem + */ + public static PluginItem loadFrom(ItemLoader loader) { + try { + DataInputStream in = loader.getInputStream(); + String plugin = in.readUTF(); + String itemId = in.readUTF(); + PluginItem item = fromConfig(plugin, itemId); + if (item == null) { + // Plugin not found when loading from Item, use a generic PluginItem that never matches other items + item = new PluginItem(plugin, itemId) { + @Override + public boolean matches(ItemStack item) { + return false; + } + }; + } + return item; + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + /** + * Registers the chosen SaveID and the loading Method for loading from Brew or BCauldron. + *

Needs to be called at Server start. + */ + public static void registerItemLoader() { + Ingredient.registerForItemLoader("PI", PluginItem::loadFrom); + } + + + /** + * Called when loading trying to find a config defined Plugin Item. By default also when loading from ingredients + *

Will call a registered constructor matching the given plugin identifier + * + * @param plugin The Identifier of the Plugin used in the config + * @param itemId The Identifier of the Item belonging to this Plugin used in the config + * @return The Plugin Item if found, or null if there is no plugin for the given String + */ + @Nullable + public static PluginItem fromConfig(String plugin, String itemId) { + plugin = plugin.toLowerCase(); + if (constructors.containsKey(plugin)) { + PluginItem item = constructors.get(plugin).get(); + item.setPlugin(plugin); + item.setItemId(itemId); + item.onConstruct(); + return item; + } + return null; + } + + + /** + * This needs to be called at Server Start before Brewery loads its data. + *

When implementing this, put Brewery as softdepend in your plugin.yml! + *

Registers a Constructor that returns a new or cloned instance of a PluginItem + *
This Constructor will be called when loading a Plugin Item from Config or by default from ingredients + *
After the Constructor is called, the plugin and itemid will be set on the new instance + *

Finally the onConstruct is called. + * + * @param pluginId The ID to use in the config + * @param constructor The constructor i.e. YourPluginItem::new + */ + public static void registerForConfig(String pluginId, Supplier constructor) { + constructors.put(pluginId.toLowerCase(), constructor); + } + + public static void unRegisterForConfig(String pluginId) { + constructors.remove(pluginId.toLowerCase()); + } + +} diff --git a/src/com/dre/brewery/recipe/PotionColor.java b/src/com/dre/brewery/recipe/PotionColor.java new file mode 100644 index 0000000..59aac49 --- /dev/null +++ b/src/com/dre/brewery/recipe/PotionColor.java @@ -0,0 +1,109 @@ +package com.dre.brewery.recipe; + +import com.dre.brewery.P; +import org.bukkit.Color; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.PotionMeta; +import org.bukkit.potion.PotionData; +import org.bukkit.potion.PotionType; + +public class PotionColor { + public static final PotionColor PINK = new PotionColor(1, PotionType.REGEN, Color.FUCHSIA); + public static final PotionColor CYAN = new PotionColor(2, PotionType.SPEED, Color.AQUA); + public static final PotionColor ORANGE = new PotionColor(3, PotionType.FIRE_RESISTANCE, Color.ORANGE); + public static final PotionColor GREEN = new PotionColor(4, PotionType.POISON, Color.GREEN); + public static final PotionColor BRIGHT_RED = new PotionColor(5, PotionType.INSTANT_HEAL, Color.fromRGB(255,0,0)); + public static final PotionColor BLUE = new PotionColor(6, PotionType.NIGHT_VISION, Color.NAVY); + public static final PotionColor BLACK = new PotionColor(8, PotionType.WEAKNESS, Color.BLACK); + public static final PotionColor RED = new PotionColor(9, PotionType.STRENGTH, Color.fromRGB(196,0,0)); + public static final PotionColor GREY = new PotionColor(10, PotionType.SLOWNESS, Color.GRAY); + public static final PotionColor WATER = new PotionColor(11, P.use1_9 ? PotionType.WATER_BREATHING : null, Color.BLUE); + public static final PotionColor DARK_RED = new PotionColor(12, PotionType.INSTANT_DAMAGE, Color.fromRGB(128,0,0)); + public static final PotionColor BRIGHT_GREY = new PotionColor(14, PotionType.INVISIBILITY, Color.SILVER); + + private final int colorId; + private final PotionType type; + private final Color color; + + PotionColor(int colorId, PotionType type, Color color) { + this.colorId = colorId; + this.type = type; + this.color = color; + } + + public PotionColor(Color color) { + colorId = -1; + type = WATER.getType(); + this.color = color; + } + + // gets the Damage Value, that sets a color on the potion + // offset +32 is not accepted by brewer, so not further destillable + // Only for minecraft pre 1.9 + public short getColorId(boolean destillable) { + if (destillable) { + return (short) (colorId + 64); + } + return (short) (colorId + 32); + } + + public PotionType getType() { + return type; + } + + public Color getColor() { + return color; + } + + @SuppressWarnings("deprecation") + public void colorBrew(PotionMeta meta, ItemStack potion, boolean destillable) { + if (P.use1_9) { + // We need to Hide Potion Effects even in 1.12, as it would otherwise show "No Effects" + meta.addItemFlags(ItemFlag.HIDE_POTION_EFFECTS); + if (P.use1_11) { + // BasePotionData was only used for the Color, so starting with 1.12 we can use setColor instead + meta.setColor(getColor()); + } else { + meta.setBasePotionData(new PotionData(getType())); + } + } else { + potion.setDurability(getColorId(destillable)); + } + } + + public static PotionColor fromString(String string) { + switch (string) { + case "PINK": return PINK; + case "CYAN": return CYAN; + case "ORANGE": return ORANGE; + case "GREEN": return GREEN; + case "BRIGHT_RED": return BRIGHT_RED; + case "BLUE": return BLUE; + case "BLACK": return BLACK; + case "RED": return RED; + case "GREY": return GREY; + case "WATER": return WATER; + case "DARK_RED": return DARK_RED; + case "BRIGHT_GREY": return BRIGHT_GREY; + default: + try{ + if (string.length() >= 7) { + string = string.substring(1); + } + return new PotionColor(Color.fromRGB( + Integer.parseInt(string.substring( 0, 2 ), 16 ), + Integer.parseInt(string.substring( 2, 4 ), 16 ), + Integer.parseInt(string.substring( 4, 6 ), 16 ) + )); + } catch (Exception e) { + return WATER; + } + } + } + + public static PotionColor fromColor(Color color) { + return new PotionColor(color); + } + +} diff --git a/src/com/dre/brewery/recipe/RecipeItem.java b/src/com/dre/brewery/recipe/RecipeItem.java new file mode 100644 index 0000000..48bf510 --- /dev/null +++ b/src/com/dre/brewery/recipe/RecipeItem.java @@ -0,0 +1,294 @@ +package com.dre.brewery.recipe; + +import com.dre.brewery.P; +import com.dre.brewery.filedata.BConfig; +import com.dre.brewery.utility.BUtil; +import org.bukkit.Material; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * Item that can be used in a Recipe. + *

They are not necessarily only loaded from config + *

They are immutable if used in a recipe. If one implements Ingredient, + * it can be used as mutable copy directly in a + * BIngredients. Otherwise it needs to be converted to an Ingredient + */ +public abstract class RecipeItem implements Cloneable { + + private String cfgId; + private int amount; + private boolean immutable = false; + + + /** + * Does this RecipeItem match the given ItemStack? + *

Used to determine if the given item corresponds to this recipeitem + * + * @param item The ItemStack for comparison + * @return True if the given item matches this recipeItem + */ + public abstract boolean matches(ItemStack item); + + /** + * Does this Item match the given Ingredient? + *

A RecipeItem matches an Ingredient if all required info of the RecipeItem are fulfilled on the Ingredient + *
This does not imply that the same holds the other way round, as the ingredient item might have more info than needed + * + * + * @param ingredient The ingredient that needs to fulfill the requirements + * @return True if the ingredient matches the required info of this + */ + public abstract boolean matches(Ingredient ingredient); + + /** + * Get the Corresponding Ingredient Item. For Items implementing Ingredient, just getMutableCopy() + *

This is called when this recipe item is added to a BIngredients + * + * @param forItem The ItemStack that has previously matched this RecipeItem. Used if the resulting Ingredient needs more info from the ItemStack + * @return The IngredientItem corresponding to this RecipeItem + */ + @NotNull + public abstract Ingredient toIngredient(ItemStack forItem); + + /** + * Gets a Generic Ingredient for this recipe item + */ + @NotNull + public abstract Ingredient toIngredientGeneric(); + + /** + * @return True if this recipeItem has one or more materials that could classify an item. if true, getMaterials() is NotNull + */ + public abstract boolean hasMaterials(); + + /** + * @return List of one or more Materials this recipeItem uses. + */ + @Nullable + public abstract List getMaterials(); + + /** + * @return The Id this Item uses in the config in the custom-items section + */ + @Nullable + public String getConfigId() { + return cfgId; + } + + /** + * @return The Amount of this Item in a Recipe + */ + public int getAmount() { + return amount; + } + + /** + * Set the Amount of this Item in a Recipe. + *

The amount can not be set on an existing item in a recipe or existing custom item. + *
To change amount you need to use getMutableCopy() and change the amount on the copy + * + * @param amount The new amount + */ + public void setAmount(int amount) { + if (immutable) throw new IllegalStateException("Setting amount only possible on mutable copy"); + this.amount = amount; + } + + /** + * Makes this Item immutable, for example when loaded from config. Used so if this is added to BIngredients, + * it needs to be cloned before changing anything like amount + */ + public void makeImmutable() { + immutable = true; + } + + /** + * Gets a shallow clone of this RecipeItem whose fields like amount can be changed. + * + * @return A mutable copy of this + */ + public RecipeItem getMutableCopy() { + try { + RecipeItem i = (RecipeItem) super.clone(); + i.immutable = false; + return i; + } catch (CloneNotSupportedException e) { + throw new InternalError(e); + } + } + + /** + * Tries to find a matching RecipeItem for this item. It checks custom items and if it has found a unique custom item + * it will return that. If there are multiple matching custom items, a new CustomItem with all item info is returned. + *
If there is no matching CustomItem, it will return a SimpleItem with the items type + * + * @param item The Item for which to find a matching RecipeItem + * @param acceptAll If true it will accept any item and return a SimpleItem even if not on the accepted list + *
If false it will return null if the item is not acceptable by the Cauldron + * @return The Matched CustomItem, new CustomItem with all item info or SimpleItem + */ + @Nullable + @Contract("_, true -> !null") + public static RecipeItem getMatchingRecipeItem(ItemStack item, boolean acceptAll) { + RecipeItem rItem = null; + boolean multiMatch = false; + for (RecipeItem ri : BCauldronRecipe.acceptedCustom) { + // If we already have a multi match, only check if there is a PluginItem that matches more strictly + if (!multiMatch || (ri instanceof PluginItem)) { + if (ri.matches(item)) { + // If we match a plugin item, thats a very strict match, so immediately return it + if (ri instanceof PluginItem) { + return ri; + } + if (rItem == null) { + rItem = ri; + } else { + multiMatch = true; + } + } + } + } + if (multiMatch) { + // We have multiple Custom Items matching, so just store all item info + return new CustomItem(item); + } + if (rItem == null && (acceptAll || BCauldronRecipe.acceptedSimple.contains(item.getType()))) { + // No Custom item found + if (P.use1_13) { + return new SimpleItem(item.getType()); + } else { + //noinspection deprecation + return new SimpleItem(item.getType(), item.getDurability()); + } + } + return rItem; + } + + @Nullable + public static RecipeItem fromConfigCustom(ConfigurationSection cfg, String id) { + RecipeItem rItem; + if (cfg.getBoolean(id + ".matchAny", false)) { + rItem = new CustomMatchAnyItem(); + } else { + rItem = new CustomItem(); + } + + rItem.cfgId = id; + rItem.immutable = true; + + List materials; + List names; + List lore; + + List load = BUtil.loadCfgStringList(cfg, id + ".material"); + if (load != null && !load.isEmpty()) { + if ((materials = loadMaterials(load)) == null) { + return null; + } + } else { + materials = new ArrayList<>(0); + } + + load = BUtil.loadCfgStringList(cfg, id + ".name"); + if (load != null && !load.isEmpty()) { + names = load.stream().map(l -> P.p.color(l)).collect(Collectors.toList()); + if (P.use1_13) { + // In 1.13 trailing Color white is removed from display names + names = names.stream().map(l -> l.startsWith("§f") ? l.substring(2) : l).collect(Collectors.toList()); + } + } else { + names = new ArrayList<>(0); + } + + load = BUtil.loadCfgStringList(cfg, id + ".lore"); + if (load != null && !load.isEmpty()) { + lore = load.stream().map(l -> P.p.color(l)).collect(Collectors.toList()); + } else { + lore = new ArrayList<>(0); + } + + if (materials.isEmpty() && names.isEmpty() && lore.isEmpty()) { + P.p.errorLog("No Config Entries found for Custom Item"); + return null; + } + + if (rItem instanceof CustomItem) { + CustomItem cItem = ((CustomItem) rItem); + if (!materials.isEmpty()) { + cItem.setMat(materials.get(0)); + } + if (!names.isEmpty()) { + cItem.setName(names.get(0)); + } + cItem.setLore(lore); + } else { + CustomMatchAnyItem maItem = (CustomMatchAnyItem) rItem; + maItem.setMaterials(materials); + maItem.setNames(names); + maItem.setLore(lore); + } + + return rItem; + } + + @Nullable + protected static List loadMaterials(List ingredientsList) { + List materials = new ArrayList<>(ingredientsList.size()); + for (String item : ingredientsList) { + String[] ingredParts = item.split("/"); + if (ingredParts.length == 2) { + P.p.errorLog("Item Amount can not be specified for Custom Items: " + item); + return null; + } + Material mat = Material.matchMaterial(ingredParts[0]); + + if (mat == null && BConfig.hasVault) { + try { + net.milkbowl.vault.item.ItemInfo vaultItem = net.milkbowl.vault.item.Items.itemByString(ingredParts[0]); + if (vaultItem != null) { + mat = vaultItem.getType(); + } + } catch (Exception e) { + P.p.errorLog("Could not check vault for Item Name"); + e.printStackTrace(); + } + } + if (mat != null) { + materials.add(mat); + } else { + P.p.errorLog("Unknown Material: " + ingredParts[0]); + return null; + } + } + return materials; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof RecipeItem)) return false; + RecipeItem that = (RecipeItem) o; + return amount == that.amount && + immutable == that.immutable && + Objects.equals(cfgId, that.cfgId); + } + + @Override + public int hashCode() { + return Objects.hash(cfgId, amount, immutable); + } + + @Override + public String toString() { + return "RecipeItem{(" + getClass().getSimpleName() + ") ID: " + getConfigId() + " Materials: " + (hasMaterials() ? getMaterials().size() : 0) + " Amount: " + getAmount(); + } +} diff --git a/src/com/dre/brewery/recipe/SimpleItem.java b/src/com/dre/brewery/recipe/SimpleItem.java new file mode 100644 index 0000000..db3a7a4 --- /dev/null +++ b/src/com/dre/brewery/recipe/SimpleItem.java @@ -0,0 +1,151 @@ +package com.dre.brewery.recipe; + +import com.dre.brewery.P; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Simple Minecraft Item with just Material + */ +public class SimpleItem extends RecipeItem implements Ingredient { + + private Material mat; + private short dur; // Old Mc + + + public SimpleItem(Material mat) { + this(mat, (short) 0); + } + + public SimpleItem(Material mat, short dur) { + this.mat = mat; + this.dur = dur; + } + + @Override + public boolean hasMaterials() { + return mat != null; + } + + public Material getMaterial() { + return mat; + } + + @Override + public List getMaterials() { + List l = new ArrayList<>(1); + l.add(mat); + return l; + } + + @NotNull + @Override + public Ingredient toIngredient(ItemStack forItem) { + return ((SimpleItem) getMutableCopy()); + } + + @NotNull + @Override + public Ingredient toIngredientGeneric() { + return ((SimpleItem) getMutableCopy()); + } + + @Override + public boolean matches(ItemStack item) { + if (!mat.equals(item.getType())) { + return false; + } + //noinspection deprecation + return P.use1_13 || dur == item.getDurability(); + } + + @Override + public boolean matches(Ingredient ingredient) { + if (isSimilar(ingredient)) { + return true; + } + if (ingredient instanceof RecipeItem) { + if (!((RecipeItem) ingredient).hasMaterials()) { + return false; + } + if (ingredient instanceof CustomItem) { + // Only match if the Custom Item also only defines material + // If the custom item has more info like name and lore, it is not supposed to match a simple item + CustomItem ci = (CustomItem) ingredient; + return !ci.hasLore() && !ci.hasName() && mat == ci.getMaterial(); + } + } + return false; + } + + @Override + public boolean isSimilar(Ingredient item) { + if (this == item) { + return true; + } + if (item instanceof SimpleItem) { + SimpleItem si = ((SimpleItem) item); + return si.mat == mat && si.dur == dur; + } + return false; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + SimpleItem item = (SimpleItem) o; + return dur == item.dur && + mat == item.mat; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), mat, dur); + } + + @Override + public String toString() { + return "SimpleItem{" + + "mat=" + mat.name().toLowerCase() + + " amount=" + getAmount() + + '}'; + } + + @Override + public void saveTo(DataOutputStream out) throws IOException { + out.writeUTF("SI"); + out.writeUTF(mat.name()); + out.writeShort(dur); + } + + public static SimpleItem loadFrom(ItemLoader loader) { + try { + DataInputStream in = loader.getInputStream(); + Material mat = Material.getMaterial(in.readUTF()); + if (mat != null) { + SimpleItem item = new SimpleItem(mat, in.readShort()); + return item; + } + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + // Needs to be called at Server start + public static void registerItemLoader() { + Ingredient.registerForItemLoader("SI", SimpleItem::loadFrom); + } + +} + diff --git a/src/com/dre/brewery/utility/BUtil.java b/src/com/dre/brewery/utility/BUtil.java new file mode 100644 index 0000000..64c7563 --- /dev/null +++ b/src/com/dre/brewery/utility/BUtil.java @@ -0,0 +1,319 @@ +package com.dre.brewery.utility; + +import com.dre.brewery.BCauldron; +import com.dre.brewery.Barrel; +import com.dre.brewery.P; +import com.dre.brewery.api.events.barrel.BarrelDestroyEvent; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.entity.Player; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.ListIterator; +import java.util.UUID; + +public class BUtil { + + /* **************************************** */ + /* ********* ********* */ + /* ********* Bukkit Utils ********* */ + /* ********* ********* */ + /* **************************************** */ + + /** + * Check if the Chunk of a Block is loaded !without loading it in the process! + */ + public static boolean isChunkLoaded(Block block) { + return block.getWorld().isChunkLoaded(block.getX() >> 4, block.getZ() >> 4); + } + + public static String color(String msg) { + if (msg != null) { + msg = ChatColor.translateAlternateColorCodes('&', msg); + } + return msg; + } + + /** + * Returns either uuid or Name of player, depending on bukkit version + */ + public static String playerString(Player player) { + if (P.useUUID) { + return player.getUniqueId().toString(); + } else { + return player.getName(); + } + } + + /** + * returns the Player if online + */ + public static Player getPlayerfromString(String name) { + if (P.useUUID) { + try { + return Bukkit.getPlayer(UUID.fromString(name)); + } catch (Exception e) { + return Bukkit.getPlayerExact(name); + } + } + return Bukkit.getPlayerExact(name); + } + + /** + * Apply a Potion Effect, if player already has this effect, overwrite the existing effect. + * + * @param onlyIfStronger Optionally only overwrite if the new one is stronger, i.e. has higher level or longer duration + */ + public static void reapplyPotionEffect(Player player, PotionEffect effect, boolean onlyIfStronger) { + final PotionEffectType type = effect.getType(); + if (player.hasPotionEffect(type)) { + PotionEffect plEffect; + if (P.use1_11) { + plEffect = player.getPotionEffect(type); + } else { + plEffect = player.getActivePotionEffects().stream().filter(e -> e.getType().equals(type)).findAny().get(); + } + if (!onlyIfStronger || + plEffect.getAmplifier() < effect.getAmplifier() || + (plEffect.getAmplifier() == effect.getAmplifier() && plEffect.getDuration() < effect.getDuration())) { + player.removePotionEffect(type); + } else { + return; + } + } + effect.apply(player); + } + + /** + * Load A List of Strings from config, if found a single String, will convert to List + */ + @Nullable + public static List loadCfgStringList(ConfigurationSection cfg, String path) { + if (cfg.isString(path)) { + List list = new ArrayList<>(1); + list.add(cfg.getString(path)); + return list; + } else if (cfg.isList(path)) { + return cfg.getStringList(path); + } + return null; + } + + /** + * Load a String from config, if found a List, will return the first String + */ + @Nullable + public static String loadCfgString(ConfigurationSection cfg, String path) { + if (cfg.isString(path)) { + return cfg.getString(path); + } else if (cfg.isList(path)) { + List list = cfg.getStringList(path); + if (!list.isEmpty()) { + return list.get(0); + } + } + return null; + } + + /* **************************************** */ + /* ********* ********* */ + /* ********* String Utils ********* */ + /* ********* ********* */ + /* **************************************** */ + + /** + * Returns the Index of a String from the list that contains this substring + * + * @param list The List in which to search for a substring + * @param substring Part of the String to search for in each of list + */ + public static int indexOfSubstring(List list, String substring) { + if (list.isEmpty()) return -1; + for (int index = 0, size = list.size(); index < size; index++) { + String string = list.get(index); + if (string.contains(substring)) { + return index; + } + } + return -1; + } + + /** + * Returns the index of a String from the list that starts with 'lineStart', returns -1 if not found; + */ + public static int indexOfStart(List list, String lineStart) { + for (int i = 0, size = list.size(); i < size; i++) { + if (list.get(i).startsWith(lineStart)) { + return i; + } + } + return -1; + } + + /* **************************************** */ + /* ********* ********* */ + /* ********* Brewery Utils ********* */ + /* ********* ********* */ + /* **************************************** */ + + /** + * create empty World save Sections + */ + public static void createWorldSections(ConfigurationSection section) { + for (World world : P.p.getServer().getWorlds()) { + String worldName = world.getName(); + if (worldName.startsWith("DXL_")) { + worldName = getDxlName(worldName); + } else { + worldName = world.getUID().toString(); + } + section.createSection(worldName); + } + } + + /** + * Returns true if the Block can be destroyed by the Player or something else (null) + * + * @param player The Player that destroyed a Block, Null if no Player involved + * @return True if the Block can be destroyed + */ + public static boolean blockDestroy(Block block, Player player, BarrelDestroyEvent.Reason reason) { + Material type = block.getType(); + if (type == Material.CAULDRON) { + // will only remove when existing + BCauldron.remove(block); + return true; + + } else if (LegacyUtil.isFence(type)) { + // remove barrel and throw potions on the ground + Barrel barrel = Barrel.getBySpigot(block); + if (barrel != null) { + if (barrel.hasPermsDestroy(player, block, reason)) { + barrel.remove(null, player, true); + return true; + } else { + return false; + } + } + return true; + + } else if (LegacyUtil.isSign(type)) { + // remove small Barrels + Barrel barrel2 = Barrel.getBySpigot(block); + if (barrel2 != null) { + if (!barrel2.isLarge()) { + if (barrel2.hasPermsDestroy(player, block, reason)) { + barrel2.remove(null, player, true); + return true; + } else { + return false; + } + } else { + barrel2.getBody().destroySign(); + } + } + return true; + + } else if (LegacyUtil.isWoodPlanks(type) || LegacyUtil.isWoodStairs(type)){ + Barrel barrel3 = Barrel.getByWood(block); + if (barrel3 != null) { + if (barrel3.hasPermsDestroy(player, block, reason)) { + barrel3.remove(block, player, true); + } else { + return false; + } + } + } + return true; + } + + /* **************************************** */ + /* ********* ********* */ + /* ********* Other Utils ********* */ + /* ********* ********* */ + /* **************************************** */ + + /** + * prints a list of Strings at the specified page + * + * @param sender The CommandSender to send the Page to + */ + public static void list(CommandSender sender, ArrayList strings, int page) { + int pages = (int) Math.ceil(strings.size() / 7F); + if (page > pages || page < 1) { + page = 1; + } + + sender.sendMessage(color("&7-------------- &f" + P.p.languageReader.get("Etc_Page") + " &6" + page + "&f/&6" + pages + " &7--------------")); + + ListIterator iter = strings.listIterator((page - 1) * 7); + + for (int i = 0; i < 7; i++) { + if (iter.hasNext()) { + sender.sendMessage(color(iter.next())); + } else { + break; + } + } + } + + /** + * gets the Name of a DXL World + */ + public static String getDxlName(String worldName) { + File dungeonFolder = new File(worldName); + if (dungeonFolder.isDirectory()) { + for (File file : dungeonFolder.listFiles()) { + if (!file.isDirectory()) { + if (file.getName().startsWith(".id_")) { + return file.getName().substring(1).toLowerCase(); + } + } + } + } + return worldName; + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + public static void saveFile(InputStream in, File dest, String name, boolean overwrite) throws IOException { + if (in == null) return; + if (!dest.exists()) { + dest.mkdirs(); + } + File result = new File(dest, name); + if (result.exists()) { + if (overwrite) { + result.delete(); + } else { + return; + } + } + + OutputStream out = new FileOutputStream(result); + byte[] buffer = new byte[1024]; + + int length; + //copy the file content in bytes + while ((length = in.read(buffer)) > 0){ + out.write(buffer, 0, length); + } + + in.close(); + out.close(); + } + +} diff --git a/src/com/dre/brewery/utility/BoundingBox.java b/src/com/dre/brewery/utility/BoundingBox.java new file mode 100644 index 0000000..a9160b7 --- /dev/null +++ b/src/com/dre/brewery/utility/BoundingBox.java @@ -0,0 +1,59 @@ +package com.dre.brewery.utility; + +import org.bukkit.Location; +import org.bukkit.block.Block; + +public class BoundingBox { + + private final int x1, y1, z1, x2, y2, z2; + + public BoundingBox(int x1, int y1, int z1, int x2, int y2, int z2) { + this.x1 = Math.min(x1, x2); + this.y1 = Math.min(y1, y2); + this.z1 = Math.min(z1, z2); + this.x2 = Math.max(x2, x1); + this.y2 = Math.max(y2, y1); + this.z2 = Math.max(z2, z1); + } + + public boolean contains(int x, int y, int z) { + return (x >= x1 && x <= x2) && (y >= y1 && y <= y2) && (z >= z1 && z <= z2); + } + + public boolean contains(Location loc) { + return contains(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()); + } + + public boolean contains(Block block) { + return contains(block.getX(), block.getY(), block.getZ()); + } + + public int area() { + return (x2 - x1 + 1) * (y2 - y1 + 1) * (z2 - z1 + 1); + } + + public String serialize() { + return x1 + "," + y1 + "," + z1 + "," + x2 + "," + y2 + "," + z2; + } + + public static BoundingBox fromPoints(int[] locations) { + if (locations.length % 3 != 0) throw new IllegalArgumentException("Locations has to be pairs of three"); + + int length = locations.length / 3; + int minx = Integer.MAX_VALUE, + miny = Integer.MAX_VALUE, + minz = Integer.MAX_VALUE, + maxx = Integer.MIN_VALUE, + maxy = Integer.MIN_VALUE, + maxz = Integer.MIN_VALUE; + for (int i = 0; i < length; i++) { + minx = Math.min(locations[i], minx); + miny = Math.min(locations[i + 1], miny); + minz = Math.min(locations[i + 2], minz); + maxx = Math.max(locations[i], maxx); + maxy = Math.max(locations[i + 1], maxy); + maxz = Math.max(locations[i + 2], maxz); + } + return new BoundingBox(minx, miny, minz, maxx, maxy, maxz); + } +} diff --git a/src/com/dre/brewery/LegacyUtil.java b/src/com/dre/brewery/utility/LegacyUtil.java similarity index 72% rename from src/com/dre/brewery/LegacyUtil.java rename to src/com/dre/brewery/utility/LegacyUtil.java index 58129a3..33a09f1 100644 --- a/src/com/dre/brewery/LegacyUtil.java +++ b/src/com/dre/brewery/utility/LegacyUtil.java @@ -1,13 +1,11 @@ -package com.dre.brewery; +package com.dre.brewery.utility; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.TreeSpecies; -import org.bukkit.World; +import com.dre.brewery.P; +import org.bukkit.*; import org.bukkit.block.Block; import org.bukkit.block.data.BlockData; import org.bukkit.block.data.Levelled; +import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.material.Cauldron; import org.bukkit.material.MaterialData; import org.bukkit.material.Tree; @@ -15,8 +13,12 @@ import org.bukkit.material.Wood; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; +import java.util.EnumSet; +import java.util.Set; + +import static com.dre.brewery.BCauldron.EMPTY; +import static com.dre.brewery.BCauldron.FULL; +import static com.dre.brewery.BCauldron.SOME; @SuppressWarnings("JavaReflectionMemberAccess") public class LegacyUtil { @@ -25,6 +27,8 @@ public class LegacyUtil { private static Method GET_BLOCK_TYPE_ID_AT; private static Method SET_DATA; + public static boolean NewNbtVer; + static { // <= 1.12.2 methods // These will be rarely used @@ -38,7 +42,7 @@ public class LegacyUtil { } catch (ClassNotFoundException | NoSuchMethodException | SecurityException ignored) { } - List planks = new ArrayList<>(6); + Set planks = EnumSet.noneOf(Material.class); for (Material m : Material.values()) { if (m.name().endsWith("PLANKS")) { planks.add(m); @@ -46,7 +50,7 @@ public class LegacyUtil { } PLANKS = planks; - List fences = new ArrayList<>(7); + Set fences = EnumSet.noneOf(Material.class); for (Material m : Material.values()) { if (m.name().endsWith("FENCE")) { fences.add(m); @@ -64,8 +68,8 @@ public class LegacyUtil { public static final Material JUNGLE_STAIRS = get("JUNGLE_STAIRS", "JUNGLE_WOOD_STAIRS"); public static final Material ACACIA_STAIRS = get("ACACIA_STAIRS"); public static final Material DARK_OAK_STAIRS = get("DARK_OAK_STAIRS"); - public static final List PLANKS; - public static final List FENCES; + public static final Set PLANKS; + public static final Set FENCES; // Materials removed in 1.13 public static final Material STATIONARY_LAVA = get("STATIONARY_LAVA"); @@ -187,35 +191,39 @@ public class LegacyUtil { } } - // 0 = empty, 1 = something in, 2 = full + /** + * Get The Fill Level of a Cauldron Block, 0 = empty, 1 = something in, 2 = full + * + * @return 0 = empty, 1 = something in, 2 = full + */ public static byte getFillLevel(Block block) { if (block.getType() != Material.CAULDRON) { - return 0; + return EMPTY; } if (P.use1_13) { Levelled cauldron = ((Levelled) block.getBlockData()); if (cauldron.getLevel() == 0) { - return 0; + return EMPTY; } else if (cauldron.getLevel() == cauldron.getMaximumLevel()) { - return 2; + return FULL; } else { - return 1; + return SOME; } } else { Cauldron cauldron = (Cauldron) block.getState().getData(); if (cauldron.isEmpty()) { - return 0; + return EMPTY; } else if (cauldron.isFull()) { - return 2; + return FULL; } else { - return 1; + return SOME; } } } - /* + /** * only used to convert a very old Datafile or config from a very old version */ public static Material getMaterial(int id) { @@ -243,4 +251,57 @@ public class LegacyUtil { } } + /** + * MC 1.13 uses a different NBT API than the newer versions.. + * We decide here which to use, the new or the old + * + * @return true if we can use nbt at all + */ + public static boolean initNbt() { + try { + Class.forName("org.bukkit.persistence.PersistentDataContainer"); + NewNbtVer = true; + P.p.log("Using the NEW nbt api"); + return true; + } catch (ClassNotFoundException e) { + try { + Class.forName("org.bukkit.inventory.meta.tags.CustomItemTagContainer"); + NewNbtVer = false; + P.p.log("Using the OLD nbt api"); + return true; + } catch (ClassNotFoundException ex) { + NewNbtVer = false; + P.p.log("No nbt api found, using Lore Save System"); + return false; + } + } + } + + @SuppressWarnings("deprecation") + public static void writeBytesItem(byte[] bytes, ItemMeta meta, NamespacedKey key) { + if (NewNbtVer) { + meta.getPersistentDataContainer().set(key, org.bukkit.persistence.PersistentDataType.BYTE_ARRAY, bytes); + } else { + meta.getCustomTagContainer().setCustomTag(key, org.bukkit.inventory.meta.tags.ItemTagType.BYTE_ARRAY, bytes); + } + } + + @SuppressWarnings("deprecation") + public static byte[] readBytesItem(ItemMeta meta, NamespacedKey key) { + if (NewNbtVer) { + return meta.getPersistentDataContainer().get(key, org.bukkit.persistence.PersistentDataType.BYTE_ARRAY); + } else { + return meta.getCustomTagContainer().getCustomTag(key, org.bukkit.inventory.meta.tags.ItemTagType.BYTE_ARRAY); + } + } + + @SuppressWarnings("deprecation") + public static boolean hasBytesItem(ItemMeta meta, NamespacedKey key) { + if (NewNbtVer) { + return meta.getPersistentDataContainer().has(key, org.bukkit.persistence.PersistentDataType.BYTE_ARRAY); + } else { + return meta.getCustomTagContainer().hasCustomTag(key, org.bukkit.inventory.meta.tags.ItemTagType.BYTE_ARRAY); + } + } + } diff --git a/src/com/dre/brewery/utility/Tuple.java b/src/com/dre/brewery/utility/Tuple.java new file mode 100644 index 0000000..95ac019 --- /dev/null +++ b/src/com/dre/brewery/utility/Tuple.java @@ -0,0 +1,90 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ +package com.dre.brewery.utility; + +public class Tuple { + + /** + * The first value in the tuple + */ + private final A a; + + /** + * The second value in the tuple + */ + private final B b; + + public Tuple(A a, B b) { + this.a = a; + this.b = b; + } + + /** + * Gets the first value in the tuple + */ + public A first() { + return a; + } + + /** + * Gets the second value in the tuple + */ + public B second() { + return b; + } + + /** + * Gets the first value in the tuple, Synonym for first() + */ + public A a() { + return a; + } + + /** + * Gets the second value in the tuple, Synonym for second() + */ + public B b() { + return b; + } + + @Override + public boolean equals(Object object) { + if (!(object instanceof Tuple)) { + return false; + } + + Tuple tuple = (Tuple) object; + return tuple.a == a && tuple.b == b; + } + + @Override + public int hashCode() { + return a.hashCode() ^ b.hashCode(); + } + +} diff --git a/test/com/dre/brewery/RecipeTests.java b/test/com/dre/brewery/RecipeTests.java new file mode 100644 index 0000000..1edc82f --- /dev/null +++ b/test/com/dre/brewery/RecipeTests.java @@ -0,0 +1,406 @@ +package com.dre.brewery; + +import com.dre.brewery.api.BreweryApi; +import com.dre.brewery.recipe.*; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.ArrayList; +import java.util.List; + +public class RecipeTests { + + public static void testCauldronRecipe() { + ItemStack item = new ItemStack(Material.BARRIER); + ItemMeta itemMeta = item.getItemMeta(); + List l = new ArrayList<>(); + l.add("Eine Tür"); + l.add("§6Besonders gut geschützt"); + itemMeta.setLore(l); + itemMeta.setDisplayName("Mauer"); + item.setItemMeta(itemMeta); + + BRecipe recipe = BRecipe.get("Beispiel"); + int x = recipe.amountOf(item); + int y = recipe.amountOf(new ItemStack(Material.NETHER_BRICK)); + + + List list = new ArrayList<>(); + Ingredient ing = new SimpleItem(Material.DIAMOND_HOE); + ing.setAmount(3); + list.add(ing); + ing = new SimpleItem(Material.RED_MUSHROOM); + list.add(ing); + for (int i = 1; i < 20; i++) { + list.get(0).setAmount(i + 3); + list.get(1).setAmount(i); + BCauldronRecipe best = null; + float bestMatch = 0; + float match; + for (BCauldronRecipe r : BCauldronRecipe.recipes) { + match = r.getIngredientMatch(list); + if (match >= 10) { + P.p.debugLog("Found match 10 Recipe: " + r); + return; + } + if (match > bestMatch) { + best = r; + bestMatch = match; + } + } + P.p.debugLog("Found best for i:" + i + " " + best); + } + + item = new ItemStack(Material.BARRIER); + itemMeta = item.getItemMeta(); + l = new ArrayList<>(); + l.add("Eine Tür"); + l.add("§6Besonders gut geschützt"); + itemMeta.setLore(l); + itemMeta.setDisplayName("Mauer"); + item.setItemMeta(itemMeta); + + RecipeItem.getMatchingRecipeItem(item, false); + } + + public static void testCustomRecipe() { + BreweryApi.removeRecipe("Good Build"); + BRecipe recipe = BreweryApi.recipeBuilder("Bad Build", "Good Build", "Uber Build") + .color(PotionColor.PINK) + .addIngredient(new ItemStack(Material.FLOWER_POT)) + .alcohol(32) + .cook(3) + .difficulty(4) + .age(3, (byte) 0) + .get(); + BreweryApi.addRecipe(recipe, false); + + P.p.log(BRecipe.getConfigRecipes().size() + ""); + + BreweryApi.removeRecipe("Bier"); + + P.p.log(BRecipe.getConfigRecipes().size() + ""); + + BCauldronRecipe r = BreweryApi.cauldronRecipeBuilder("Cooler Trank") + .color(PotionColor.PINK) + .addIngredient(new SimpleItem(Material.FLOWER_POT)) + .addLore("Schmeckt nAcH TOn?!") + .get(); + BreweryApi.addCauldronRecipe(r, false); + } + + public static void onClick() { + /*try { + DataInputStream in = new DataInputStream(new Base91DecoderStream(new LoreLoadStream(potion))); + + brew.testLoad(in); + + *//*if (in.readByte() == 27 && in.skip(48) > 0) { + in.mark(100); + if (in.readUTF().equals("TESTHalloª∆Ω") && in.readInt() == 34834 && in.skip(4) > 0 && in.readLong() == Long.MAX_VALUE) { + in.reset(); + if (in.readUTF().equals("TESTHalloª∆Ω")) { + P.p.log("true"); + } else { + P.p.log("false3"); + } + } else { + P.p.log("false2"); + } + } else { + P.p.log("false1"); + }*//* + + in.close(); + } catch (IllegalArgumentException argExc) { + P.p.log("No Data in Lore"); + + try { + + DataOutputStream out = new DataOutputStream(new Base91EncoderStream(new LoreSaveStream(potion, 2))); + + brew.testStore(out); + + + *//*out.writeByte(27); + out.writeLong(1111); //skip + out.writeLong(1111); //skip + out.writeLong(1111); //skip + out.writeLong(1111); //skip + out.writeLong(1111); //skip + out.writeLong(1111); //skip + out.writeUTF("TESTHalloª∆Ω"); + out.writeInt(34834); + out.writeInt(6436); //skip + out.writeLong(Long.MAX_VALUE);*//* + + out.close(); + *//*StringBuilder b = new StringBuilder(); + for (char c : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!$%&()*+,-./:;<=>?@[]^_`{|}~\"".toCharArray()) { + b.append('§').append(c); + } + List lore = potion.getLore(); + lore.add(b.toString()); + potion.setLore(lore);*//* + item.setItemMeta(potion); + + } catch (IOException h) { + h.printStackTrace(); + } + + } catch (IOException e) { + e.printStackTrace(); + }*/ + } + + public static void onLoad() { + //P.p.log("§" + (use1_9 ? "a":"c") + "1.9 " + "§" + (use1_11 ? "a":"c") + "1.11 " + "§" + (use1_13 ? "a":"c") + "1.13 " + "§" + (use1_14 ? "a":"c") + "1.14"); + + /*long master = new SecureRandom().nextLong(); + ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); + XORScrambleStream scramble = new XORScrambleStream(new Base91EncoderStream(byteStream), master); + DataOutputStream data = new DataOutputStream(scramble); + DataInputStream dataIn = null; + try { + scramble.start(); + data.writeLong(12345L); + scramble.stop(); + data.writeInt(1); + data.writeInt(1); + scramble.start(); + data.writeDouble(0.55555D); + data.writeInt(234323); + //data.writeUTF("Hallo Peter"); + data.writeLong(5419L); // Skip + data.writeDouble(0.55555D); + + data.close(); + + XORUnscrambleStream unscramble = new XORUnscrambleStream(new Base91DecoderStream(new ByteArrayInputStream(byteStream.toByteArray())), master); + dataIn = new DataInputStream(unscramble); + unscramble.start(); + P.p.log(dataIn.readLong() + ""); + unscramble.stop(); + P.p.log(dataIn.readInt() + ""); + P.p.log(dataIn.readInt() + ""); + unscramble.start(); + P.p.log(dataIn.readDouble() + ""); + dataIn.mark(1000); + P.p.log(dataIn.readInt() + ""); + //P.p.log(dataIn.readUTF()); + dataIn.skip(8); + P.p.log(dataIn.readDouble() + ""); + P.p.log("reset"); + dataIn.reset(); + P.p.log(dataIn.readInt() + ""); + //P.p.log(dataIn.readUTF()); + dataIn.skip(8); + P.p.log(dataIn.readDouble() + ""); + + dataIn.close(); + + *//*for (int i = 0; i < 10; i++) { + byteStream = new ByteArrayOutputStream(); + scramble = new XORScrambleStream(new Base91EncoderStream(byteStream)); + data = new DataOutputStream(scramble); + data.writeInt(i); + scramble.start(); + data.writeLong(12345L); + data.writeLong(12345L); + scramble.stop(); + data.writeInt(1); + data.writeInt(1); + scramble.start(); + data.writeInt(234323); + data.writeDouble(0.55555D); + + P.p.log(byteStream.toString()); + data.close(); + }*//* + + + long time = System.currentTimeMillis(); + for (int i = 0; i < 100000; i++) { + unscramble = new XORUnscrambleStream(new Base91DecoderStream(new ByteArrayInputStream(byteStream.toByteArray())), master); + dataIn = new DataInputStream(unscramble); + unscramble.start(); + dataIn.readLong(); + unscramble.stop(); + dataIn.readInt(); + dataIn.readInt(); + unscramble.start(); + dataIn.readDouble(); + dataIn.mark(1000); + dataIn.readInt(); + //dataIn.readUTF(); + dataIn.skip(8); + dataIn.readDouble(); + dataIn.reset(); + dataIn.readInt(); + //dataIn.readUTF(); + dataIn.skip(8); + dataIn.readDouble(); + + dataIn.close(); + } + long time2 = System.currentTimeMillis(); + + for (int i = 0; i < 100000; i++) { + unscramble = new XORUnscrambleStream(new ByteArrayInputStream(byteStream.toByteArray()), master); + dataIn = new DataInputStream(unscramble); + unscramble.start(); + dataIn.skip(2); + dataIn.readLong(); + unscramble.stop(); + dataIn.readInt(); + dataIn.readInt(); + unscramble.start(); + dataIn.readDouble(); + dataIn.mark(1000); + dataIn.readInt(); + //dataIn.readUTF(); + dataIn.skip(8); + dataIn.readDouble(); + dataIn.reset(); + dataIn.readInt(); + //dataIn.readUTF(); + dataIn.skip(8); + dataIn.readDouble(); + + dataIn.close(); + } + long time3 = System.currentTimeMillis(); + + P.p.log("Time with base91: " + (time2 - time)); + P.p.log("Time without base91: " + (time3 - time2)); + + } catch (IOException e) { + e.printStackTrace(); + } catch (InvalidKeyException e) { + e.printStackTrace(); + } finally { + try { + data.close(); + if (dataIn != null) { + dataIn.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + }*/ + + /*try { + ItemMeta meta = new ItemStack(Material.POTION).getItemMeta(); + DataOutputStream data = new DataOutputStream(new Base91EncoderStream(new LoreSaveStream(meta, 3))); + + data.writeInt(2); + data.writeLong(5); + + byte[] test = new byte[128]; + test[1] = 6; + test[2] = 12; + test[3] = 21; + test[127] = 99; + data.write(test); + + data.writeInt(123324); + data.writeLong(12343843); + + data.close(); + meta.getLore(); + + DataInputStream dataIn = new DataInputStream(new Base91DecoderStream(new LoreLoadStream(meta))); + + P.p.log(dataIn.readInt() + ", " + dataIn.readLong() + ", "); + + byte[] testIn = new byte[128]; + dataIn.read(testIn); + P.p.log(testIn[1] + ", " + testIn[2] + ", " + testIn[3] + ", " + testIn[127]); + + P.p.log(dataIn.readInt() + ", " + dataIn.readLong() + ", "); + + dataIn.close(); + + + + basE91 basE91 = new basE91(); + int[] input = new int[] {12, 65, 324, 5, 12, 129459, 1234567, Integer.MIN_VALUE, Integer.MAX_VALUE}; + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + DataOutputStream data = new DataOutputStream(stream); + for (int i = 0; i < input.length; i++) { + data.writeInt(input[i]); + } + data.flush(); + data.close(); + byte[] in = stream.toByteArray(); + byte[] out = new byte[4096]; + int lenght = basE91.encode(in, in.length, out); + basE91.encEnd(out); + String done = new String(out, 0, lenght); + + byte[] tin = done.getBytes(); + + byte[] tout = new byte[4096]; + lenght = basE91.decode(tin, tin.length, tout); + basE91.decEnd(tout); + + + ByteArrayInputStream tstream = new ByteArrayInputStream(tout, 0, lenght); + DataInputStream tdata = new DataInputStream(tstream); + int[] test = new int[4096]; + for (int j = 0; j < 6; j++) { + if (tstream.available() <= 0) break; + test[j] = tdata.readInt(); + + } + tdata.close(); + test = test;*/ + + + + /*basE91 basE91 = new basE91(); + int[] input = new int[] {12, 65, 324, 5, 12, 129459, 1234567, Integer.MIN_VALUE, Integer.MAX_VALUE}; + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + DataOutputStream data = new DataOutputStream(stream); + for (int i = 0; i < input.length; i++) { + data.writeInt(input[i]); + } + data.flush(); + data.close(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ByteArrayInputStream in = new ByteArrayInputStream(stream.toByteArray()); + + encode(in, out, in.available()); + + in.close(); + out.flush(); + out.close(); + + String done = new String(out.toByteArray()); + + ByteArrayInputStream tin = new ByteArrayInputStream(done.getBytes()); + ByteArrayOutputStream tout = new ByteArrayOutputStream(); + + decode(tin, tout, tin.available()); + + tin.close(); + tout.flush(); + tout.close(); + + ByteArrayInputStream tstream = new ByteArrayInputStream(tout.toByteArray()); + DataInputStream tdata = new DataInputStream(tstream); + int[] test = new int[4096]; + for (int j = 0; j < 9; j++) { + if (tstream.available() <= 0) break; + test[j] = tdata.readInt(); + + } + tdata.close(); + test = test; + + } catch (IOException e) { + e.printStackTrace(); + }*/ + } +}