elementList;
+
+ /**
+ * This variable holds current pageIndex for multi-page island choosing.
+ */
+ private int pageIndex;
+}
diff --git a/src/main/java/world/bentobox/bentobox/util/ItemParser.java b/src/main/java/world/bentobox/bentobox/util/ItemParser.java
index f483c3d2e..c315aa897 100644
--- a/src/main/java/world/bentobox/bentobox/util/ItemParser.java
+++ b/src/main/java/world/bentobox/bentobox/util/ItemParser.java
@@ -1,16 +1,14 @@
package world.bentobox.bentobox.util;
-import java.lang.reflect.Field;
-import java.util.Arrays;
-import java.util.MissingFormatArgumentException;
-import java.util.Optional;
-import java.util.UUID;
+import java.net.URL;
+import java.util.*;
import org.bukkit.Bukkit;
import org.bukkit.DyeColor;
import org.bukkit.Material;
import org.bukkit.block.banner.Pattern;
import org.bukkit.block.banner.PatternType;
+import org.bukkit.inventory.ItemFactory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BannerMeta;
import org.bukkit.inventory.meta.Damageable;
@@ -19,10 +17,12 @@
import org.bukkit.inventory.meta.SkullMeta;
import org.bukkit.potion.PotionData;
import org.bukkit.potion.PotionType;
+import org.bukkit.profile.PlayerProfile;
import org.eclipse.jdt.annotation.Nullable;
-import com.mojang.authlib.GameProfile;
-import com.mojang.authlib.properties.Property;
+import com.google.common.base.Enums;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
import world.bentobox.bentobox.BentoBox;
@@ -55,6 +55,33 @@ public static ItemStack parse(String text) {
*/
@Nullable
public static ItemStack parse(@Nullable String text, @Nullable ItemStack defaultItemStack) {
+ if (text == null || text.isBlank()) {
+ // Text does not exist or is empty.
+ return defaultItemStack;
+ }
+
+ ItemStack returnValue;
+
+ try {
+ // Check if item can be parsed using bukkit item factory.
+ returnValue = Bukkit.getItemFactory().createItemStack(text);
+ }
+ catch (IllegalArgumentException exception) {
+ returnValue = ItemParser.parseOld(text, defaultItemStack);
+ }
+
+ return returnValue;
+ }
+
+
+ /**
+ * Parse given string to ItemStack.
+ * @param text String value of item stack.
+ * @param defaultItemStack Material that should be returned if parsing failed.
+ * @return ItemStack of parsed item or defaultItemStack.
+ */
+ @Nullable
+ private static ItemStack parseOld(@Nullable String text, @Nullable ItemStack defaultItemStack) {
if (text == null || text.isBlank()) {
// Text does not exist or is empty.
@@ -113,22 +140,25 @@ else if (part.length == 2) {
returnValue = parseItemDurabilityAndQuantity(part);
}
- if (returnValue != null
- // If wrapper is just for code-style null-pointer checks.
- && customModelData != null) {
- return customValue(returnValue, customModelData);
+ // Update item meta with custom data model.
+ if (returnValue != null && customModelData != null) {
+ ItemParser.setCustomModelData(returnValue, customModelData);
}
-
} catch (Exception exception) {
BentoBox.getInstance().logError("Could not parse item " + text + " " + exception.getLocalizedMessage());
returnValue = defaultItemStack;
}
- return returnValue;
+ return returnValue;
}
- private static @Nullable ItemStack customValue(ItemStack returnValue, Integer customModelData) {
+ /**
+ * This method assigns custom model data to the item stack.
+ * @param returnValue Item stack that should be updated.
+ * @param customModelData Integer value of custom model data.
+ */
+ private static void setCustomModelData(ItemStack returnValue, Integer customModelData) {
// We have custom data model. Now assign it to the item-stack.
ItemMeta itemMeta = returnValue.getItemMeta();
@@ -138,8 +168,9 @@ else if (part.length == 2) {
// Update meta to the return item.
returnValue.setItemMeta(itemMeta);
}
- return null;
}
+
+
/**
* This method parses array of 2 items into an item stack.
* First array element is material, while second array element is integer, that represents item count.
@@ -197,8 +228,10 @@ private static ItemStack parseItemDurabilityAndQuantity(String[] part) {
* }
* @param part String array that contains 6 elements.
* @return Potion with given properties.
+ * @deprecated due to the spigot potion changes.
*/
- private static ItemStack parsePotion(String[] part) {
+ @Deprecated
+ private static ItemStack parsePotionOld(String[] part) {
if (part.length != 6) {
throw new MissingFormatArgumentException("Potion parsing requires 6 parts.");
}
@@ -235,6 +268,61 @@ private static ItemStack parsePotion(String[] part) {
}
+ /**
+ * This method parses array of 6 items into an item stack.
+ * Format:
+ * {@code
+ * POTION::QTY
+ * }
+ * Example:
+ * {@code
+ * POTION:STRENGTH:1
+ * }
+ * @link Potion Type
+ * @param part String array that contains 3 elements.
+ * @return Potion with given properties.
+ */
+ private static ItemStack parsePotion(String[] part) {
+ if (part.length == 6) {
+ BentoBox.getInstance().logWarning("The old potion parsing detected for " + part[0] +
+ ". Please update your configs, as SPIGOT changed potion types.");
+ return parsePotionOld(part);
+ }
+
+ if (part.length != 3) {
+ throw new MissingFormatArgumentException("Potion parsing requires 3 parts.");
+ }
+
+ /*
+ # Format POTION::QTY
+ # Potion Type can be found out in: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/potion/PotionType.html
+ # Examples:
+ # POTION:STRENGTH:1
+ # POTION:INSTANT_DAMAGE:2
+ # POTION:JUMP:1
+ # POTION:WEAKNESS:1 - any weakness potion
+ */
+
+ Material material = Material.matchMaterial(part[0]);
+
+ if (material == null) {
+ BentoBox.getInstance().logWarning("Could not parse potion item " + part[0] + " so using a regular potion.");
+ material = Material.POTION;
+ }
+
+ ItemStack result = new ItemStack(material, Integer.parseInt(part[2]));
+
+ if (result.getItemMeta() instanceof PotionMeta meta) {
+ PotionType potionType = Enums.getIfPresent(PotionType.class, part[1].toUpperCase(Locale.ENGLISH)).
+ or(PotionType.WATER);
+ meta.setBasePotionType(potionType);
+ result.setItemMeta(meta);
+ }
+
+ return result;
+ }
+
+
/**
* This method parses array of multiple elements for the Banner.
* @param part String array that contains at least 2 elements.
@@ -298,39 +386,62 @@ private static ItemStack parsePlayerHead(String[] part) {
// Set correct Skull texture
try {
- SkullMeta meta = (SkullMeta) playerHead.getItemMeta();
-
- if (part[1].length() < 17) {
- // Minecraft player names are in length between 3 and 16 chars.
- meta.setOwner(part[1]);
- } else if (part[1].length() == 32) {
- // trimmed UUID length are 32 chars.
- meta.setOwningPlayer(Bukkit.getOfflinePlayer(
- UUID.fromString(part[1].replaceAll("(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})", "$1-$2-$3-$4-$5"))));
- } else if (part[1].length() == 36) {
- // full UUID length are 36 chars.
- meta.setOwningPlayer(Bukkit.getOfflinePlayer(UUID.fromString(part[1])));
- } else {
- // If chars are more than 36, apparently it is base64 encoded texture.
- GameProfile profile = new GameProfile(UUID.randomUUID(), "");
- profile.getProperties().put("textures", new Property("textures", part[1]));
-
- // Null pointer will be caught and ignored.
- Field profileField = meta.getClass().getDeclaredField("profile");
- profileField.setAccessible(true);
- profileField.set(meta, profile);
- }
+ if (playerHead.getItemMeta() instanceof SkullMeta meta)
+ {
+ PlayerProfile profile;
+
+ if (part[1].length() < 17) {
+ // Minecraft player names are in length between 3 and 16 chars.
+ profile = Bukkit.createPlayerProfile(part[1]);
+ } else if (part[1].length() == 32) {
+ // trimmed UUID length are 32 chars.
+ profile = Bukkit.createPlayerProfile(UUID.fromString(part[1].replaceAll("(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})", "$1-$2-$3-$4-$5")));
+ } else if (part[1].length() == 36) {
+ // full UUID length are 36 chars.
+ profile = Bukkit.createPlayerProfile(UUID.fromString(part[1]));
+ } else {
+ // If chars are more than 36, apparently it is base64 encoded texture.
+ profile = Bukkit.createPlayerProfile(UUID.randomUUID(), "");
+ profile.getTextures().setSkin(ItemParser.getSkinURLFromBase64(part[1]));
+ }
- // Apply new meta to the item.
- playerHead.setItemMeta(meta);
+ // Apply item meta.
+ meta.setOwnerProfile(profile);
+ playerHead.setItemMeta(meta);
+ }
} catch (Exception ignored) {
- // Ignored
+ // Could not parse player head.
+ BentoBox.getInstance().logError("Could not parse player head item " + part[1] + " so using a Steve head.");
}
return playerHead;
}
+ /**
+ * This method parses base64 encoded string into URL.
+ * @param base64 Base64 encoded string.
+ * @return URL of the skin.
+ */
+ private static URL getSkinURLFromBase64(String base64) {
+ /*
+ * Base64 encoded string is in format: { "timestamp": 0, "profileId": "UUID",
+ * "profileName": "USERNAME", "textures": { "SKIN": { "url":
+ * "https://textures.minecraft.net/texture/TEXTURE_ID" }, "CAPE": { "url":
+ * "https://textures.minecraft.net/texture/TEXTURE_ID" } } }
+ */
+ try {
+ String decoded = new String(Base64.getDecoder().decode(base64));
+ JsonObject json = new Gson().fromJson(decoded, JsonObject.class);
+ String url = json.getAsJsonObject("textures").getAsJsonObject("SKIN").get("url").getAsString();
+ return new URL(url);
+ }
+ catch (Exception e) {
+ return null;
+ }
+ }
+
+
/**
* Check if given sting is an integer.
* @param string Value that must be checked.
diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml
index 51e4ea18b..3cc79aa1d 100644
--- a/src/main/resources/locales/en-US.yml
+++ b/src/main/resources/locales/en-US.yml
@@ -1622,15 +1622,6 @@ protection:
setting-active: '&a Active'
setting-disabled: '&c Disabled'
-language:
- panel-title: Select your language
- description:
- selected: '&a Currently selected.'
- click-to-select: '&e Click &a to select.'
- authors: '&a Authors:'
- author: '&3 - &b [name]'
- edited: '&a Changed your language to &e [lang]&a .'
-
management:
panel:
title: BentoBox Management
@@ -1788,6 +1779,51 @@ panel:
&a Allow BentoBox to connect to GitHub in
&a the configuration or try again later.
+
+# This section contains values for BentoBox panels.
+panels:
+ # The section of translations used in Island Creation Panel
+ island_creation:
+ title: "&2&l Pick an island"
+ buttons:
+ # This button is used for displaying blueprint bundle in the island creation panel.
+ bundle:
+ name: "&l [name]"
+ description: |-
+ [description]
+ # The section of translations used in Language Panel
+ language:
+ title: "&2&l Select your language"
+ buttons:
+ # This button is used for displaying different locales that are available in language selection panel.
+ language:
+ name: "&f&l [name]"
+ description: |-
+ [authors]
+ |[selected]
+ authors: "&7 Authors: "
+ author: "&7 - &b [name]"
+ selected: "&a Currently selected."
+ # The set of common buttons used in multiple panels.
+ buttons:
+ # Button that is used in multi-page GUIs which allows to return to previous page.
+ previous:
+ name: "&f&l Previous Page"
+ description: |-
+ &7 Switch to [number] page
+ # Button that is used in multi-page GUIs which allows to go to next page.
+ next:
+ name: "&f&l Next Page"
+ description: |-
+ &7 Switch to [number] page
+ tips:
+ click-to-next: "&e Click &7 for next."
+ click-to-previous: "&e Click &7 for previous."
+ click-to-choose: "&e Click &7 to select."
+ click-to-toggle: "&e Click &7 to toggle."
+ left-click-to-cycle-down: "&e Left Click &7 to cycle downwards."
+ right-click-to-cycle-up: "&e Right Click &7 to cycle upwards."
+
successfully-loaded: |2
&6 ____ _ ____
diff --git a/src/main/resources/panels/island_creation_panel.yml b/src/main/resources/panels/island_creation_panel.yml
new file mode 100644
index 000000000..f58940e29
--- /dev/null
+++ b/src/main/resources/panels/island_creation_panel.yml
@@ -0,0 +1,71 @@
+# This is default island creation panel. It is used in all situations when gamemode addon does not have specified their
+# of panel.
+island_creation_panel:
+ title: panels.island_creation.title # The title of panel or link to the localization location.
+ type: INVENTORY # The type of inventory: INVENTORY, DROPPER, HOPPER
+ background: # The item that will be displayed in empty spots. This section can be removed.
+ icon: BLACK_STAINED_GLASS_PANE # The icon of background item
+ title: "&b&r" # Empty text # The text of background item
+ border: # The item that will be displayed around the inventory. This section can be removed.
+ icon: BLACK_STAINED_GLASS_PANE # The icon of background item
+ title: "&b&r" # Empty text # The text of background item
+ force-shown: [] # Allow to specify (1-6, 1-3, 1) which rows must be showed regardless of empty elements.
+ content: # Allow to define buttons in your panel.
+ 2:
+ 2: blueprint_bundle_button # String values are expected to be `reusables` that are defined at the end of this file.
+ 3: blueprint_bundle_button
+ 4: blueprint_bundle_button
+ 5: blueprint_bundle_button
+ 6: blueprint_bundle_button
+ 7: blueprint_bundle_button
+ 8: blueprint_bundle_button
+ 3:
+ 1:
+ icon: TIPPED_ARROW:INSTANT_HEAL::::1 # The icon for button
+ title: panels.buttons.previous.name # The name of button, or link to the localization.
+ description: panels.buttons.previous.description # The description of button, or link to the localization.
+ data:
+ type: PREVIOUS # Indicates what button is doing. Available values depends on panel
+ indexing: true # Parameter for button.
+ actions: # List of actions that button can do. Available values depends on button
+ previous:
+ click-type: UNKNOWN # UNKNOWN means that any click type is respected.
+ tooltip: panels.tips.click-to-previous # Tooltips are always generated an empty line bellow description/title. Not required.
+ 2: blueprint_bundle_button
+ 3: blueprint_bundle_button
+ 4: blueprint_bundle_button
+ 5: blueprint_bundle_button
+ 6: blueprint_bundle_button
+ 7: blueprint_bundle_button
+ 8: blueprint_bundle_button
+ 9:
+ icon: TIPPED_ARROW:JUMP::::1
+ title: panels.buttons.next.name
+ description: panels.buttons.next.description
+ data:
+ type: NEXT
+ indexing: true
+ actions:
+ next:
+ click-type: UNKNOWN
+ tooltip: panels.tips.click-to-next
+ 4:
+ 2: blueprint_bundle_button
+ 3: blueprint_bundle_button
+ 4: blueprint_bundle_button
+ 5: blueprint_bundle_button
+ 6: blueprint_bundle_button
+ 7: blueprint_bundle_button
+ 8: blueprint_bundle_button
+ reusable: # List of reoccurring buttons in the panels.
+ blueprint_bundle_button: # The ID of the button
+ # icon: GRASS_BLOCK
+ title: panels.island_creation.buttons.bundle.name
+ description: panels.island_creation.buttons.bundle.description
+ data:
+ type: BUNDLE
+ # unique_id: default # Specifying unique_id will force to show the requested bundle if it is available.
+ actions:
+ select:
+ click-type: UNKNOWN
+ tooltip: panels.tips.click-to-choose
\ No newline at end of file
diff --git a/src/main/resources/panels/language_panel.yml b/src/main/resources/panels/language_panel.yml
new file mode 100644
index 000000000..707be9534
--- /dev/null
+++ b/src/main/resources/panels/language_panel.yml
@@ -0,0 +1,71 @@
+# This is default language selection panel. It is used in all situations when gamemode addon does not have specified their
+# of panel.
+language_panel:
+ title: panels.language.title # The title of panel or link to the localization location.
+ type: INVENTORY # The type of inventory: INVENTORY, DROPPER, HOPPER
+ background: # The item that will be displayed in empty spots. This section can be removed.
+ icon: BLACK_STAINED_GLASS_PANE # The icon of background item
+ title: "&b&r" # Empty text # The text of background item
+ border: # The item that will be displayed around the inventory. This section can be removed.
+ icon: BLACK_STAINED_GLASS_PANE # The icon of background item
+ title: "&b&r" # Empty text # The text of background item
+ force-shown: [] # Allow to specify (1-6, 1-3, 1) which rows must be showed regardless of empty elements.
+ content: # Allow to define buttons in your panel.
+ 2:
+ 2: language_button # String values are expected to be `reusables` that are defined at the end of this file.
+ 3: language_button
+ 4: language_button
+ 5: language_button
+ 6: language_button
+ 7: language_button
+ 8: language_button
+ 3:
+ 1:
+ icon: TIPPED_ARROW:INSTANT_HEAL::::1 # The icon for button
+ title: panels.buttons.previous.name # The name of button, or link to the localization.
+ description: panels.buttons.previous.description # The description of button, or link to the localization.
+ data:
+ type: PREVIOUS # Indicates what button is doing. Available values depends on panel
+ indexing: true # Parameter for button.
+ actions: # List of actions that button can do. Available values depends on button
+ previous:
+ click-type: UNKNOWN # UNKNOWN means that any click type is respected.
+ tooltip: panels.tips.click-to-previous # Tooltips are always generated an empty line bellow description/title. Not required.
+ 2: language_button
+ 3: language_button
+ 4: language_button
+ 5: language_button
+ 6: language_button
+ 7: language_button
+ 8: language_button
+ 9:
+ icon: TIPPED_ARROW:JUMP::::1
+ title: panels.buttons.next.name
+ description: panels.buttons.next.description
+ data:
+ type: NEXT
+ indexing: true
+ actions:
+ next:
+ click-type: UNKNOWN
+ tooltip: panels.tips.click-to-next
+ 4:
+ 2: language_button
+ 3: language_button
+ 4: language_button
+ 5: language_button
+ 6: language_button
+ 7: language_button
+ 8: language_button
+ reusable: # List of reoccurring buttons in the panels.
+ language_button: # The ID of the button
+ # icon: GRASS_BLOCK
+ title: panels.language.buttons.language.name
+ description: panels.language.buttons.language.description
+ data:
+ type: LOCALE
+ # lang_id: default # Specifying lang_id will force to show the requested locale if it is available.
+ actions:
+ select:
+ click-type: UNKNOWN
+ tooltip: panels.tips.click-to-choose
\ No newline at end of file
diff --git a/src/test/java/world/bentobox/bentobox/api/commands/island/IslandCreateCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/island/IslandCreateCommandTest.java
index 39eba339a..5d35d71a7 100644
--- a/src/test/java/world/bentobox/bentobox/api/commands/island/IslandCreateCommandTest.java
+++ b/src/test/java/world/bentobox/bentobox/api/commands/island/IslandCreateCommandTest.java
@@ -54,7 +54,7 @@
import world.bentobox.bentobox.managers.PlayersManager;
import world.bentobox.bentobox.managers.island.NewIsland;
import world.bentobox.bentobox.managers.island.NewIsland.Builder;
-import world.bentobox.bentobox.panels.IslandCreationPanel;
+import world.bentobox.bentobox.panels.customizable.IslandCreationPanel;
/**
* @author tastybento
diff --git a/src/test/java/world/bentobox/bentobox/api/localization/BentoBoxLocaleTest.java b/src/test/java/world/bentobox/bentobox/api/localization/BentoBoxLocaleTest.java
index 1beefa775..a195ebecd 100644
--- a/src/test/java/world/bentobox/bentobox/api/localization/BentoBoxLocaleTest.java
+++ b/src/test/java/world/bentobox/bentobox/api/localization/BentoBoxLocaleTest.java
@@ -50,6 +50,7 @@ public void setUp() throws Exception {
ItemFactory itemFactory = mock(ItemFactory.class);
bannerMeta = mock(BannerMeta.class);
when(itemFactory.getItemMeta(any())).thenReturn(bannerMeta);
+ when(itemFactory.createItemStack(any())).thenThrow(IllegalArgumentException.class);
when(Bukkit.getItemFactory()).thenReturn(itemFactory);
Locale locale = Locale.US;
diff --git a/src/test/java/world/bentobox/bentobox/panels/IslandCreationPanelTest.java b/src/test/java/world/bentobox/bentobox/panels/customizable/IslandCreationPanelTest.java
similarity index 72%
rename from src/test/java/world/bentobox/bentobox/panels/IslandCreationPanelTest.java
rename to src/test/java/world/bentobox/bentobox/panels/customizable/IslandCreationPanelTest.java
index 271bb0b37..19f5ca654 100644
--- a/src/test/java/world/bentobox/bentobox/panels/IslandCreationPanelTest.java
+++ b/src/test/java/world/bentobox/bentobox/panels/customizable/IslandCreationPanelTest.java
@@ -1,4 +1,4 @@
-package world.bentobox.bentobox.panels;
+package world.bentobox.bentobox.panels.customizable;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
@@ -7,18 +7,18 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
-import java.util.UUID;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.*;
import org.bukkit.Bukkit;
import org.bukkit.Material;
+import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemFactory;
import org.bukkit.inventory.meta.ItemMeta;
+import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.scheduler.BukkitScheduler;
import org.junit.After;
import org.junit.Before;
@@ -43,7 +43,6 @@
import world.bentobox.bentobox.managers.IslandWorldManager;
import world.bentobox.bentobox.managers.IslandsManager;
import world.bentobox.bentobox.managers.PlayersManager;
-import world.bentobox.bentobox.managers.island.NewIsland.Builder;
/**
* @author tastybento
@@ -60,8 +59,6 @@ public class IslandCreationPanelTest {
@Mock
private IslandWorldManager iwm;
@Mock
- private Builder builder;
- @Mock
private BentoBox plugin;
@Mock
private Settings settings;
@@ -78,6 +75,11 @@ public class IslandCreationPanelTest {
@Mock
private BlueprintBundle bb3;
+ /**
+ * Location of the resources folder
+ */
+ private final Path resourcePath = Paths.get("src","test","resources");
+
/**
*/
@Before
@@ -100,14 +102,25 @@ public void setUp() throws Exception {
when(user.getUniqueId()).thenReturn(uuid);
when(user.getPlayer()).thenReturn(player);
when(user.hasPermission(anyString())).thenReturn(true);
- when(user.getTranslation(any()))
- .thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class));
User.setPlugin(plugin);
// Set up user already
User.getInstance(player);
// Addon
GameModeAddon addon = mock(GameModeAddon.class);
+ when(addon.getDataFolder()).thenReturn(resourcePath.toFile());
+
+ when(user.getTranslation(any())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class));
+ when(user.getTranslation(any(World.class), any(), any())).thenAnswer((Answer) invocation -> invocation.getArgument(1, String.class));
+ when(user.getTranslation(any(String.class), any())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class));
+ when(user.getTranslationOrNothing(any(), any())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class));
+
+ when(user.getTranslation(any(World.class), eq("panels.island_creation.buttons.bundle.name"), any())).
+ thenAnswer((Answer) invocation -> invocation.getArgument(3, String.class));
+ when(user.getTranslation(any(World.class), eq("panels.island_creation.buttons.bundle.description"), any())).
+ thenAnswer((Answer) invocation -> invocation.getArgument(3, String.class));
+ when(plugin.getDescription()).thenAnswer((Answer) invocation ->
+ new PluginDescriptionFile("BentoBox", "1.0", "world.bentobox.bentobox"));
// Parent command has no aliases
when(ic.getSubCommandAliases()).thenReturn(new HashMap<>());
@@ -117,6 +130,8 @@ public void setUp() throws Exception {
when(ic.getUsage()).thenReturn("");
when(ic.getSubCommand(Mockito.anyString())).thenReturn(Optional.empty());
when(ic.getAddon()).thenReturn(addon);
+ World world = mock(World.class);
+ when(ic.getWorld()).thenReturn(world);
// No island for player to begin with (set it later in the tests)
when(im.hasIsland(any(), eq(uuid))).thenReturn(false);
@@ -184,42 +199,41 @@ public void tearDown() {
/**
* Test method for
- * {@link world.bentobox.bentobox.panels.IslandCreationPanel#openPanel(world.bentobox.bentobox.api.commands.CompositeCommand, world.bentobox.bentobox.api.user.User, java.lang.String)}.
+ * {@link world.bentobox.bentobox.panels.customizable.IslandCreationPanel#openPanel(world.bentobox.bentobox.api.commands.CompositeCommand, world.bentobox.bentobox.api.user.User, java.lang.String)}.
*/
@Test
public void testOpenPanel() {
IslandCreationPanel.openPanel(ic, user, "");
- // Check for slot being set to 0
- verify(bb2).setSlot(eq(0));
- verify(bb3).setSlot(eq(0));
+
// Set correctly
- verify(inv).setItem(eq(5), any());
+ verify(inv).setItem(eq(0), any());
+ verify(inv).setItem(eq(1), any());
verify(meta).setDisplayName(eq("test"));
verify(meta).setLocalizedName(eq("test"));
- verify(meta).setLore(eq(Collections.singletonList("A description")));
+ verify(meta).setLore(eq(List.of("A description", "", "panels.tips.click-to-choose")));
}
/**
- * Test method for {@link world.bentobox.bentobox.panels.IslandCreationPanel#openPanel(world.bentobox.bentobox.api.commands.CompositeCommand, world.bentobox.bentobox.api.user.User, java.lang.String)}.
+ * Test method for {@link world.bentobox.bentobox.panels.customizable.IslandCreationPanel#openPanel(world.bentobox.bentobox.api.commands.CompositeCommand, world.bentobox.bentobox.api.user.User, java.lang.String)}.
*/
@Test
public void testOpenPanelSameSlot() {
when(bb2.getSlot()).thenReturn(5);
when(bb3.getSlot()).thenReturn(5);
IslandCreationPanel.openPanel(ic, user, "");
- verify(inv).setItem(eq(5), any());
+ verify(inv).setItem(eq(0), any());
+ verify(inv).setItem(eq(1), any());
verify(meta).setDisplayName(eq("test"));
verify(meta).setLocalizedName(eq("test"));
- verify(meta).setLore(eq(Collections.singletonList("A description")));
+ verify(meta).setLore(eq(List.of("A description", "", "panels.tips.click-to-choose")));
verify(inv).setItem(eq(0), any());
verify(meta).setDisplayName(eq("test2"));
verify(meta).setLocalizedName(eq("test2"));
- verify(meta).setLore(eq(Collections.singletonList("A description 2")));
+ verify(meta).setLore(eq(List.of("A description 2", "", "panels.tips.click-to-choose")));
verify(inv).setItem(eq(1), any());
verify(meta).setDisplayName(eq("test3"));
verify(meta).setLocalizedName(eq("test3"));
- verify(meta).setLore(eq(Collections.singletonList("A description 3")));
-
+ verify(meta).setLore(eq(List.of("A description 3", "", "panels.tips.click-to-choose")));
}
}
diff --git a/src/test/java/world/bentobox/bentobox/panels/LanguagePanelTest.java b/src/test/java/world/bentobox/bentobox/panels/customizable/LanguagePanelTest.java
similarity index 61%
rename from src/test/java/world/bentobox/bentobox/panels/LanguagePanelTest.java
rename to src/test/java/world/bentobox/bentobox/panels/customizable/LanguagePanelTest.java
index cce75f540..aaed88101 100644
--- a/src/test/java/world/bentobox/bentobox/panels/LanguagePanelTest.java
+++ b/src/test/java/world/bentobox/bentobox/panels/customizable/LanguagePanelTest.java
@@ -1,28 +1,15 @@
-package world.bentobox.bentobox.panels;
+package world.bentobox.bentobox.panels.customizable;
-import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.awt.Panel;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.Material;
+import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemFactory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
+import org.bukkit.plugin.PluginDescriptionFile;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -36,14 +23,23 @@
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.reflect.Whitebox;
+import java.awt.Panel;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.*;
-import net.md_5.bungee.api.ChatColor;
import world.bentobox.bentobox.BentoBox;
+import world.bentobox.bentobox.api.addons.GameModeAddon;
+import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.localization.BentoBoxLocale;
import world.bentobox.bentobox.api.panels.builders.PanelBuilder;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.managers.LocalesManager;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+
/**
* @author tastybento
*
@@ -61,20 +57,24 @@ public class LanguagePanelTest {
private ArrayList localeList;
- @Mock
- private PanelBuilder pb;
- @Mock
- private Panel panel;
@Mock
private Inventory inv;
@Mock
private ItemMeta meta;
+ @Mock
+ private CompositeCommand command;
+
@Captor
private ArgumentCaptor argument;
private Map map;
+ /**
+ * Location of the resources folder
+ */
+ private final Path resourcePath = Paths.get("src","test","resources");
+
/**
*/
@Before
@@ -90,7 +90,24 @@ public void setUp() throws Exception {
when(user.getPlayer()).thenReturn(player);
when(user.hasPermission(anyString())).thenReturn(true);
when(user.getTranslation(any())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class));
+ when(user.getTranslation(any(World.class), any(), any())).thenAnswer((Answer) invocation -> invocation.getArgument(1, String.class));
+ when(user.getTranslation(any(String.class), any())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class));
+ when(user.getTranslationOrNothing(any(), any())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class));
when(user.getLocale()).thenReturn(Locale.ENGLISH);
+
+ when(user.getTranslation(any(World.class), eq("panels.language.buttons.language.name"), any())).
+ thenAnswer((Answer) invocation -> invocation.getArgument(3, String.class));
+
+ GameModeAddon addon = mock(GameModeAddon.class);
+ when(command.getAddon()).thenReturn(addon);
+ when(addon.getDataFolder()).thenReturn(resourcePath.toFile());
+
+ World world = mock(World.class);
+ when(command.getWorld()).thenReturn(world);
+
+ when(plugin.getDescription()).thenAnswer((Answer) invocation ->
+ new PluginDescriptionFile("BentoBox", "1.0", "world.bentobox.bentobox"));
+
User.setPlugin(plugin);
// Set up user already
User.getInstance(player);
@@ -123,17 +140,17 @@ public void tearDown() {
}
/**
- * Test method for {@link world.bentobox.bentobox.panels.LanguagePanel#openPanel(world.bentobox.bentobox.api.user.User)}.
+ * Test method for {@link world.bentobox.bentobox.panels.customizable.LanguagePanel#openPanel(world.bentobox.bentobox.api.commands.CompositeCommand,world.bentobox.bentobox.api.user.User)}.
*/
@Test
public void testOpenPanelNoLocales() {
- LanguagePanel.openPanel(user);
+ LanguagePanel.openPanel(command, user);
verify(plugin).getLocalesManager();
verify(lm).getAvailableLocales(eq(true));
}
/**
- * Test method for {@link world.bentobox.bentobox.panels.LanguagePanel#openPanel(world.bentobox.bentobox.api.user.User)}.
+ * Test method for {@link world.bentobox.bentobox.panels.customizable.LanguagePanel#openPanel(world.bentobox.bentobox.api.commands.CompositeCommand,world.bentobox.bentobox.api.user.User)}.
*/
@Test
public void testOpenPanelLocalesNullBanner() {
@@ -146,29 +163,30 @@ public void testOpenPanelLocalesNullBanner() {
map.put(Locale.CHINA, bbl);
map.put(Locale.ENGLISH, bbl);
- LanguagePanel.openPanel(user);
+ LanguagePanel.openPanel(command, user);
verify(lm, times(3)).getLanguages();
verify(bbl, times(3)).getBanner();
- verify(user).getTranslation("language.panel-title");
+ verify(user).getTranslation("panels.language.title");
// Other langs
- verify(user, times(2)).getTranslation(eq("language.description.click-to-select"));
- verify(user, times(3)).getTranslation(eq("language.description.authors"));
- // Selected language
- verify(user, Mockito.atMostOnce()).getTranslation(eq("language.description.selected"));
+ verify(user, times(3)).getTranslation(eq("panels.language.buttons.language.authors"));
+ verify(user, times(1)).getTranslation(eq("panels.language.buttons.language.selected"));
+ verify(user, times(3)).getTranslationOrNothing(eq("panels.language.buttons.language.description"), any());
+ verify(user, times(2)).getTranslation(any(World.class), eq("panels.tips.click-to-choose"));
verify(inv).setItem(eq(0), argument.capture());
assertEquals(Material.WHITE_BANNER, argument.getValue().getType());
assertEquals(1, argument.getValue().getAmount());
assertEquals(meta, argument.getValue().getItemMeta());
- verify(meta).setDisplayName(eq(ChatColor.WHITE + "Chinese (China)"));
- verify(meta).setDisplayName(eq(ChatColor.WHITE + "English (Canada)"));
+
+ verify(meta).setDisplayName(eq("Chinese (China)"));
+ verify(meta).setDisplayName(eq("English (Canada)"));
verify(inv).setItem(eq(1), any());
verify(inv).setItem(eq(2), any());
verify(inv, Mockito.never()).setItem(eq(3), any());
}
/**
- * Test method for {@link world.bentobox.bentobox.panels.LanguagePanel#openPanel(world.bentobox.bentobox.api.user.User)}.
+ * Test method for {@link world.bentobox.bentobox.panels.customizable.LanguagePanel#openPanel(world.bentobox.bentobox.api.commands.CompositeCommand,world.bentobox.bentobox.api.user.User)}.
*/
@Test
public void testOpenPanelLocalesNotNullBanner() {
@@ -178,7 +196,7 @@ public void testOpenPanelLocalesNotNullBanner() {
map.put(Locale.CANADA, bbl);
when(bbl.getBanner()).thenReturn(new ItemStack(Material.CYAN_BANNER));
- LanguagePanel.openPanel(user);
+ LanguagePanel.openPanel(command, user);
verify(inv).setItem(eq(0), argument.capture());
assertEquals(Material.CYAN_BANNER, argument.getValue().getType());
}
diff --git a/src/test/java/world/bentobox/bentobox/util/ItemParserTest.java b/src/test/java/world/bentobox/bentobox/util/ItemParserTest.java
index 739587827..b14cc32ee 100644
--- a/src/test/java/world/bentobox/bentobox/util/ItemParserTest.java
+++ b/src/test/java/world/bentobox/bentobox/util/ItemParserTest.java
@@ -21,8 +21,10 @@
import org.bukkit.inventory.meta.BannerMeta;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.PotionMeta;
+import org.bukkit.inventory.meta.SkullMeta;
import org.bukkit.potion.PotionData;
import org.bukkit.potion.PotionType;
+import org.bukkit.profile.PlayerProfile;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
@@ -50,6 +52,9 @@ public class ItemParserTest {
private ItemMeta itemMeta;
@Mock
private ItemFactory itemFactory;
+ @Mock
+ private SkullMeta skullMeta;
+
private ItemStack defaultItem;
@SuppressWarnings("deprecation")
@@ -61,6 +66,8 @@ public void setUp() throws Exception {
PowerMockito.mockStatic(Bukkit.class);
when(Bukkit.getItemFactory()).thenReturn(itemFactory);
+ // Do not test Bukkit createItemStack method output as I assume Bukkit has their tests covered.
+ when(itemFactory.createItemStack(any())).thenThrow(IllegalArgumentException.class);
/*
when(itemFactory.getItemMeta(Mockito.eq(Material.POTION))).thenReturn(potionMeta);
when(itemFactory.getItemMeta(Mockito.eq(Material.SPLASH_POTION))).thenReturn(potionMeta);
@@ -106,148 +113,66 @@ public void testParseNoColons() {
assertEquals(defaultItem, ItemParser.parse("NOCOLONS", defaultItem));
}
- /*
- * # Format POTION:NAME::::QTY
- # LEVEL, EXTENDED, SPLASH, LINGER are optional.
- # LEVEL is a number, 1 or 2
- # LINGER is for V1.9 servers and later
- # Examples:
- # POTION:STRENGTH:1:EXTENDED:SPLASH:1
- # POTION:INSTANT_DAMAGE:2::LINGER:2
- # POTION:JUMP:2:NOTEXTENDED:NOSPLASH:1
- # POTION:WEAKNESS::::1 - any weakness potion
- */
-
- @Ignore("Extended potions now have their own names and are not extended like this")
- @Test
- public void testParsePotionStrengthExtended() {
- when(itemFactory.getItemMeta(any())).thenReturn(potionMeta);
- ItemStack result = ItemParser.parse("POTION:STRENGTH:1:EXTENDED::5");
- assertNotNull(result);
- assertEquals(Material.POTION, result.getType());
- PotionType type = PotionType.STRENGTH;
- boolean isExtended = true;
- boolean isUpgraded = false;
- PotionData data = new PotionData(type, isExtended, isUpgraded);
- verify(potionMeta).setBasePotionData(Mockito.eq(data));
- assertEquals(5, result.getAmount());
- }
-
- @SuppressWarnings("deprecation")
@Test
- public void testParsePotionStrengthNotExtended() {
+ public void testParsePotion() {
when(itemFactory.getItemMeta(any())).thenReturn(potionMeta);
- ItemStack result = ItemParser.parse("POTION:STRENGTH:1:::4");
- assertNotNull(result);
- assertEquals(Material.POTION, result.getType());
- PotionType type = PotionType.STRENGTH;
- boolean isExtended = false;
- boolean isUpgraded = false;
- PotionData data = new PotionData(type, isExtended, isUpgraded);
- verify(potionMeta).setBasePotionData(Mockito.eq(data));
- assertEquals(4, result.getAmount());
+ for (PotionType type : PotionType.values()) {
+ ItemStack itemStack = ItemParser.parse("POTION:" + type.name() + ":1");
+ assertEquals(itemStack.getType(), Material.POTION);
+ // Not sure how this can be tested.
+ // assertEquals(type, ((PotionMeta) itemStack.getItemMeta()).getBasePotionType());
+ assertEquals(1, itemStack.getAmount());
+ }
}
- @SuppressWarnings("deprecation")
@Test
- public void testParsePotionStrengthNotExtendedSplash() {
+ public void testParseSplashPotion() {
when(itemFactory.getItemMeta(any())).thenReturn(potionMeta);
- ItemStack result = ItemParser.parse("POTION:STRENGTH:1::SPLASH:3");
- assertNotNull(result);
- assertEquals(Material.SPLASH_POTION, result.getType());
- PotionType type = PotionType.STRENGTH;
- boolean isExtended = false;
- boolean isUpgraded = false;
- PotionData data = new PotionData(type, isExtended, isUpgraded);
- verify(potionMeta).setBasePotionData(Mockito.eq(data));
- assertEquals(3, result.getAmount());
+ for (PotionType type : PotionType.values()) {
+ ItemStack itemStack = ItemParser.parse("SPLASH_POTION:" + type.name() + ":1");
+ assertEquals(itemStack.getType(), Material.SPLASH_POTION);
+ // Not sure how this can be tested.
+ // assertEquals(type, ((PotionMeta) itemStack.getItemMeta()).getBasePotionType());
+ assertEquals(1, itemStack.getAmount());
+ }
}
- @SuppressWarnings("deprecation")
- @Ignore("Potions are no longer upgraded like this")
@Test
- public void testParsePotionStrengthNotExtendedUpgradedSplash() {
+ public void testParseLingeringPotion() {
when(itemFactory.getItemMeta(any())).thenReturn(potionMeta);
- ItemStack result = ItemParser.parse("POTION:STRENGTH:2::SPLASH:3");
- assertNotNull(result);
- assertEquals(Material.SPLASH_POTION, result.getType());
- PotionType type = PotionType.STRENGTH;
- boolean isExtended = false;
- boolean isUpgraded = true;
- PotionData data = new PotionData(type, isExtended, isUpgraded);
- verify(potionMeta).setBasePotionData(Mockito.eq(data));
- assertEquals(3, result.getAmount());
- }
-
- enum extend {
- NOT_EXTENDED,
- EXTENDED
- }
-
- enum type {
- NO_SPLASH,
- SPLASH,
- LINGER
+ for (PotionType type : PotionType.values()) {
+ ItemStack itemStack = ItemParser.parse("LINGERING_POTION:" + type.name() + ":1");
+ assertEquals(itemStack.getType(), Material.LINGERING_POTION);
+ // Not sure how this can be tested.
+ // assertEquals(type, ((PotionMeta) itemStack.getItemMeta()).getBasePotionType());
+ assertEquals(1, itemStack.getAmount());
+ }
}
- List notExtendable = Arrays.asList(
- PotionType.UNCRAFTABLE,
- PotionType.WATER,
- PotionType.MUNDANE,
- PotionType.THICK,
- PotionType.AWKWARD,
- PotionType.INSTANT_HEAL,
- PotionType.INSTANT_DAMAGE,
- PotionType.LUCK,
- PotionType.NIGHT_VISION
- );
-
- @SuppressWarnings("deprecation")
@Test
- public void testParsePotion() {
+ public void testParseTippedArrow() {
when(itemFactory.getItemMeta(any())).thenReturn(potionMeta);
for (PotionType type : PotionType.values()) {
- if (type.name().contains("LONG") || type.name().contains("STRONG")) {
- continue;
- }
- for (ItemParserTest.type t: ItemParserTest.type.values()) {
- for (int up = 1; up < 2; up++) {
- boolean isUpgraded = up > 1;
- String req = "POTION:" + type.name() + ":" + up + "::"+ t.name() + ":3";
- ItemStack result = ItemParser.parse(req);
- assertNotNull(result);
- switch (t) {
- case LINGER:
- assertEquals(Material.LINGERING_POTION, result.getType());
- PotionData data = new PotionData(type, false, isUpgraded);
- verify(potionMeta, times(3)).setBasePotionData(Mockito.eq(data));
- break;
- case NO_SPLASH:
- assertEquals(Material.POTION, result.getType());
- data = new PotionData(type, false, isUpgraded);
- verify(potionMeta).setBasePotionData(Mockito.eq(data));
- break;
- case SPLASH:
- assertEquals(Material.SPLASH_POTION, result.getType());
- data = new PotionData(type, false, isUpgraded);
- verify(potionMeta, times(2)).setBasePotionData(Mockito.eq(data));
- break;
- default:
- break;
- }
-
- assertEquals(3, result.getAmount());
- }
- }
+ ItemStack itemStack = ItemParser.parse("TIPPED_ARROW:" + type.name() + ":1");
+ assertEquals(itemStack.getType(), Material.TIPPED_ARROW);
+ // Not sure how this can be tested.
+ // assertEquals(type, ((PotionMeta) itemStack.getItemMeta()).getBasePotionType());
+ assertEquals(1, itemStack.getAmount());
}
}
@Test
- public void testParseTippedArrow() {
+ public void testParseBadPotion()
+ {
when(itemFactory.getItemMeta(any())).thenReturn(potionMeta);
- ItemStack result = ItemParser.parse("TIPPED_ARROW:WEAKNESS::::1");
- assertNotNull(result);
- assertEquals(Material.TIPPED_ARROW, result.getType());
+ ItemStack itemStack = ItemParser.parse("POTION::5");
+ assertEquals(5, itemStack.getAmount());
+ // Not sure how this can be tested
+ // assertEquals(PotionType.WATER, ((PotionMeta) itemStack.getItemMeta()).getBasePotionType());
+ itemStack = ItemParser.parse("POTION:NO_POTION:1");
+ assertEquals(1, itemStack.getAmount());
+ // Not sure how this can be tested
+ // assertEquals(PotionType.WATER, ((PotionMeta) itemStack.getItemMeta()).getBasePotionType());
}
@@ -318,7 +243,6 @@ public void testParseBadThreeItem() {
assertEquals(defaultItem, ItemParser.parse("WOODEN_SWORD:4:AA", defaultItem));
}
- @Ignore("This doesn't work for some reason")
@Test
public void parseCustomModelData() {
ItemStack result = ItemParser.parse("WOODEN_SWORD:CMD-23151212:2");
@@ -327,4 +251,21 @@ public void parseCustomModelData() {
assertEquals(2, result.getAmount());
assertNull(ItemParser.parse("WOODEN_SWORD:CMD-23151212:2:CMD-23151212"));
}
+
+ @Test
+ public void parsePlayerHead() {
+ when(itemFactory.getItemMeta(any())).thenReturn(skullMeta);
+ ItemStack result = ItemParser.parse("PLAYER_HEAD:2");
+ assertNotNull(result);
+ assertEquals(Material.PLAYER_HEAD, result.getType());
+ assertEquals(2, result.getAmount());
+
+ result = ItemParser.parse("PLAYER_HEAD:BONNe1704");
+ assertNotNull(result);
+ assertEquals(Material.PLAYER_HEAD, result.getType());
+ assertEquals(1, result.getAmount());
+
+ // I do not know if it is possible to test metadata, as skull meta is not applied to player heads in testing.
+ //assertEquals("BONNe1704", ((SkullMeta) result.getItemMeta()).getOwnerProfile().getName());
+ }
}
diff --git a/src/test/resources/panels/island_creation_panel.yml b/src/test/resources/panels/island_creation_panel.yml
new file mode 100644
index 000000000..a0e55f5bd
--- /dev/null
+++ b/src/test/resources/panels/island_creation_panel.yml
@@ -0,0 +1,29 @@
+# This is default island creation panel. It is used in all situations when gamemode addon does not have specified their
+# of panel.
+island_creation_panel:
+ title: panels.island_creation.title # The title of panel or link to the localization location.
+ type: INVENTORY # The type of inventory: INVENTORY, DROPPER, HOPPER
+ force-shown: [] # Allow to specify (1-6, 1-3, 1) which rows must be showed regardless of empty elements.
+ content: # Allow to define buttons in your panel.
+ 1:
+ 1: blueprint_bundle_button # String values are expected to be `reusables` that are defined at the end of this file.
+ 2: blueprint_bundle_button
+ 3: blueprint_bundle_button
+ 4: blueprint_bundle_button
+ 5: blueprint_bundle_button
+ 6: blueprint_bundle_button
+ 7: blueprint_bundle_button
+ 8: blueprint_bundle_button
+ 9: blueprint_bundle_button
+ reusable: # List of reoccurring buttons in the panels.
+ blueprint_bundle_button: # The ID of the button
+ # icon: GRASS_BLOCK
+ title: panels.island_creation.buttons.bundle.name
+ description: panels.island_creation.buttons.bundle.description
+ data:
+ type: BUNDLE
+ # unique_id: default # Specifying unique_id will force to show the requested bundle if it is available.
+ actions:
+ select:
+ click-type: UNKNOWN
+ tooltip: panels.tips.click-to-choose
\ No newline at end of file
diff --git a/src/test/resources/panels/language_panel.yml b/src/test/resources/panels/language_panel.yml
new file mode 100644
index 000000000..1f0df0f35
--- /dev/null
+++ b/src/test/resources/panels/language_panel.yml
@@ -0,0 +1,29 @@
+# This is default language selection panel. It is used in all situations when gamemode addon does not have specified their
+# of panel.
+language_panel:
+ title: panels.language.title # The title of panel or link to the localization location.
+ type: INVENTORY # The type of inventory: INVENTORY, DROPPER, HOPPER
+ force-shown: [] # Allow to specify (1-6, 1-3, 1) which rows must be showed regardless of empty elements.
+ content: # Allow to define buttons in your panel.
+ 1:
+ 1: language_button # String values are expected to be `reusables` that are defined at the end of this file.
+ 2: language_button
+ 3: language_button
+ 4: language_button
+ 5: language_button
+ 6: language_button
+ 7: language_button
+ 8: language_button
+ 9: language_button
+ reusable: # List of reoccurring buttons in the panels.
+ language_button: # The ID of the button
+ # icon: GRASS_BLOCK
+ title: panels.language.buttons.language.name
+ description: panels.language.buttons.language.description
+ data:
+ type: LOCALE
+ # lang_id: default # Specifying lang_id will force to show the requested locale if it is available.
+ actions:
+ select:
+ click-type: UNKNOWN
+ tooltip: panels.tips.click-to-choose
\ No newline at end of file