Merge branch 'develop' into fix-item-parser

This commit is contained in:
BONNe 2024-01-04 10:41:16 +02:00 committed by GitHub
commit a784335e8d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 1463 additions and 234 deletions

View File

@ -463,6 +463,10 @@ public class BentoBox extends JavaPlugin implements Listener {
getPluginLoader().disablePlugin(this);
return false;
}
log("Saving default panels...");
this.saveResource("panels/island_creation_panel.yml", false);
this.saveResource("panels/language_panel.yml", false);
return true;
}

View File

@ -12,6 +12,7 @@ import java.util.Optional;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import org.bukkit.Bukkit;
import org.bukkit.Server;
@ -263,7 +264,7 @@ public abstract class Addon {
throw new IllegalArgumentException("ResourcePath cannot be null or empty");
}
jarResource = jarResource.replace("\\", File.separator).replace("/", File.separator);
jarResource = jarResource.replace('\\', '/');
try (JarFile jar = new JarFile(file)) {
JarEntry jarConfig = jar.getJarEntry(jarResource);
if (jarConfig != null) {
@ -273,7 +274,9 @@ public abstract class Addon {
"The embedded resource '" + jarResource + "' cannot be found in " + jar.getName());
}
// There are two options, use the path of the resource or not
File outFile = new File(destinationFolder, jarResource);
File outFile = new File(destinationFolder,
jarResource.replaceAll("/", Matcher.quoteReplacement(File.separator)));
if (noPath) {
outFile = new File(destinationFolder, outFile.getName());
}
@ -308,7 +311,7 @@ public abstract class Addon {
throw new IllegalArgumentException("jarResource cannot be null or empty");
}
YamlConfiguration result = new YamlConfiguration();
jarResource = jarResource.replace("\\", File.separator).replace("/", File.separator);
jarResource = jarResource.replace('\\', '/');
try (JarFile jar = new JarFile(file)) {
JarEntry jarConfig = jar.getJarEntry(jarResource);
if (jarConfig != null) {
@ -330,7 +333,7 @@ public abstract class Addon {
throw new IllegalArgumentException("ResourcePath cannot be null or empty");
}
jarResource = jarResource.replace("\\", File.separator).replace("/", File.separator);
jarResource = jarResource.replace('\\', '/');
try (JarFile jar = new JarFile(file)) {
JarEntry jarConfig = jar.getJarEntry(jarResource);
if (jarConfig != null) {

View File

@ -12,7 +12,7 @@ import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.BlueprintsManager;
import world.bentobox.bentobox.managers.island.NewIsland;
import world.bentobox.bentobox.panels.IslandCreationPanel;
import world.bentobox.bentobox.panels.customizable.IslandCreationPanel;
import world.bentobox.bentobox.util.Util;
/**

View File

@ -7,7 +7,7 @@ import java.util.Optional;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.panels.LanguagePanel;
import world.bentobox.bentobox.panels.customizable.LanguagePanel;
import world.bentobox.bentobox.util.Util;
/**
@ -46,7 +46,7 @@ public class IslandLanguageCommand extends CompositeCommand {
return false;
}
} else {
LanguagePanel.openPanel(user);
LanguagePanel.openPanel(this, user);
}
return true;
}

View File

@ -16,7 +16,7 @@ import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.BlueprintsManager;
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;
import world.bentobox.bentobox.util.Util;
/**

View File

@ -1,91 +0,0 @@
package world.bentobox.bentobox.panels;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.eclipse.jdt.annotation.NonNull;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.panels.PanelItem;
import world.bentobox.bentobox.api.panels.builders.PanelBuilder;
import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBundle;
import world.bentobox.bentobox.managers.BlueprintsManager;
import world.bentobox.bentobox.util.Util;
/**
* Displays the available BlueprintBundles to pick up as the island.
* @author tastybento
* @since 1.5.0
*/
public class IslandCreationPanel {
private IslandCreationPanel() {}
/**
* Shows a player a panel of selectable blueprint bundles. Checks user's permission
* @param command - the command requesting the panel, e.g., create or reset
* @param user - the user
* @param label - label
*/
public static void openPanel(@NonNull CompositeCommand command, @NonNull User user, @NonNull String label) {
BentoBox plugin = BentoBox.getInstance();
// Create the panel
PanelBuilder pb = new PanelBuilder().name(user.getTranslation("commands.island.create.pick")).user(user);
// Get the bundles
Comparator<BlueprintBundle> sortByDisplayName = (p, o) -> p.getDisplayName().compareToIgnoreCase(o.getDisplayName());
List<BlueprintBundle> bbs = plugin.getBlueprintsManager().getBlueprintBundles(command.getAddon()).values()
.stream().sorted(sortByDisplayName).toList();
// Loop through them and create items in the panel
for (BlueprintBundle bb : bbs) {
String perm = command.getPermissionPrefix() + "island.create." + bb.getUniqueId();
if (bb.getUniqueId().equals(BlueprintsManager.DEFAULT_BUNDLE_NAME)
|| !bb.isRequirePermission()
|| user.hasPermission(perm)) {
// Add an item
PanelItem item = new PanelItemBuilder()
.name(bb.getDisplayName())
.description(bb.getDescription().stream().map(Util::translateColorCodes).toList())
.icon(bb.getIcon()).clickHandler((panel, user1, clickType, slot1) -> {
user1.closeInventory();
command.execute(user1, label, Collections.singletonList(bb.getUniqueId()));
return true;
}).build();
// Determine slot
if (bb.getSlot() < 0 || bb.getSlot() > BlueprintManagementPanel.MAX_BP_SLOT) {
bb.setSlot(0);
}
if (pb.slotOccupied(bb.getSlot())) {
int slot = getFirstAvailableSlot(pb);
if (slot == -1) {
// TODO add paging
plugin.logError("Too many blueprint bundles to show!");
pb.item(item);
} else {
pb.item(slot, item);
}
} else {
pb.item(bb.getSlot(), item);
}
}
}
pb.build();
}
/**
* @param pb - panel builder
* @return first available slot, or -1 if none
*/
private static int getFirstAvailableSlot(PanelBuilder pb) {
for (int i = 0; i < BlueprintManagementPanel.MAX_BP_SLOT; i++) {
if (!pb.slotOccupied(i)) {
return i;
}
}
return -1;
}
}

View File

@ -1,68 +0,0 @@
package world.bentobox.bentobox.panels;
import java.util.Locale;
import java.util.Objects;
import org.apache.commons.lang.WordUtils;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.localization.BentoBoxLocale;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.panels.builders.PanelBuilder;
import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.managers.LocalesManager;
/**
* @author Poslovitch
*/
public class LanguagePanel {
private LanguagePanel() {}
/**
* Dynamically creates the panel.
* @param user the User to show the panel to
*/
public static void openPanel(User user) {
PanelBuilder panelBuilder = new PanelBuilder()
.name(user.getTranslation("language.panel-title"));
LocalesManager localesManager = BentoBox.getInstance().getLocalesManager();
for (Locale locale : localesManager.getAvailableLocales(true)) {
PanelItemBuilder localeIcon = new PanelItemBuilder();
BentoBoxLocale language = localesManager.getLanguages().get(locale);
ItemStack localeBanner = language.getBanner();
// Set to a blank banner.
localeIcon.icon(Objects.requireNonNullElseGet(localeBanner, () -> new ItemStack(Material.WHITE_BANNER, 1)));
localeIcon.name(ChatColor.WHITE + WordUtils.capitalize(locale.getDisplayName(user.getLocale())))
.clickHandler((panel, u, click, slot) -> {
BentoBox.getInstance().getPlayers().setLocale(u.getUniqueId(), locale.toLanguageTag());
u.sendMessage("language.edited", "[lang]", WordUtils.capitalize(locale.getDisplayName(user.getLocale())));
openPanel(u);
return true;
});
if (user.getLocale().equals(locale)) {
localeIcon.description(user.getTranslation("language.description.selected"), "");
} else {
localeIcon.description(user.getTranslation("language.description.click-to-select"), "");
}
localeIcon.description(user.getTranslation("language.description.authors"));
for (String author : language.getAuthors()) {
localeIcon.description(user.getTranslation("language.description.author", TextVariables.NAME, author));
}
panelBuilder.item(localeIcon.build());
}
panelBuilder.build().open(user);
}
}

View File

@ -0,0 +1,544 @@
//
// Created by BONNe
// Copyright - 2023
//
package world.bentobox.bentobox.panels.customizable;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.inventory.ItemStack;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import java.io.File;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
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.TextVariables;
import world.bentobox.bentobox.api.panels.PanelItem;
import world.bentobox.bentobox.api.panels.TemplatedPanel;
import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder;
import world.bentobox.bentobox.api.panels.builders.TemplatedPanelBuilder;
import world.bentobox.bentobox.api.panels.reader.ItemTemplateRecord;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBundle;
import world.bentobox.bentobox.util.Util;
/**
* This class generates Island Creation Panel based on user specified file with name: "island_creation_panel.yml".
* If file with such name is located at gamemode panels directory, then that file will be used.
* Otherwise, file in BentoBox/panels is used.
*/
public class IslandCreationPanel
{
// ---------------------------------------------------------------------
// Section: Constructor
// ---------------------------------------------------------------------
/**
* This is internal constructor. It is used internally in current class to avoid creating objects everywhere.
*
* @param command CompositeCommand object
* @param label The main command label
* @param user User who opens panel
*/
private IslandCreationPanel(@NonNull CompositeCommand command,
@NonNull User user,
@NonNull String label)
{
this.plugin = BentoBox.getInstance();
this.user = user;
this.mainLabel = label;
this.elementList = this.plugin.getBlueprintsManager().getBlueprintBundles(command.getAddon()).values().stream().
sorted(Comparator.comparingInt(BlueprintBundle::getSlot).thenComparing(BlueprintBundle::getUniqueId)).
filter(bundle -> !bundle.isRequirePermission() ||
this.user.hasPermission(command.getPermissionPrefix() + "island.create." + bundle.getUniqueId())).
toList();
this.mainCommand = command;
}
// ---------------------------------------------------------------------
// Section: Methods
// ---------------------------------------------------------------------
/**
* Build method manages current panel opening. It uses BentoBox PanelAPI that is easy to use and users can get nice
* panels.
*/
private void build()
{
// Do not open gui if there is no magic sticks.
if (this.elementList.isEmpty())
{
this.plugin.logError("There are no available phases for selection!");
this.user.sendMessage("no-phases",
TextVariables.GAMEMODE, this.plugin.getDescription().getName());
return;
}
// Start building panel.
TemplatedPanelBuilder panelBuilder = new TemplatedPanelBuilder();
// Set main template.
if (this.doesCustomPanelExists(this.mainCommand.getAddon(), "island_creation_panel"))
{
// Addon has its own island creation panel. Use it.
panelBuilder.template("island_creation_panel", new File(this.mainCommand.getAddon().getDataFolder(), "panels"));
}
else
{
// Use default island creation panel.
panelBuilder.template("island_creation_panel", new File(this.plugin.getDataFolder(), "panels"));
}
panelBuilder.user(this.user);
panelBuilder.world(this.user.getWorld());
// Register button builders
panelBuilder.registerTypeBuilder(BUNDLES, this::createBundleButton);
// Register next and previous builders
panelBuilder.registerTypeBuilder(NEXT, this::createNextButton);
panelBuilder.registerTypeBuilder(PREVIOUS, this::createPreviousButton);
// Register unknown type builder.
panelBuilder.build();
}
/**
* This method returns if panel with the requested name is located in GameModeAddon folder.
* @param addon GameModeAddon that need to be checked.
* @param name Name of the panel.
* @return {@code true} if panel exists, {@code false} otherwise.
*/
private boolean doesCustomPanelExists(GameModeAddon addon, String name)
{
return addon.getDataFolder().exists() &&
new File(addon.getDataFolder(), "panels").exists() &&
new File(addon.getDataFolder(), "panels" + File.separator + name + ".yml").exists();
}
// ---------------------------------------------------------------------
// Section: Buttons
// ---------------------------------------------------------------------
/**
* Create next button panel item.
*
* @param template the template
* @param slot the slot
* @return the panel item
*/
@Nullable
private PanelItem createNextButton(@NonNull ItemTemplateRecord template, TemplatedPanel.ItemSlot slot)
{
int size = this.elementList.size();
if (size <= slot.amountMap().getOrDefault(BUNDLES, 1) ||
1.0 * size / slot.amountMap().getOrDefault(BUNDLES, 1) <= this.pageIndex + 1)
{
// There are no next elements
return null;
}
int nextPageIndex = this.pageIndex + 2;
PanelItemBuilder builder = new PanelItemBuilder();
if (template.icon() != null)
{
ItemStack clone = template.icon().clone();
if ((boolean) template.dataMap().getOrDefault(INDEXING, false))
{
clone.setAmount(nextPageIndex);
}
builder.icon(clone);
}
if (template.title() != null)
{
builder.name(this.user.getTranslation(this.mainCommand.getWorld(), template.title()));
}
if (template.description() != null)
{
builder.description(this.user.getTranslation(this.mainCommand.getWorld(), template.description(),
TextVariables.NUMBER, String.valueOf(nextPageIndex)));
}
// Add ClickHandler
builder.clickHandler((panel, user, clickType, i) ->
{
template.actions().forEach(action -> {
if ((clickType == action.clickType() ||
action.clickType() == ClickType.UNKNOWN) && NEXT.equalsIgnoreCase(action.actionType()))
{
// Next button ignores click type currently.
this.pageIndex++;
this.build();
}
});
// Always return true.
return true;
});
// Collect tooltips.
List<String> tooltips = template.actions().stream().
filter(action -> action.tooltip() != null).
map(action -> this.user.getTranslation(this.mainCommand.getWorld(), action.tooltip())).
filter(text -> !text.isBlank()).
collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size())));
// Add tooltips.
if (!tooltips.isEmpty())
{
// Empty line and tooltips.
builder.description("");
builder.description(tooltips);
}
return builder.build();
}
/**
* Create previous button panel item.
*
* @param template the template
* @param slot the slot
* @return the panel item
*/
@Nullable
private PanelItem createPreviousButton(@NonNull ItemTemplateRecord template, TemplatedPanel.ItemSlot slot)
{
if (this.pageIndex == 0)
{
// There are no next elements
return null;
}
int previousPageIndex = this.pageIndex;
PanelItemBuilder builder = new PanelItemBuilder();
if (template.icon() != null)
{
ItemStack clone = template.icon().clone();
if ((boolean) template.dataMap().getOrDefault(INDEXING, false))
{
clone.setAmount(previousPageIndex);
}
builder.icon(clone);
}
if (template.title() != null)
{
builder.name(this.user.getTranslation(this.mainCommand.getWorld(), template.title()));
}
if (template.description() != null)
{
builder.description(this.user.getTranslation(this.mainCommand.getWorld(), template.description(),
TextVariables.NUMBER, String.valueOf(previousPageIndex)));
}
// Add ClickHandler
// Add ClickHandler
builder.clickHandler((panel, user, clickType, i) ->
{
template.actions().forEach(action -> {
if ((clickType == action.clickType() ||
action.clickType() == ClickType.UNKNOWN) && PREVIOUS.equalsIgnoreCase(action.actionType()))
{
// Next button ignores click type currently.
this.pageIndex--;
this.build();
}
});
// Always return true.
return true;
});
// Collect tooltips.
List<String> tooltips = template.actions().stream().
filter(action -> action.tooltip() != null).
map(action -> this.user.getTranslation(this.mainCommand.getWorld(), action.tooltip())).
filter(text -> !text.isBlank()).
collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size())));
// Add tooltips.
if (!tooltips.isEmpty())
{
// Empty line and tooltips.
builder.description("");
builder.description(tooltips);
}
return builder.build();
}
/**
* This method creates and returns bundle button.
*
* @return PanelItem that represents bundle button.
*/
@Nullable
private PanelItem createBundleButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot)
{
if (this.elementList.isEmpty())
{
// Does not contain any sticks.
return null;
}
int index = this.pageIndex * slot.amountMap().getOrDefault(BUNDLES, 1) + slot.slot();
BlueprintBundle blueprintBundle;
if (index >= this.elementList.size())
{
// Out of index.
blueprintBundle = null;
}
else
{
blueprintBundle = this.elementList.get(index);
}
if (template.dataMap().containsKey("unique_id"))
{
// Try to find bundle with requested ID. if not found, use already collected bundle.
blueprintBundle = this.elementList.stream().
filter(bundle -> bundle.getUniqueId().equals(template.dataMap().get("unique_id"))).
findFirst().
orElse(blueprintBundle);
}
return this.createBundleButton(template, blueprintBundle);
}
// ---------------------------------------------------------------------
// Section: Other methods
// ---------------------------------------------------------------------
/**
* This method creates bundle button.
*
* @return PanelItem that allows to select bundle button
*/
private PanelItem createBundleButton(ItemTemplateRecord template, BlueprintBundle bundle)
{
if (bundle == null)
{
// return as bundle is null. Empty button will be created.
return null;
}
final String reference = "panels.island_creation.buttons.bundle.";
// Get settings for island.
PanelItemBuilder builder = new PanelItemBuilder();
if (template.icon() != null)
{
builder.icon(template.icon().clone());
}
else
{
builder.icon(bundle.getIcon());
}
if (template.title() != null)
{
builder.name(this.user.getTranslation(this.mainCommand.getWorld(), template.title(),
TextVariables.NAME, bundle.getDisplayName()));
}
else
{
builder.name(this.user.getTranslation(reference + "name",
TextVariables.NAME, bundle.getDisplayName()));
}
if (template.description() != null)
{
builder.description(this.user.getTranslation(this.mainCommand.getWorld(), template.description(),
TextVariables.DESCRIPTION, String.join("\n", bundle.getDescription())));
}
else
{
builder.description(this.user.getTranslation(reference + "description",
TextVariables.DESCRIPTION, String.join("\n", bundle.getDescription())));
}
List<ItemTemplateRecord.ActionRecords> actions = template.actions().stream().
filter(action -> SELECT_ACTION.equalsIgnoreCase(action.actionType()) ||
COMMANDS_ACTION.equalsIgnoreCase(action.actionType())).
toList();
// Add ClickHandler
builder.clickHandler((panel, user, clickType, i) ->
{
actions.forEach(action -> {
if (clickType == action.clickType() || action.clickType() == ClickType.UNKNOWN)
{
if (SELECT_ACTION.equalsIgnoreCase(action.actionType()))
{
user.closeInventory();
this.mainCommand.execute(user, this.mainLabel, Collections.singletonList(bundle.getUniqueId()));
}
else if (COMMANDS_ACTION.equalsIgnoreCase(action.actionType()))
{
Util.runCommands(user,
Arrays.stream(action.content().
replaceAll(Pattern.quote(TextVariables.LABEL), this.mainCommand.getTopLabel()).
split("\n")).
toList(),
ISLAND_CREATION_COMMANDS);
}
}
});
// Always return true.
return true;
});
// Collect tooltips.
List<String> tooltips = actions.stream().
filter(action -> action.tooltip() != null).
map(action -> this.user.getTranslation(this.mainCommand.getWorld(), action.tooltip())).
filter(text -> !text.isBlank()).
collect(Collectors.toCollection(() -> new ArrayList<>(actions.size())));
// Add tooltips.
if (!tooltips.isEmpty())
{
// Empty line and tooltips.
builder.description("");
builder.description(tooltips);
}
return builder.build();
}
// ---------------------------------------------------------------------
// Section: Static methods
// ---------------------------------------------------------------------
/**
* This method is used to open Panel outside this class. It will be much easier to open panel with single method
* call then initializing new object.
*
* @param command CompositeCommand object
* @param label The main command label
* @param user User who opens panel
*/
public static void openPanel(@NonNull CompositeCommand command,
@NonNull User user,
@NonNull String label)
{
new IslandCreationPanel(command, user, label).build();
}
// ---------------------------------------------------------------------
// Section: Constants
// ---------------------------------------------------------------------
/**
* This constant is used for button to indicate that it is Blueprint Bundle type.
*/
private static final String BUNDLES = "BUNDLE";
/**
* This constant is used for button to indicate that it is previous page type.
*/
private static final String PREVIOUS = "PREVIOUS";
/**
* This constant is used for button to indicate that it is next page type.
*/
private static final String NEXT = "NEXT";
/**
* This constant is used for indicating that pages should contain numbering.
*/
private static final String INDEXING = "indexing";
/**
* This constant stores value for SELECT action that is used in panels.
*/
private static final String SELECT_ACTION = "SELECT";
/**
* This constant stores value for COMMAND action that is used in panels.
*/
private static final String COMMANDS_ACTION = "COMMANDS";
/**
* This constant stores value for ERROR message that will be displayed upon failing to run creation commands.
*/
private static final String ISLAND_CREATION_COMMANDS = "ISLAND_CREATION_COMMANDS";
// ---------------------------------------------------------------------
// Section: Variables
// ---------------------------------------------------------------------
/**
* This variable allows to access plugin object.
*/
private final BentoBox plugin;
/**
* This variable stores main command that was triggered.
*/
private final CompositeCommand mainCommand;
/**
* This variable holds user who opens panel. Without it panel cannot be opened.
*/
private final User user;
/**
* This variable holds world where panel is opened. Without it panel cannot be opened.
*/
private final String mainLabel;
/**
* This variable stores filtered elements.
*/
private final List<BlueprintBundle> elementList;
/**
* This variable holds current pageIndex for multi-page island choosing.
*/
private int pageIndex;
}

View File

@ -0,0 +1,569 @@
//
// Created by BONNe
// Copyright - 2022
//
package world.bentobox.bentobox.panels.customizable;
import org.apache.commons.lang.WordUtils;
import org.bukkit.Material;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.inventory.ItemStack;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import java.io.File;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
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.localization.TextVariables;
import world.bentobox.bentobox.api.panels.PanelItem;
import world.bentobox.bentobox.api.panels.TemplatedPanel;
import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder;
import world.bentobox.bentobox.api.panels.builders.TemplatedPanelBuilder;
import world.bentobox.bentobox.api.panels.reader.ItemTemplateRecord;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.util.Util;
/**
* This class generates Language Panel based on user specified file with name: "language_panel.yml".
* If file with such name is located at gamemode panels directory, then that file will be used.
* Otherwise, file in BentoBox/panels is used.
*/
public class LanguagePanel
{
// ---------------------------------------------------------------------
// Section: Constructor
// ---------------------------------------------------------------------
/**
* This is internal constructor. It is used internally in current class to avoid creating objects everywhere.
*
* @param command The main addon command.
* @param user User who opens panel
*/
private LanguagePanel(@NonNull CompositeCommand command, @NonNull User user)
{
this.plugin = BentoBox.getInstance();
this.mainCommand = command;
this.user = user;
this.elementList = BentoBox.getInstance().getLocalesManager().getAvailableLocales(true);
}
// ---------------------------------------------------------------------
// Section: Methods
// ---------------------------------------------------------------------
/**
* Build method manages current panel opening. It uses BentoBox PanelAPI that is easy to use and users can get nice
* panels.
*/
private void build()
{
// Do not open gui if there is no magic sticks.
if (this.elementList.isEmpty())
{
this.plugin.logError("There are no available locales for selection!");
this.user.sendMessage("no-locales",
TextVariables.GAMEMODE, this.plugin.getDescription().getName());
return;
}
// Start building panel.
TemplatedPanelBuilder panelBuilder = new TemplatedPanelBuilder();
// Set main template.
if (this.doesCustomPanelExists(this.mainCommand.getAddon(), "language_panel"))
{
// Addon has its own island creation panel. Use it.
panelBuilder.template("language_panel", new File(this.mainCommand.getAddon().getDataFolder(), "panels"));
}
else
{
// Use default island creation panel.
panelBuilder.template("language_panel", new File(this.plugin.getDataFolder(), "panels"));
}
panelBuilder.user(this.user);
panelBuilder.world(this.user.getWorld());
// Register button builders
panelBuilder.registerTypeBuilder(LOCALE, this::createLocaleButton);
// Register next and previous builders
panelBuilder.registerTypeBuilder(NEXT, this::createNextButton);
panelBuilder.registerTypeBuilder(PREVIOUS, this::createPreviousButton);
// Register unknown type builder.
panelBuilder.build();
}
/**
* This method returns if panel with the requested name is located in GameModeAddon folder.
* @param addon GameModeAddon that need to be checked.
* @param name Name of the panel.
* @return {@code true} if panel exists, {@code false} otherwise.
*/
private boolean doesCustomPanelExists(GameModeAddon addon, String name)
{
return addon.getDataFolder().exists() &&
new File(addon.getDataFolder(), "panels").exists() &&
new File(addon.getDataFolder(), "panels" + File.separator + name + ".yml").exists();
}
// ---------------------------------------------------------------------
// Section: Buttons
// ---------------------------------------------------------------------
/**
* Create next button panel item.
*
* @param template the template
* @param slot the slot
* @return the panel item
*/
@Nullable
private PanelItem createNextButton(@NonNull ItemTemplateRecord template, TemplatedPanel.ItemSlot slot)
{
int size = this.elementList.size();
if (size <= slot.amountMap().getOrDefault(LOCALE, 1) ||
1.0 * size / slot.amountMap().getOrDefault(LOCALE, 1) <= this.pageIndex + 1)
{
// There are no next elements
return null;
}
int nextPageIndex = this.pageIndex + 2;
PanelItemBuilder builder = new PanelItemBuilder();
if (template.icon() != null)
{
ItemStack clone = template.icon().clone();
if ((boolean) template.dataMap().getOrDefault(INDEXING, false))
{
clone.setAmount(nextPageIndex);
}
builder.icon(clone);
}
if (template.title() != null)
{
builder.name(this.user.getTranslation(template.title()));
}
if (template.description() != null)
{
builder.description(this.user.getTranslation(template.description(),
TextVariables.NUMBER, String.valueOf(nextPageIndex)));
}
// Add ClickHandler
builder.clickHandler((panel, user, clickType, i) ->
{
template.actions().forEach(action -> {
if ((clickType == action.clickType() ||
action.clickType() == ClickType.UNKNOWN) && NEXT.equalsIgnoreCase(action.actionType()))
{
// Next button ignores click type currently.
this.pageIndex++;
this.build();
}
});
// Always return true.
return true;
});
// Collect tooltips.
List<String> tooltips = template.actions().stream().
filter(action -> action.tooltip() != null).
map(action -> this.user.getTranslation( action.tooltip())).
filter(text -> !text.isBlank()).
collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size())));
// Add tooltips.
if (!tooltips.isEmpty())
{
// Empty line and tooltips.
builder.description("");
builder.description(tooltips);
}
return builder.build();
}
/**
* Create previous button panel item.
*
* @param template the template
* @param slot the slot
* @return the panel item
*/
@Nullable
private PanelItem createPreviousButton(@NonNull ItemTemplateRecord template, TemplatedPanel.ItemSlot slot)
{
if (this.pageIndex == 0)
{
// There are no next elements
return null;
}
int previousPageIndex = this.pageIndex;
PanelItemBuilder builder = new PanelItemBuilder();
if (template.icon() != null)
{
ItemStack clone = template.icon().clone();
if ((boolean) template.dataMap().getOrDefault(INDEXING, false))
{
clone.setAmount(previousPageIndex);
}
builder.icon(clone);
}
if (template.title() != null)
{
builder.name(this.user.getTranslation(this.mainCommand.getWorld(), template.title()));
}
if (template.description() != null)
{
builder.description(this.user.getTranslation(this.mainCommand.getWorld(), template.description(),
TextVariables.NUMBER, String.valueOf(previousPageIndex)));
}
// Add ClickHandler
// Add ClickHandler
builder.clickHandler((panel, user, clickType, i) ->
{
template.actions().forEach(action -> {
if ((clickType == action.clickType() ||
action.clickType() == ClickType.UNKNOWN) && PREVIOUS.equalsIgnoreCase(action.actionType()))
{
// Next button ignores click type currently.
this.pageIndex--;
this.build();
}
});
// Always return true.
return true;
});
// Collect tooltips.
List<String> tooltips = template.actions().stream().
filter(action -> action.tooltip() != null).
map(action -> this.user.getTranslation(this.mainCommand.getWorld(), action.tooltip())).
filter(text -> !text.isBlank()).
collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size())));
// Add tooltips.
if (!tooltips.isEmpty())
{
// Empty line and tooltips.
builder.description("");
builder.description(tooltips);
}
return builder.build();
}
/**
* This method creates and returns locale button.
*
* @return PanelItem that represents locale button.
*/
@Nullable
private PanelItem createLocaleButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot)
{
if (this.elementList.isEmpty())
{
// Does not contain any sticks.
return null;
}
int index = this.pageIndex * slot.amountMap().getOrDefault(LOCALE, 1) + slot.slot();
Locale locale;
if (index >= this.elementList.size())
{
// Out of index.
locale = null;
}
else
{
locale = this.elementList.get(index);
}
if (template.dataMap().containsKey("lang_id"))
{
// Try to find locale with requested ID. if not found, use already collected locale.
locale = this.elementList.stream().
filter(localeID -> localeID.toLanguageTag().equals(template.dataMap().get("lang_id"))).
findFirst().
orElse(locale);
}
return this.createLocaleButton(template, locale);
}
// ---------------------------------------------------------------------
// Section: Other methods
// ---------------------------------------------------------------------
/**
* This method creates locale button.
*
* @return PanelItem that allows to select locale button
*/
private PanelItem createLocaleButton(ItemTemplateRecord template, Locale locale)
{
if (locale == null)
{
// return as locale is null. Empty button will be created.
return null;
}
final String reference = "panels.language.buttons.language.";
// Get settings for island.
PanelItemBuilder builder = new PanelItemBuilder();
BentoBoxLocale language = this.plugin.getLocalesManager().getLanguages().get(locale);
if (template.icon() != null)
{
builder.icon(template.icon().clone());
}
else
{
builder.icon(Objects.requireNonNullElseGet(language.getBanner(),
() -> new ItemStack(Material.WHITE_BANNER, 1)));
}
if (template.title() != null)
{
builder.name(this.user.getTranslation(this.mainCommand.getWorld(), template.title(),
TextVariables.NAME, WordUtils.capitalize(locale.getDisplayName(this.user.getLocale()))));
}
else
{
builder.name(this.user.getTranslation(reference + "name",
TextVariables.NAME, WordUtils.capitalize(locale.getDisplayName(this.user.getLocale()))));
}
final StringBuilder authors = new StringBuilder();
authors.append(this.user.getTranslation(reference + "authors"));
for (String author : language.getAuthors())
{
authors.append("\n").append(this.user.getTranslation(reference + "author", TextVariables.NAME, author));
}
final StringBuilder selected = new StringBuilder();
if (this.user.getLocale().equals(locale))
{
selected.append(this.user.getTranslation(reference + "selected"));
}
String descriptionText;
if (template.description() != null)
{
descriptionText = this.user.getTranslationOrNothing(template.description(),
AUTHORS, authors.toString(),
SELECTED, selected.toString());
}
else
{
descriptionText = this.user.getTranslationOrNothing(reference + "description",
AUTHORS, authors.toString(),
SELECTED, selected.toString());
}
descriptionText = descriptionText.replaceAll("(?m)^[ \\t]*\\r?\\n", "").
replaceAll("(?<!\\\\)\\|", "\n").
replaceAll("\\\\\\|", "|");
builder.description(descriptionText);
// Display actions only for non-selected locales.
List<ItemTemplateRecord.ActionRecords> actions = template.actions().stream().
filter(action -> !this.user.getLocale().equals(locale) &&
(SELECT_ACTION.equalsIgnoreCase(action.actionType()) ||
COMMANDS_ACTION.equalsIgnoreCase(action.actionType()))).
toList();
// Add ClickHandler
builder.clickHandler((panel, user, clickType, i) ->
{
actions.forEach(action -> {
if (clickType == action.clickType() || action.clickType() == ClickType.UNKNOWN)
{
if (SELECT_ACTION.equalsIgnoreCase(action.actionType()))
{
this.plugin.getPlayers().setLocale(this.user.getUniqueId(), locale.toLanguageTag());
this.user.sendMessage("language.edited", "[lang]",
WordUtils.capitalize(locale.getDisplayName(this.user.getLocale())));
// Rebuild panel
this.build();
}
else if (COMMANDS_ACTION.equalsIgnoreCase(action.actionType()))
{
Util.runCommands(user,
Arrays.stream(action.content().
replaceAll(Pattern.quote(TextVariables.LABEL), this.mainCommand.getTopLabel()).
split("\n")).
toList(),
"CHANGE_LOCALE_COMMANDS");
}
}
});
// Always return true.
return true;
});
// Collect tooltips.
List<String> tooltips = actions.stream().
filter(action -> action.tooltip() != null).
map(action -> this.user.getTranslation(this.mainCommand.getWorld(), action.tooltip())).
filter(text -> !text.isBlank()).
collect(Collectors.toCollection(() -> new ArrayList<>(actions.size())));
// Add tooltips.
if (!tooltips.isEmpty())
{
// Empty line and tooltips.
builder.description("");
builder.description(tooltips);
}
return builder.build();
}
// ---------------------------------------------------------------------
// Section: Static methods
// ---------------------------------------------------------------------
/**
* This method is used to open Panel outside this class. It will be much easier to open panel with single method
* call then initializing new object.
*
* @param command The main addon command.
* @param user User who opens panel
*/
public static void openPanel(@NonNull CompositeCommand command, @NonNull User user)
{
new LanguagePanel(command, user).build();
}
// ---------------------------------------------------------------------
// Section: Constants
// ---------------------------------------------------------------------
/**
* This constant is used for button to indicate that it is Language type.
*/
private static final String LOCALE = "LOCALE";
/**
* This constant is used for button to indicate that it is previous page type.
*/
private static final String PREVIOUS = "PREVIOUS";
/**
* This constant is used for button to indicate that it is next page type.
*/
private static final String NEXT = "NEXT";
/**
* This constant is used for indicating that pages should contain numbering.
*/
private static final String INDEXING = "indexing";
/**
* This constant stores value for SELECT action that is used in panels.
*/
private static final String SELECT_ACTION = "SELECT";
/**
* This constant stores value for COMMANDS action that is used in panels.
*/
private static final String COMMANDS_ACTION = "COMMANDS";
/**
* This constant stores value for AUTHORS label that is used in panels.
*/
public static final String AUTHORS = "[authors]";
/**
* This constant stores value for SELECTED label that is used in panels.
*/
public static final String SELECTED = "[selected]";
// ---------------------------------------------------------------------
// Section: Variables
// ---------------------------------------------------------------------
/**
* This variable allows to access plugin object.
*/
private final BentoBox plugin;
/**
* This variable stores the main command object.
*/
private final CompositeCommand mainCommand;
/**
* This variable holds user who opens panel. Without it panel cannot be opened.
*/
private final User user;
/**
* This variable stores filtered elements.
*/
private final List<Locale> elementList;
/**
* This variable holds current pageIndex for multi-page island choosing.
*/
private int pageIndex;
}

View File

@ -1576,15 +1576,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
@ -1742,6 +1733,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 ____ _ ____

View File

@ -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

View File

@ -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

View File

@ -54,7 +54,7 @@ import world.bentobox.bentobox.managers.IslandsManager;
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

View File

@ -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.mock;
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.CommandsManager;
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 class IslandCreationPanelTest {
when(user.getUniqueId()).thenReturn(uuid);
when(user.getPlayer()).thenReturn(player);
when(user.hasPermission(anyString())).thenReturn(true);
when(user.getTranslation(any()))
.thenAnswer((Answer<String>) 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<String>) invocation -> invocation.getArgument(0, String.class));
when(user.getTranslation(any(World.class), any(), any())).thenAnswer((Answer<String>) invocation -> invocation.getArgument(1, String.class));
when(user.getTranslation(any(String.class), any())).thenAnswer((Answer<String>) invocation -> invocation.getArgument(0, String.class));
when(user.getTranslationOrNothing(any(), any())).thenAnswer((Answer<String>) invocation -> invocation.getArgument(0, String.class));
when(user.getTranslation(any(World.class), eq("panels.island_creation.buttons.bundle.name"), any())).
thenAnswer((Answer<String>) invocation -> invocation.getArgument(3, String.class));
when(user.getTranslation(any(World.class), eq("panels.island_creation.buttons.bundle.description"), any())).
thenAnswer((Answer<String>) invocation -> invocation.getArgument(3, String.class));
when(plugin.getDescription()).thenAnswer((Answer<PluginDescriptionFile>) 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 class IslandCreationPanelTest {
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 class IslandCreationPanelTest {
/**
* 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")));
}
}

View File

@ -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.api.mockito.PowerMockito;
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<Locale> localeList;
@Mock
private PanelBuilder pb;
@Mock
private Panel panel;
@Mock
private Inventory inv;
@Mock
private ItemMeta meta;
@Mock
private CompositeCommand command;
@Captor
private ArgumentCaptor<ItemStack> argument;
private Map<Locale, BentoBoxLocale> map;
/**
* Location of the resources folder
*/
private final Path resourcePath = Paths.get("src","test","resources");
/**
*/
@Before
@ -90,7 +90,24 @@ public class LanguagePanelTest {
when(user.getPlayer()).thenReturn(player);
when(user.hasPermission(anyString())).thenReturn(true);
when(user.getTranslation(any())).thenAnswer((Answer<String>) invocation -> invocation.getArgument(0, String.class));
when(user.getTranslation(any(World.class), any(), any())).thenAnswer((Answer<String>) invocation -> invocation.getArgument(1, String.class));
when(user.getTranslation(any(String.class), any())).thenAnswer((Answer<String>) invocation -> invocation.getArgument(0, String.class));
when(user.getTranslationOrNothing(any(), any())).thenAnswer((Answer<String>) 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<String>) 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<PluginDescriptionFile>) invocation ->
new PluginDescriptionFile("BentoBox", "1.0", "world.bentobox.bentobox"));
User.setPlugin(plugin);
// Set up user already
User.getInstance(player);
@ -123,17 +140,17 @@ public class LanguagePanelTest {
}
/**
* 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 class LanguagePanelTest {
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 class LanguagePanelTest {
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());
}

View File

@ -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

View File

@ -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