diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a1c2a23 --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* diff --git a/README.md b/README.md index a119d30..5294fdd 100644 --- a/README.md +++ b/README.md @@ -1 +1,23 @@ -# ControlPanel \ No newline at end of file +# ControlPanel Addon +[![Discord](https://img.shields.io/discord/272499714048524288.svg?logo=discord)](https://discord.bentobox.world) +[![Build Status](https://ci.codemc.org/buildStatus/icon?job=BentoBoxWorld/ControlPanel)](https://ci.codemc.org/job/BentoBoxWorld/job/ControlPanel/) + +This is simple ControlPanel for all BentoBox GameMode addons. Allows to customize GUI for users. + +## How to use + +1. Place the addon jar in the addons folder of the BentoBox plugin +2. Restart the server +3. Use admin command to import control panels. + +## Compatibility + +- [x] BentoBox - 1.7.0 version +- [x] BSkyBlock +- [x] AcidIsland +- [x] SkyGrid +- [x] CaveBlock + +## Information + +More information can be found in [Wiki Pages](https://github.com/BentoBoxWorld/ControlPanel/wiki). diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..2428e13 --- /dev/null +++ b/pom.xml @@ -0,0 +1,245 @@ + + + 4.0.0 + + world.bentobox + controlpanel + ${revision} + + ControlPanel + This is simple ControlPanel for all BentoBox GameMode addons. + https://github.com/BentoBoxWorld/ControlPanel + + 2019 + + + + + scm:git:https://github.com/BentoBoxWorld/ControlPanel.git + scm:git:git@github.com:BentoBoxWorld/ControlPanel.git + https://github.com/BentoBoxWorld/ControlPanel + + + + + + jenkins + http://ci.codemc.org/job/BentoBoxWorld/job/ControlPanel + + + + + + GitHub + https://github.com/BentoBoxWorld/ControlPanel/issues + + + + + + + codemc-snapshots + https://repo.codemc.org/repository/maven-snapshots + + + codemc-releases + https://repo.codemc.org/repository/maven-releases + + + + + + + UTF-8 + UTF-8 + 1.8 + + + 1.14.4-R0.1-SNAPSHOT + + + 1.7.0 + + + ${build.version}-SNAPSHOT + + 1.7.0 + -LOCAL + + + + + ci + + + env.BUILD_NUMBER + + + + + -#${env.BUILD_NUMBER} + + + + + master + + + env.GIT_BRANCH + origin/master + + + + + ${build.version} + + + + + + + + + + spigot-repo + https://hub.spigotmc.org/nexus/content/repositories/snapshots + + + spigotmc-public + https://hub.spigotmc.org/nexus/content/groups/public/ + + + codemc-repo + https://repo.codemc.org/repository/maven-public/ + + + vault-repo + http://nexus.hc.to/content/repositories/pub_releases + + + + + + + org.spigotmc + spigot-api + ${spigot.version} + provided + + + + world.bentobox + bentobox + ${bentobox.version} + provided + + + + + + ${project.name}-${revision}${build.number} + + clean package + + + src/main/resources + true + + + src/main/resources/locales + ./locales + false + + *.yml + *.json + + + + + + org.apache.maven.plugins + maven-clean-plugin + 3.1.0 + + + org.apache.maven.plugins + maven-resources-plugin + 3.1.0 + + + org.apache.maven.plugins + maven-compiler-plugin + 3.7.0 + + ${java.version} + ${java.version} + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.0 + + + org.apache.maven.plugins + maven-jar-plugin + 3.1.0 + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.0.1 + + public + false + -Xdoclint:none + + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.0.1 + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.1.1 + + true + + + + package + + shade + + + + + + org.apache.maven.plugins + maven-install-plugin + 2.5.2 + + + + + diff --git a/src/main/java/world/bentobox/controlpanel/ControlPanelAddon.java b/src/main/java/world/bentobox/controlpanel/ControlPanelAddon.java new file mode 100644 index 0000000..ed4376e --- /dev/null +++ b/src/main/java/world/bentobox/controlpanel/ControlPanelAddon.java @@ -0,0 +1,199 @@ +package world.bentobox.controlpanel; + + +import org.bukkit.Bukkit; + +import world.bentobox.bentobox.api.addons.Addon; +import world.bentobox.bentobox.api.configuration.Config; +import world.bentobox.controlpanel.commands.admin.AdminCommand; +import world.bentobox.controlpanel.commands.user.PlayerCommand; +import world.bentobox.controlpanel.config.Settings; +import world.bentobox.controlpanel.managers.ControlPanelManager; + + +/** + * This is main Addon class. It allows to load it into BentoBox hierarchy. + */ +public class ControlPanelAddon extends Addon +{ + // --------------------------------------------------------------------- + // Section: Methods + // --------------------------------------------------------------------- + + + /** + * Executes code when loading the addon. This is called before {@link #onEnable()}. + * This must be used to setup configuration, worlds and commands. + */ + @Override + public void onLoad() + { + super.onLoad(); + + // in most of addons, onLoad we want to store default configuration if it does not + // exist and load it. + + // Storing default configuration is simple. But be aware, you need + // @StoreAt(filename="config.yml", path="addons/Likes") in header of your Config file. + this.saveDefaultConfig(); + + this.settings = new Config<>(this, Settings.class).loadConfigObject(); + + if (this.settings == null) + { + // If we failed to load Settings then we should not enable addon. + // We can log error and set state to DISABLED. + + this.logError("ControlPanel settings could not load! Addon disabled."); + this.setState(State.DISABLED); + } + } + + + /** + * Executes code when enabling the addon. This is called after {@link #onLoad()}. + *
Note that commands and worlds registration must be done in {@link + * #onLoad()}, if need be. Failure to do so will result in issues such as + * tab-completion not working for commands. + */ + @Override + public void onEnable() + { + // Check if it is enabled - it might be loaded, but not enabled. + + if (this.getPlugin() == null || !this.getPlugin().isEnabled()) + { + Bukkit.getLogger().severe("BentoBox is not available or disabled!"); + this.setState(State.DISABLED); + return; + } + + // Check if addon is not disabled before. + + if (this.getState().equals(State.DISABLED)) + { + Bukkit.getLogger().severe("ControlPanel Addon is not available or disabled!"); + return; + } + + // Initialize data manager + this.manager = new ControlPanelManager(this); + + // If your addon wants to hook into other GameModes, f.e. use flags, then you should + // hook these flags into each GameMode. + + // Fortunately BentoBox provides ability to a list of all loaded GameModes. + + this.getPlugin().getAddonsManager().getGameModeAddons().forEach(gameModeAddon -> { + // In Settings (and config) we define DisabledGameModes, list of GameModes where + // current Addon should not work. + // This is where we do not hook current addon into GameMode addon. + + if (!this.settings.getDisabledGameModes().contains(gameModeAddon.getDescription().getName())) + { + // Each GameMode could have Player Command and Admin Command and we could + // want to integrate our Example Command into these commands. + // It provides ability to call command with GameMode command f.e. "/island example" + + // Of course we should check if these commands exists, as it is possible to + // create GameMode without them. + + gameModeAddon.getPlayerCommand().ifPresent( + playerCommand -> new PlayerCommand(this, playerCommand)); + + gameModeAddon.getAdminCommand().ifPresent( + adminCommand -> new AdminCommand(this, adminCommand)); + } + }); + } + + + /** + * Executes code when reloading the addon. + */ + @Override + public void onReload() + { + super.onReload(); + + // onReload most of addons just need to reload configuration. + // If flags, listeners and handlers were set up correctly via Addon.class then + // they will be reloaded automatically. + + this.settings = new Config<>(this, Settings.class).loadConfigObject(); + + if (this.settings == null) + { + // If we failed to load Settings then we should not enable addon. + // We can log error and set state to DISABLED. + + this.logError("ControlPanel settings could not load! Addon disabled."); + this.setState(State.DISABLED); + } + else + { + this.manager.reload(); + } + } + + + /** + * Executes code when disabling the addon. + */ + @Override + public void onDisable() + { + // onDisable we would like to save exisitng settings. It is not necessary for + // addons that does not have interface for settings editing! + + if (this.settings != null) + { + new Config<>(this, Settings.class).saveConfigObject(this.settings); + } + + this.manager.save(); + } + + +// --------------------------------------------------------------------- +// Section: Getters +// --------------------------------------------------------------------- + + + /** + * Method LikesAddon#getSettings returns the settings of this object. + * + * @return the settings (type Settings) of this object. + */ + public Settings getSettings() + { + return this.settings; + } + + + /** + * Method ControlPanel#getManager returns the manager of this object. + * + * @return the manager (type ControlPanelManager) of this object. + */ + public ControlPanelManager getAddonManager() + { + return this.manager; + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + + /** + * Settings object contains + */ + private Settings settings; + + /** + * Likes addon manager. + */ + private ControlPanelManager manager; +} diff --git a/src/main/java/world/bentobox/controlpanel/commands/admin/AdminCommand.java b/src/main/java/world/bentobox/controlpanel/commands/admin/AdminCommand.java new file mode 100644 index 0000000..2d4b7de --- /dev/null +++ b/src/main/java/world/bentobox/controlpanel/commands/admin/AdminCommand.java @@ -0,0 +1,69 @@ +package world.bentobox.controlpanel.commands.admin; + + +import java.util.List; + +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.controlpanel.ControlPanelAddon; +import world.bentobox.controlpanel.utils.Constants; + + +/** + * This class process /{gamemode_admin_command} example command call. + */ +public class AdminCommand extends CompositeCommand +{ + /** + * This is simple constructor for initializing /{gamemode_admin_command} example command. + * @param addon Our Example addon. + * @param parentCommand Parent Command where we hook our command into. + */ + public AdminCommand(ControlPanelAddon addon, CompositeCommand parentCommand) + { + super(addon, parentCommand, "controlpanel", "cp"); + } + + + /** + * Setups anything that is needed for this command.

It is recommended you + * do the following in this method: + * + */ + @Override + public void setup() + { + this.setPermission("controlpanel.admin"); + this.setParametersHelp(Constants.COMMANDS + "admin.help.parameters"); + this.setDescription(Constants.COMMANDS + "admin.help.description"); + + // Import Command ? + new ImportCommand(this.getAddon(), this); + // Edit Panel ? + + // Settings Panel ? + } + + + /** + * Defines what will be executed when this command is run. + * + * @param user the {@link User} who is executing this command. + * @param label the label which has been used to execute this command. It can be + * {@link CompositeCommand#getLabel()} or an alias. + * @param args the command arguments. + * @return {@code true} if the command executed successfully, {@code false} otherwise. + */ + @Override + public boolean execute(User user, String label, List args) + { + this.showHelp(this, user); + return true; + } +} diff --git a/src/main/java/world/bentobox/controlpanel/commands/admin/ImportCommand.java b/src/main/java/world/bentobox/controlpanel/commands/admin/ImportCommand.java new file mode 100644 index 0000000..f324bf4 --- /dev/null +++ b/src/main/java/world/bentobox/controlpanel/commands/admin/ImportCommand.java @@ -0,0 +1,53 @@ +package world.bentobox.controlpanel.commands.admin; + + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +import world.bentobox.bentobox.api.addons.Addon; +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.util.Util; +import world.bentobox.controlpanel.ControlPanelAddon; +import world.bentobox.controlpanel.utils.Constants; + + +/** + * This class process "cp import" command for admins. + */ +public class ImportCommand extends CompositeCommand +{ + public ImportCommand(Addon addon, CompositeCommand cmd) + { + super(addon, cmd, "import"); + } + + + @Override + public void setup() + { + this.setPermission("controlpanel.admin.import"); + this.setParametersHelp(Constants.COMMANDS + "admin.import.parameters"); + this.setDescription(Constants.COMMANDS + "admin.import.description"); + } + + + @Override + public boolean execute(User user, String label, List args) + { + ((ControlPanelAddon) this.getAddon()).getAddonManager().importControlPanels(user, + this.getWorld(), + !args.isEmpty() && args.get(0).equalsIgnoreCase("overwrite")); + + return true; + } + + + @Override + public Optional> tabComplete(User user, String alias, List args) + { + String lastArg = !args.isEmpty() ? args.get(args.size() - 1) : ""; + return Optional.of(Util.tabLimit(Arrays.asList("overwrite"), lastArg)); + } +} diff --git a/src/main/java/world/bentobox/controlpanel/commands/user/PlayerCommand.java b/src/main/java/world/bentobox/controlpanel/commands/user/PlayerCommand.java new file mode 100644 index 0000000..aeb6998 --- /dev/null +++ b/src/main/java/world/bentobox/controlpanel/commands/user/PlayerCommand.java @@ -0,0 +1,119 @@ +package world.bentobox.controlpanel.commands.user; + + +import java.util.List; + +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.controlpanel.ControlPanelAddon; +import world.bentobox.controlpanel.database.objects.ControlPanelObject; +import world.bentobox.controlpanel.panels.ControlPanelGenerator; +import world.bentobox.controlpanel.utils.Constants; + + +/** + * This class process /{gamemode_player_command} example command call. + */ +public class PlayerCommand extends CompositeCommand +{ + /** + * This is simple constructor for initializing /{gamemode_player_command} example command. + * @param addon Our Example addon. + * @param parentCommand Parent Command where we hook our command into. + */ + public PlayerCommand(ControlPanelAddon addon, CompositeCommand parentCommand) + { + super(addon, parentCommand, "controlpanel", "cp"); + } + + + /** + * Setups anything that is needed for this command.

It is recommended you + * do the following in this method: + *
    + *
  • Register any of the sub-commands of this command;
  • + *
  • Define the permission required to use this command using {@link + * CompositeCommand#setPermission(String)};
  • + *
  • Define whether this command can only be run by players or not using {@link + * CompositeCommand#setOnlyPlayer(boolean)};
  • + *
+ */ + @Override + public void setup() + { + this.setPermission("controlpanel"); + this.setOnlyPlayer(true); + this.setParametersHelp(Constants.COMMANDS + "help.parameters"); + this.setDescription(Constants.COMMANDS + "help.description"); + } + + + /** + * Returns whether the command can be executed by this user or not. It is recommended + * to send messages to let this user know why they could not execute the command. Note + * that this is run previous to {@link #execute(User, String, List)}. + * + * @param user the {@link User} who is executing this command. + * @param label the label which has been used to execute this command. It can be + * {@link CompositeCommand#getLabel()} or an alias. + * @param args the command arguments. + * @return {@code true} if this command can be executed, {@code false} otherwise. + * @since 1.3.0 + */ + @Override + public boolean canExecute(User user, String label, List args) + { + this.panelObject = ((ControlPanelAddon) this.getAddon()). + getAddonManager().getUserControlPanel(user, this.getWorld(), this.getPermissionPrefix()); + + if (this.panelObject == null) + { + if (user.isOp()) + { + user.sendMessage(Constants.ERRORS + "no-valid-panels-op"); + } + else + { + user.sendMessage(Constants.ERRORS + "no-valid-panels"); + } + + return false; + } + else + { + return true; + } + } + + + /** + * Defines what will be executed when this command is run. + * + * @param user the {@link User} who is executing this command. + * @param label the label which has been used to execute this command. It can be + * {@link CompositeCommand#getLabel()} or an alias. + * @param args the command arguments. + * @return {@code true} if the command executed successfully, {@code false} otherwise. + */ + @Override + public boolean execute(User user, String label, List args) + { + // Execute panel + ControlPanelGenerator.open((ControlPanelAddon) this.getAddon(), + user, + this.panelObject, + this.getTopLabel()); + + return true; + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * Panel that must be opened. + */ + private ControlPanelObject panelObject; +} diff --git a/src/main/java/world/bentobox/controlpanel/config/Settings.java b/src/main/java/world/bentobox/controlpanel/config/Settings.java new file mode 100644 index 0000000..7be79aa --- /dev/null +++ b/src/main/java/world/bentobox/controlpanel/config/Settings.java @@ -0,0 +1,67 @@ +package world.bentobox.controlpanel.config; + + +import java.util.HashSet; +import java.util.Set; + +import world.bentobox.bentobox.api.configuration.ConfigComment; +import world.bentobox.bentobox.api.configuration.ConfigEntry; +import world.bentobox.bentobox.api.configuration.ConfigObject; +import world.bentobox.bentobox.api.configuration.StoreAt; + + +/** + * Settings that implements ConfigObject is powerful and dynamic Config Objects that + * does not need custom parsing. If it is correctly loaded, all its values will be available. + * + * Without Getter and Setter this class will not work. + * + * To specify location for config object to be stored, you should use @StoreAt(filename="{config file name}", path="{Path to your addon}") + * To save comments in config file you should use @ConfigComment("{message}") that adds any message you want to be in file. + */ +@StoreAt(filename="config.yml", path="addons/ControlPanel") +@ConfigComment("ControlPanelAddon Configuration [version]") +@ConfigComment("This config file is dynamic and saved when the server is shutdown.") +@ConfigComment("") +public class Settings implements ConfigObject +{ + // --------------------------------------------------------------------- + // Section: Getters and Setters + // --------------------------------------------------------------------- + + + /** + * This method returns the disabledGameModes value. + * + * @return the value of disabledGameModes. + */ + public Set getDisabledGameModes() + { + return disabledGameModes; + } + + + /** + * This method sets the disabledGameModes value. + * + * @param disabledGameModes the disabledGameModes new value. + */ + public void setDisabledGameModes(Set disabledGameModes) + { + this.disabledGameModes = disabledGameModes; + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + + @ConfigComment("") + @ConfigComment("This list stores GameModes in which Likes addon should not work.") + @ConfigComment("To disable addon it is necessary to write its name in new line that starts with -. Example:") + @ConfigComment("disabled-gamemodes:") + @ConfigComment(" - BSkyBlock") + @ConfigEntry(path = "disabled-gamemodes") + private Set disabledGameModes = new HashSet<>(); +} diff --git a/src/main/java/world/bentobox/controlpanel/database/objects/ControlPanelObject.java b/src/main/java/world/bentobox/controlpanel/database/objects/ControlPanelObject.java new file mode 100644 index 0000000..f9fe91f --- /dev/null +++ b/src/main/java/world/bentobox/controlpanel/database/objects/ControlPanelObject.java @@ -0,0 +1,349 @@ +// +// Created by BONNe +// Copyright - 2019 +// + + +package world.bentobox.controlpanel.database.objects; + + +import com.google.gson.annotations.Expose; + +import org.bukkit.Material; +import java.util.List; + +import world.bentobox.bentobox.database.objects.DataObject; + + +/** + * Object that allows to define different control panels. + */ +public class ControlPanelObject implements DataObject +{ + /** + * Constructor ControlPanelObject creates a new ControlPanelObject instance. + */ + public ControlPanelObject() + { + // Empty constructor + } + + +// --------------------------------------------------------------------- +// Section: Getters and Setters +// --------------------------------------------------------------------- + + + /** + * @return the uniqueId + */ + @Override + public String getUniqueId() + { + return this.uniqueId; + } + + + /** + * @param uniqueId - unique ID the uniqueId to set + */ + @Override + public void setUniqueId(String uniqueId) + { + this.uniqueId = uniqueId; + } + + + /** + * Method LikesObject#getGameMode returns the gameMode of this object. + * + * @return the gameMode (type String) of this object. + */ + public String getGameMode() + { + return gameMode; + } + + + /** + * Method LikesObject#setGameMode sets new value for the gameMode of this object. + * + * @param gameMode new value for this object. + */ + public void setGameMode(String gameMode) + { + this.gameMode = gameMode; + } + + + /** + * Method ControlPanelObject#getPermissionSuffix returns the permissionSuffix of this object. + * + * @return the permissionSuffix (type String) of this object. + */ + public String getPermissionSuffix() + { + return permissionSuffix; + } + + + /** + * Method ControlPanelObject#setPermissionSuffix sets new value for the permissionSuffix of this object. + * @param permissionSuffix new value for this object. + * + */ + public void setPermissionSuffix(String permissionSuffix) + { + this.permissionSuffix = permissionSuffix; + } + + + /** + * Method ControlPanelObject#getPanelName returns the panelName of this object. + * + * @return the panelName (type String) of this object. + */ + public String getPanelName() + { + return panelName; + } + + + /** + * Method ControlPanelObject#setPanelName sets new value for the panelName of this object. + * @param panelName new value for this object. + * + */ + public void setPanelName(String panelName) + { + this.panelName = panelName; + } + + + /** + * Method ControlPanelObject#getPanelButtons returns the panelButtons of this object. + * + * @return the panelButtons (type List) of this object. + */ + public List getPanelButtons() + { + return panelButtons; + } + + + /** + * Method ControlPanelObject#setPanelButtons sets new value for the panelButtons of this object. + * @param panelButtons new value for this object. + * + */ + public void setPanelButtons(List panelButtons) + { + this.panelButtons = panelButtons; + } + + + /** + * Method ControlPanelObject#isDefaultPanel returns the defaultPanel of this object. + * + * @return the defaultPanel (type boolean) of this object. + */ + public boolean isDefaultPanel() + { + return defaultPanel; + } + + + /** + * Method ControlPanelObject#setDefaultPanel sets new value for the defaultPanel of this object. + * @param defaultPanel new value for this object. + * + */ + public void setDefaultPanel(boolean defaultPanel) + { + this.defaultPanel = defaultPanel; + } + + +// --------------------------------------------------------------------- +// Section: Private Class +// --------------------------------------------------------------------- + + + /** + * This class allows to add custom buttons to current panel. + */ + public static class ControlPanelButton + { + /** + * Constructor ControlPanelButton creates a new ControlPanelButton instance. + */ + public ControlPanelButton() + { + // Empty constructor + } + + + // --------------------------------------------------------------------- + // Section: Getters and Setters + // --------------------------------------------------------------------- + + + /** + * Method ControlPanelButton#getSlot returns the slot of this object. + * + * @return the slot (type int) of this object. + */ + public int getSlot() + { + return slot; + } + + + /** + * Method ControlPanelButton#setSlot sets new value for the slot of this object. + * @param slot new value for this object. + * + */ + public void setSlot(int slot) + { + this.slot = slot; + } + + + /** + * Method ControlPanelButton#getMaterial returns the material of this object. + * + * @return the material (type Material) of this object. + */ + public Material getMaterial() + { + return material; + } + + + /** + * Method ControlPanelButton#setMaterial sets new value for the material of this object. + * @param material new value for this object. + * + */ + public void setMaterial(Material material) + { + this.material = material; + } + + + /** + * Method ControlPanelButton#getDescription returns the description of this object. + * + * @return the description (type String) of this object. + */ + public String getDescription() + { + return description; + } + + + /** + * Method ControlPanelButton#setDescription sets new value for the description of this object. + * @param description new value for this object. + * + */ + public void setDescription(String description) + { + this.description = description; + } + + + /** + * Method ControlPanelButton#getCommand returns the command of this object. + * + * @return the command (type String) of this object. + */ + public String getCommand() + { + return command; + } + + + /** + * Method ControlPanelButton#setCommand sets new value for the command of this object. + * @param command new value for this object. + * + */ + public void setCommand(String command) + { + this.command = command; + } + + + // --------------------------------------------------------------------- + // Section: Variables + // --------------------------------------------------------------------- + + + /** + * Slot number of the button + */ + @Expose + private int slot; + + /** + * Material icon for button + */ + @Expose + private Material material; + + /** + * Description for the button + */ + @Expose + private String description; + + /** + * Command that will run on the click. + */ + @Expose + private String command; + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * Likes object id. Island ID; + */ + @Expose + private String uniqueId; + + /** + * Indicate that current panel is default one. + */ + @Expose + private boolean defaultPanel; + + /** + * GameMode where current object operates. + */ + @Expose + private String gameMode; + + /** + * Permission suffix for panel to work + */ + @Expose + private String permissionSuffix; + + /** + * Name of current panel + */ + @Expose + private String panelName; + + /** + * List of buttons in current panel + */ + @Expose + private List panelButtons; +} diff --git a/src/main/java/world/bentobox/controlpanel/managers/ControlPanelManager.java b/src/main/java/world/bentobox/controlpanel/managers/ControlPanelManager.java new file mode 100644 index 0000000..c135727 --- /dev/null +++ b/src/main/java/world/bentobox/controlpanel/managers/ControlPanelManager.java @@ -0,0 +1,307 @@ +// +// Created by BONNe +// Copyright - 2019 +// + + +package world.bentobox.controlpanel.managers; + + +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.Database; +import world.bentobox.controlpanel.ControlPanelAddon; +import world.bentobox.controlpanel.database.objects.ControlPanelObject; +import world.bentobox.controlpanel.database.objects.ControlPanelObject.ControlPanelButton; +import world.bentobox.controlpanel.utils.Constants; +import world.bentobox.controlpanel.utils.Utils; + + +/** + * This class manages control panel addon data. + */ +public class ControlPanelManager +{ +// --------------------------------------------------------------------- +// Section: Constructor +// --------------------------------------------------------------------- + + + /** + * Default constructor. + * @param addon Likes Addon instance + */ + public ControlPanelManager(ControlPanelAddon addon) + { + this.addon = addon; + + this.controlFile = new File(this.addon.getDataFolder(), "controlPanels.yml"); + + if (!this.controlFile.exists()) + { + this.addon.saveResource("controlPanels.yml", false); + } + + this.controlPanelCache = new HashMap<>(); + this.controlPanelDatabase = new Database<>(addon, ControlPanelObject.class); + + this.load(); + } + + +// --------------------------------------------------------------------- +// Section: Load Methods +// --------------------------------------------------------------------- + + + /** + * This method loads all control panel objects. + */ + public void load() + { + this.controlPanelCache.clear(); + + this.addon.getLogger().info("Loading control panels..."); + + this.controlPanelDatabase.loadObjects().forEach(this::load); + } + + + /** + * This method loads given controlPanelObject inside cache. + * @param controlPanelObject Object that must be added to cache. + */ + private void load(ControlPanelObject controlPanelObject) + { + // Add object into cache + this.controlPanelCache.put(controlPanelObject.getUniqueId(), controlPanelObject); + } + + + /** + * This method reloads all control panels from database to cache. + */ + public void reload() + { + this.controlPanelCache.clear(); + + this.addon.getLogger().info("Reloading control panels..."); + + this.controlPanelDatabase.loadObjects().forEach(this::load); + } + + +// --------------------------------------------------------------------- +// Section: Save methods +// --------------------------------------------------------------------- + + + /** + * This method saves all cached values into database. + */ + public void save() + { + this.controlPanelCache.values().forEach(this.controlPanelDatabase::saveObject); + } + + +// --------------------------------------------------------------------- +// Section: Wipe methods +// --------------------------------------------------------------------- + + + /** + * This method removes all data from database that referee to given world. + */ + public void wipeData(World world) + { + String gameMode = Utils.getGameMode(world); + + // Empty sorted cache + } + + +// --------------------------------------------------------------------- +// Section: Import methods +// --------------------------------------------------------------------- + + + /** + * This method imports control panels + * + * @param user - user + * @param world - world to import into + * @param overwrite - true if previous ones should be overwritten + * @return true if successful + */ + public void importControlPanels(User user, World world, boolean overwrite) + { + if (!this.controlFile.exists()) + { + user.sendMessage(Constants.ERRORS + "no-file"); + return; + } + + YamlConfiguration config = new YamlConfiguration(); + + try + { + config.load(this.controlFile); + } + catch (IOException | InvalidConfigurationException e) + { + user.sendMessage(Constants.ERRORS + "no-load", + "[message]", + e.getMessage()); + return; + } + + this.readControlPanel(config, user, Utils.getGameMode(world), overwrite); + + // Update biome order. + this.addon.getAddonManager().save(); + } + + + /** + * This method creates control panel object from config file. + * @param config YamlConfiguration that contains all control panels. + * @param user User who calls reading. + * @param gameMode GameMode where current panel works. + * @param overwrite Boolean that indicate if existing control panel should be overwritted. + */ + private void readControlPanel(YamlConfiguration config, User user, final String gameMode, boolean overwrite) + { + int newControlPanelCount = 0; + + ConfigurationSection reader = config.getConfigurationSection("panel-list"); + + for (String keyReference : Objects.requireNonNull(reader).getKeys(false)) + { + final String uniqueId = gameMode + "_" + keyReference; + + if (!this.controlPanelCache.containsKey(uniqueId) || overwrite) + { + ControlPanelObject controlPanel = new ControlPanelObject(); + controlPanel.setUniqueId(uniqueId); + controlPanel.setGameMode(gameMode); + + ConfigurationSection panelSection = reader.getConfigurationSection(keyReference); + + if (panelSection != null) + { + controlPanel.setPanelName(panelSection.getString("panelName", "&1Commands")); + controlPanel.setPermissionSuffix(panelSection.getString("permission", "default")); + controlPanel.setDefaultPanel(panelSection.getBoolean("defaultPanel", false)); + + List buttonList = new ArrayList<>(); + controlPanel.setPanelButtons(buttonList); + + ConfigurationSection buttonListSection = panelSection.getConfigurationSection("buttons"); + + if (buttonListSection != null) + { + buttonListSection.getKeys(false).forEach(slotReference -> { + ControlPanelButton button = new ControlPanelButton(); + button.setSlot(Integer.parseInt(slotReference)); + + ConfigurationSection buttonSection = buttonListSection.getConfigurationSection(slotReference); + + if (buttonSection != null) + { + button.setCommand(buttonSection.getString("command", "[user_command]")); + button.setDescription(buttonSection.getString("description", "").replace("[gamemode]", gameMode.toLowerCase())); + button.setMaterial(Material.matchMaterial(buttonSection.getString("material", "GRASS"))); + + buttonList.add(button); + } + }); + } + + // Save and load in cache. + this.controlPanelDatabase.saveObject(controlPanel); + this.load(controlPanel); + + newControlPanelCount++; + } + } + } + + user.sendMessage(Constants.MESSAGE + "import-count", + "[number]", + String.valueOf(newControlPanelCount)); + } + + +// --------------------------------------------------------------------- +// Section: Processing methods +// --------------------------------------------------------------------- + + + /** + * This method finds corresponding ControlPanel Object for user in given world. + * @param user User who wants to open panel + * @param world World where panel should be opened + * @param permissionPrefix Permission prefix. + * @return ControlPanelObject or null. + */ + public ControlPanelObject getUserControlPanel(User user, World world, String permissionPrefix) + { + String gameMode = Utils.getGameMode(world); + + String permission = Utils.getPermissionValue(user, + permissionPrefix + "controlpanel.panel", + null); + + if (permission == null || !this.controlPanelCache.containsKey(gameMode + "_" + permission)) + { + // Find first default for current game mode. + + return this.controlPanelCache.values().stream(). + filter(panel -> panel.isDefaultPanel() && panel.getGameMode().equals(gameMode)). + findFirst().orElse(null); + } + else + { + return this.controlPanelCache.get(gameMode + "_" + permission); + } + } + + +// --------------------------------------------------------------------- +// Section: Instance Variables +// --------------------------------------------------------------------- + + + /** + * Control Panel Addon instance. + */ + private ControlPanelAddon addon; + + /** + * This database allows to access to all stored control panels. + */ + private Database controlPanelDatabase; + + /** + * This map contains all control panel object linked to their reference game mode. + */ + private Map controlPanelCache; + + /** + * Variable stores template.yml location + */ + private File controlFile; +} diff --git a/src/main/java/world/bentobox/controlpanel/panels/ControlPanelGenerator.java b/src/main/java/world/bentobox/controlpanel/panels/ControlPanelGenerator.java new file mode 100644 index 0000000..e068b57 --- /dev/null +++ b/src/main/java/world/bentobox/controlpanel/panels/ControlPanelGenerator.java @@ -0,0 +1,164 @@ +// +// Created by BONNe +// Copyright - 2019 +// + + +package world.bentobox.controlpanel.panels; + + +import org.bukkit.Material; +import java.util.Comparator; + +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.controlpanel.ControlPanelAddon; +import world.bentobox.controlpanel.database.objects.ControlPanelObject; +import world.bentobox.controlpanel.database.objects.ControlPanelObject.ControlPanelButton; + + +/** + * This class generates Control Panel using given ControlPanelObject. + */ +public class ControlPanelGenerator +{ + /** + * Default constructor. + * @param addon ControlPanelAddon instance. + * @param user User who opens panel. + * @param controlPanel ControlPanelSettings that must be constructed + * @param topLabel Main command. + */ + private ControlPanelGenerator(ControlPanelAddon addon, User user, ControlPanelObject controlPanel, String topLabel) + { + this.addon = addon; + this.user = user; + this.controlPanel = controlPanel; + + this.topLabel = topLabel; + } + + + /** + * This method opens Panel for user with given panel settings. + * @param addon ControlPanelAddon instance. + * @param user User who opens panel. + * @param controlPanel ControlPanelSettings that must be constructed + * @param topLabel Main command. + */ + public static void open(ControlPanelAddon addon, User user, ControlPanelObject controlPanel, String topLabel) + { + new ControlPanelGenerator(addon, user, controlPanel, topLabel).build(); + } + + +// --------------------------------------------------------------------- +// Section: Methods +// --------------------------------------------------------------------- + + + /** + * This method build panel. + */ + private void build() + { + PanelBuilder panelBuilder = new PanelBuilder(). + user(this.user). + name(this.controlPanel.getPanelName()); + + // Sort and add. + this.controlPanel.getPanelButtons().stream(). + sorted(Comparator.comparing(ControlPanelButton::getSlot)). + forEach(button -> { + if (button.getSlot() < 0 || button.getSlot() > 53 || panelBuilder.slotOccupied(button.getSlot())) + { + // TODO: need to manage empty slots as I can put 53 as first item and go *** other buttons :) + panelBuilder.item(this.generateButton(button)); + } + else + { + panelBuilder.item(button.getSlot(), this.generateButton(button)); + } + }); + + panelBuilder.build(); + } + + + /** + * This method generates PanelItem from elements that is defined in ControlPanelButton object. + * @param button ControlPanelButton which must be transformed to PanelItem. + * @return PanelItem that corresponds to ControlPanelButton + */ + private PanelItem generateButton(ControlPanelButton button) + { + final boolean runAsServer = button.getCommand().startsWith("[server]"); + final String parsedCommand = button.getCommand(). + replace("[label]", this.topLabel). + replace("[server]", ""). + replace("[player]", this.user.getName()). + trim(); + + String description = this.addon.getPlugin().getPlaceholdersManager(). + replacePlaceholders(this.user.getPlayer(), button.getDescription()); + + return new PanelItemBuilder(). + name(parsedCommand.isEmpty() ? " " : "/" + parsedCommand). + icon(button.getMaterial() == null ? Material.PAPER : button.getMaterial()). + description(GuiUtils.stringSplit(description, 999)). + clickHandler((panel, user, clickType, slot) -> { + + if (!parsedCommand.isEmpty()) + { + if (runAsServer) + { + if (!this.addon.getServer().dispatchCommand( + this.addon.getServer().getConsoleSender(), + parsedCommand)) + { + this.addon.logError("Problem executing command executed by server!"); + this.addon.logError("Command was : " + parsedCommand); + } + } + else + { + if (!user.performCommand(parsedCommand)) + { + this.addon.logError("Problem executing command executed by player!"); + this.addon.logError("Command was : " + parsedCommand); + } + } + } + + return true; + }). + build(); + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * Instance of Addon + */ + private ControlPanelAddon addon; + + /** + * User who wants to open panel. + */ + private User user; + + /** + * Object that holds information about custom panel structure. + */ + private ControlPanelObject controlPanel; + + /** + * Main command label string + */ + private String topLabel; +} diff --git a/src/main/java/world/bentobox/controlpanel/panels/GuiUtils.java b/src/main/java/world/bentobox/controlpanel/panels/GuiUtils.java new file mode 100644 index 0000000..7c620c4 --- /dev/null +++ b/src/main/java/world/bentobox/controlpanel/panels/GuiUtils.java @@ -0,0 +1,292 @@ +package world.bentobox.controlpanel.panels; + + +import org.apache.commons.lang.WordUtils; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; + + +/** + * This class contains static methods that is used through multiple GUIs. + */ +public class GuiUtils +{ +// --------------------------------------------------------------------- +// Section: Border around GUIs +// --------------------------------------------------------------------- + + + /** + * This method creates border of black panes around given panel with 5 rows. + * @param panelBuilder PanelBuilder which must be filled with border blocks. + */ + public static void fillBorder(PanelBuilder panelBuilder) + { + GuiUtils.fillBorder(panelBuilder, 5, Material.BLACK_STAINED_GLASS_PANE); + } + + + /** + * This method sets black stained glass pane around Panel with given row count. + * @param panelBuilder object that builds Panel. + * @param rowCount in Panel. + */ + public static void fillBorder(PanelBuilder panelBuilder, int rowCount) + { + GuiUtils.fillBorder(panelBuilder, rowCount, Material.BLACK_STAINED_GLASS_PANE); + } + + + /** + * This method sets blocks with given Material around Panel with 5 rows. + * @param panelBuilder object that builds Panel. + * @param material that will be around Panel. + */ + public static void fillBorder(PanelBuilder panelBuilder, Material material) + { + GuiUtils.fillBorder(panelBuilder, 5, material); + } + + + /** + * This method sets blocks with given Material around Panel with given row count. + * @param panelBuilder object that builds Panel. + * @param rowCount in Panel. + * @param material that will be around Panel. + */ + public static void fillBorder(PanelBuilder panelBuilder, int rowCount, Material material) + { + // Only for useful filling. + if (rowCount < 3) + { + return; + } + + for (int i = 0; i < 9 * rowCount; i++) + { + // First (i < 9) and last (i > 35) rows must be filled + // First column (i % 9 == 0) and last column (i % 9 == 8) also must be filled. + + if (i < 9 || i > 9 * (rowCount - 1) || i % 9 == 0 || i % 9 == 8) + { + panelBuilder.item(i, BorderBlock.getPanelBorder(material)); + } + } + } + + +// --------------------------------------------------------------------- +// Section: ItemStack transformations +// --------------------------------------------------------------------- + + + /** + * This BorderBlock is simple PanelItem but without item meta data. + */ + private static class BorderBlock extends PanelItem + { + private BorderBlock(ItemStack icon) + { + super(new PanelItemBuilder(). + icon(icon.clone()). + name(" "). + description(Collections.emptyList()). + glow(false). + clickHandler(null)); + } + + + /** + * This method retunrs BorderBlock with requested item stack. + * @param material of which broder must be created. + * @return PanelItem that acts like border. + */ + private static BorderBlock getPanelBorder(Material material) + { + ItemStack itemStack = new ItemStack(material); + itemStack.getItemMeta().setDisplayName(" "); + + return new BorderBlock(itemStack); + } + } + + + /** + * Simple splitter + * + * @param string - string to be split + * @param warpLength - whn warp should be affected. + * @return list of split strings + */ + public static List stringSplit(String string, int warpLength) + { + // Remove all ending lines from string. + string = string.replaceAll("([\\r\\n])", "\\|"); + string = ChatColor.translateAlternateColorCodes('&', string); + // Check length of lines + List result = new ArrayList<>(); + + Arrays.stream(string.split("\\|")). + map(line -> Arrays.asList(WordUtils.wrap(line, warpLength).split(System.getProperty("line.separator")))). + forEach(result::addAll); + + // Fix colors, as splitting my lost that information. + + for (int i = 0, resultSize = result.size(); i < resultSize; i++) + { + if (i > 0) + { + String lastColor = ChatColor.getLastColors(result.get(i - 1)); + result.set(i, lastColor + result.get(i)); + } + } + + return result; + } + + + /** + * Simple splitter for all strings in list. + * @param stringList - list of string to be split + * @param warpLength - whn warp should be affected. + * @return list of split strings + */ + public static List stringSplit(List stringList, int warpLength) + { + if (stringList.isEmpty()) + { + return stringList; + } + + List newList = new ArrayList<>(stringList.size()); + stringList.stream().map(string -> GuiUtils.stringSplit(string, warpLength)).forEach(newList::addAll); + return newList; + } + + +// --------------------------------------------------------------------- +// Section: Materials +// --------------------------------------------------------------------- + + + /** + * This method transforms material into item stack that can be displayed in users + * inventory. + * @param material Material which item stack must be returned. + * @return ItemStack that represents given material. + */ + public static ItemStack getMaterialItem(Material material) + { + return GuiUtils.getMaterialItem(material, 1); + } + + + /** + * This method transforms material into item stack that can be displayed in users + * inventory. + * @param material Material which item stack must be returned. + * @param amount Amount of ItemStack elements. + * @return ItemStack that represents given material. + */ + public static ItemStack getMaterialItem(Material material, int amount) + { + ItemStack itemStack; + + // Process items that cannot be item-stacks. + if (material.name().contains("WALL_")) + { + // Materials that is attached to wall cannot be showed in GUI. But they should be in list. + itemStack = new ItemStack(Material.getMaterial(material.name().replace("WALL_", ""))); + } + else if (material.name().startsWith("POTTED_")) + { + // Materials Potted elements cannot be in inventory. + itemStack = new ItemStack(Material.getMaterial(material.name().replace("POTTED_", ""))); + } + else if (material.equals(Material.MELON_STEM) || material.equals(Material.ATTACHED_MELON_STEM)) + { + itemStack = new ItemStack(Material.MELON_SEEDS); + } + else if (material.equals(Material.PUMPKIN_STEM) || material.equals(Material.ATTACHED_PUMPKIN_STEM)) + { + itemStack = new ItemStack(Material.PUMPKIN_SEEDS); + } + else if (material.equals(Material.TALL_SEAGRASS)) + { + itemStack = new ItemStack(Material.SEAGRASS); + } + else if (material.equals(Material.CARROTS)) + { + itemStack = new ItemStack(Material.CARROT); + } + else if (material.equals(Material.BEETROOTS)) + { + itemStack = new ItemStack(Material.BEETROOT); + } + else if (material.equals(Material.POTATOES)) + { + itemStack = new ItemStack(Material.POTATO); + } + else if (material.equals(Material.COCOA)) + { + itemStack = new ItemStack(Material.COCOA_BEANS); + } + else if (material.equals(Material.KELP_PLANT)) + { + itemStack = new ItemStack(Material.KELP); + } + else if (material.equals(Material.REDSTONE_WIRE)) + { + itemStack = new ItemStack(Material.REDSTONE); + } + else if (material.equals(Material.TRIPWIRE)) + { + itemStack = new ItemStack(Material.STRING); + } + else if (material.equals(Material.FROSTED_ICE)) + { + itemStack = new ItemStack(Material.ICE); + } + else if (material.equals(Material.END_PORTAL) || material.equals(Material.END_GATEWAY) || material.equals(Material.NETHER_PORTAL)) + { + itemStack = new ItemStack(Material.PAPER); + } + else if (material.equals(Material.BUBBLE_COLUMN) || material.equals(Material.WATER)) + { + itemStack = new ItemStack(Material.WATER_BUCKET); + } + else if (material.equals(Material.LAVA)) + { + itemStack = new ItemStack(Material.LAVA_BUCKET); + } + else if (material.equals(Material.FIRE)) + { + itemStack = new ItemStack(Material.FIRE_CHARGE); + } + else if (material.equals(Material.AIR) || material.equals(Material.CAVE_AIR) || material.equals(Material.VOID_AIR)) + { + itemStack = new ItemStack(Material.GLASS_BOTTLE); + } + else if (material.equals(Material.PISTON_HEAD) || material.equals(Material.MOVING_PISTON)) + { + itemStack = new ItemStack(Material.PISTON); + } + else + { + itemStack = new ItemStack(material); + } + + itemStack.setAmount(amount); + + return itemStack; + } +} \ No newline at end of file diff --git a/src/main/java/world/bentobox/controlpanel/utils/Constants.java b/src/main/java/world/bentobox/controlpanel/utils/Constants.java new file mode 100644 index 0000000..65172b6 --- /dev/null +++ b/src/main/java/world/bentobox/controlpanel/utils/Constants.java @@ -0,0 +1,51 @@ +// +// Created by BONNe +// Copyright - 2019 +// + + +package world.bentobox.controlpanel.utils; + + +public class Constants +{ + /** + * Reference string to GUI Titles in translations. + */ + public static final String TITLE = "controlpanel.gui.titles."; + + /** + * Reference string to GUI Buttons in translations. + */ + public static final String BUTTON = "controlpanel.gui.buttons."; + + /** + * Reference string to GUI Descriptions in translations. + */ + public static final String DESCRIPTION = "controlpanel.gui.descriptions."; + + /** + * Reference string to Messages in translations. + */ + public static final String MESSAGE = "controlpanel.messages."; + + /** + * Reference string to Errors in translations. + */ + public static final String ERRORS = "controlpanel.errors."; + + /** + * Reference string to Questions in translations. + */ + public static final String QUESTIONS = "controlpanel.questions."; + + /** + * Reference string to Commands in translations. + */ + public static final String COMMANDS = "controlpanel.commands."; + + /** + * Reference string to types in translations. + */ + public static final String TYPES = "controlpanel.types."; +} diff --git a/src/main/java/world/bentobox/controlpanel/utils/Utils.java b/src/main/java/world/bentobox/controlpanel/utils/Utils.java new file mode 100644 index 0000000..6e82522 --- /dev/null +++ b/src/main/java/world/bentobox/controlpanel/utils/Utils.java @@ -0,0 +1,146 @@ +// +// Created by BONNe +// Copyright - 2019 +// + + +package world.bentobox.controlpanel.utils; + + +import org.bukkit.World; +import org.bukkit.permissions.PermissionAttachmentInfo; +import java.util.List; +import java.util.stream.Collectors; + +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.api.addons.GameModeAddon; +import world.bentobox.bentobox.api.user.User; + + +public class Utils +{ + /** + * This method gets string value of given permission prefix. If user does not have + * given permission or it have all (*), then return default value. + * @param user User who's permission should be checked. + * @param permissionPrefix Prefix that need to be found. + * @param defaultValue Default value that will be returned if permission not found. + * @return String value that follows permissionPrefix. + */ + public static String getPermissionValue(User user, String permissionPrefix, String defaultValue) + { + if (user.isPlayer()) + { + if (permissionPrefix.endsWith(".")) + { + permissionPrefix = permissionPrefix.substring(0, permissionPrefix.length() - 1); + } + + String permPrefix = permissionPrefix + "."; + + List permissions = user.getEffectivePermissions().stream(). + map(PermissionAttachmentInfo::getPermission). + filter(permission -> permission.startsWith(permPrefix)). + collect(Collectors.toList()); + + for (String permission : permissions) + { + if (permission.contains(permPrefix + "*")) + { + // * means all. So continue to search more specific. + continue; + } + + String[] parts = permission.split(permPrefix); + + if (parts.length > 1) + { + return parts[1]; + } + } + } + + return defaultValue; + } + + + /** + * This method transforms given World into GameMode name. If world is not a GameMode + * world then it returns null. + * @param world World which gameMode name must be found out. + * @return GameMode name or null. + */ + public static String getGameMode(World world) + { + return BentoBox.getInstance().getIWM().getAddon(world). + map(gameModeAddon -> gameModeAddon.getDescription().getName()). + orElse(null); + } + + + /** + * This method transforms given GameMode into name. + * @param gameModeAddon GameMode which name must be returned. + * @return GameMode name. + */ + public static String getGameMode(GameModeAddon gameModeAddon) + { + return gameModeAddon.getDescription().getName(); + } + + + /** + * This method allows to get next value from array list after given value. + * @param values Array that should be searched for given value. + * @param currentValue Value which next element should be found. + * @param Instance of given object. + * @return Next value after currentValue in values array. + */ + public static T getNextValue(T[] values, T currentValue) + { + for (int i = 0; i < values.length; i++) + { + if (values[i].equals(currentValue)) + { + if (i + 1 == values.length) + { + return values[0]; + } + else + { + return values[i + 1]; + } + } + } + + return currentValue; + } + + + /** + * This method allows to get previous value from array list after given value. + * @param values Array that should be searched for given value. + * @param currentValue Value which previous element should be found. + * @param Instance of given object. + * @return Previous value before currentValue in values array. + */ + public static T getPreviousValue(T[] values, T currentValue) + { + for (int i = 0; i < values.length; i++) + { + if (values[i].equals(currentValue)) + { + if (i > 0) + { + return values[i - 1]; + } + else + { + return values[values.length - 1]; + } + } + } + + return currentValue; + } +} diff --git a/src/main/resources/addon.yml b/src/main/resources/addon.yml new file mode 100644 index 0000000..fa5abb9 --- /dev/null +++ b/src/main/resources/addon.yml @@ -0,0 +1,74 @@ +# Name of your addon that wil lbe used in displaying it. +name: ControlPanel +# Addon main class. This class should extend Addon.class +main: world.bentobox.controlpanel.ControlPanelAddon +# Version of your addon. Can use maven variables. +version: ${version}${build.number} +# Allow to send metric about this addon usage. +metrics: true +# GitHub version check. Will work only for GitHub. +repository: 'BentoBoxWorld/ControlPanel' +# Icon of addon that will be displayed in Addon Manager. +# Must use Material.values() with uppercase. +icon: 'COMMAND_BLOCK' + +# List of addon authors. +authors: + - BONNe + +# Soft dependencies of current addon. +softdepend: AcidIsland, BSkyBlock, CaveBlock, SkyGrid + +# List of addon permissions +permissions: + acidisland.controlpanel: + description: Allows access to control panel command + default: true + acidisland.controlpanel.admin: + description: Allows access to control panel admin command + default: op + acidisland.controlpanel.admin.import: + description: Allows importing control panels from file + default: op + acidisland.controlpanel.panel.default: + description: Allows access to default control panel layout + default: true + + bskyblock.controlpanel: + description: Allows access to control panel command + default: true + bskyblock.controlpanel.admin: + description: Allows access to control panel admin command + default: op + bskyblock.controlpanel.admin.import: + description: Allows importing control panels from file + default: op + bskyblock.controlpanel.panel.default: + description: Allows access to default control panel layout + default: true + + caveblock.controlpanel: + description: Allows access to control panel command + default: true + caveblock.controlpanel.admin: + description: Allows access to control panel admin command + default: op + caveblock.controlpanel.admin.import: + description: Allows importing control panels from file + default: op + caveblock.controlpanel.panel.default: + description: Allows access to default control panel layout + default: true + + skygrid.controlpanel: + description: Allows access to control panel command + default: true + skygrid.controlpanel.admin: + description: Allows access to control panel admin command + default: op + skygrid.controlpanel.admin.import: + description: Allows importing control panels from file + default: op + skygrid.controlpanel.panel.default: + description: Allows access to default control panel layout + default: true \ No newline at end of file diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..84d005c --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,7 @@ +# ControlPanel Configuration ${version} +# +# This list stores GameModes in which Likes addon should not work. +# To disable addon it is necessary to write its name in new line that starts with -. Example: +# disabled-gamemodes: +# - BSkyBlock +disabled-gamemodes: [] diff --git a/src/main/resources/controlPanels.yml b/src/main/resources/controlPanels.yml new file mode 100644 index 0000000..fd83568 --- /dev/null +++ b/src/main/resources/controlPanels.yml @@ -0,0 +1,62 @@ +# Control Panel Template file +# Command Parser Values: +# [player] - [player] in command will be replaced with username, +# f.e. 'give [player] diamond' will result in '/give BONNe1704 diamond' +# [server] - Command will be run by console, f.e. '[server] op [player]' will result in '/op BONNe1704' +# [label] - [label] in command will be replaced with corresponding GameMode user command, +# f.e. '[label] challenges' in BSkyblock will result in 'island challenges' +# material is used from Matherial.match +# permission is a suffix that will be added to the end of "[gamemode].controlpanel.panel.[suffix]". +# Adding permission means that user will open control panel defined by permission. +# If user will have multiple panel permissions, it will open first encountered with default flag. +# If user will have '*', it will open first defined panel with default flag. +# description can include color codes using & and new lines using | and placeholders with with % at the +# start and the end. [gamemode] in placeholders will be converted to correct string. +# number before each button means slot location + +panel-list: + default: + defaultPanel: true + panelName: '&1Commands' + permission: 'default' + buttons: + 0: + material: GRASS + description: 'Go to your island' + command: '[label] go' + 8: + material: LAVA_BUCKET + description: 'Open your island settings' + command: '[label] settings' + 3: + material: SKULL_ITEM + description: 'List team members' + command: '[label] team' + 1: + material: WHITE_BED + description: 'Set your home here' + command: '[label] sethome' + 9: + material: ACACIA_STAIRS + description: 'Island level |Level: %Level_[gamemode]-island-level%' + command: '[label] level' + 10: + material: BOOK + description: 'List the Top 10 islands' + command: '[label] top' + 12: + material: OAK_SIGN + description: 'List warps available' + command: '[label] warps' + 5: + material: BEDROCK + description: 'Go to world spawn' + command: '[label] spawn' + 14: + material: OAK_SAPLING + description: 'Biomes' + command: '[label] biomes' + 15: + material: ENCHANTING_TABLE + description: 'Challenges' + command: '[label] challenges' \ No newline at end of file diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml new file mode 100644 index 0000000..059eaff --- /dev/null +++ b/src/main/resources/locales/en-US.yml @@ -0,0 +1,19 @@ +controlpanel: + messages: + import-count: "Imported [number] control panels" + errors: + no-load: "&4Cannot load controlPanels.yml file: [message]" + no-file: "&4Missing controlPanels.yml file" + no-valid-panels: "&4Could not find any valid control panel." + no-valid-panels-op: "&4Could not find any valid control panel. You should try to import it using /[admin] cp import" + commands: + admin: + help: + parameters: "" + description: "shows admin help commands" + import: + parameters: "[overwrite]" + description: "imports control panels from controlPanels.yml" + help: + parameters: "" + description: "opens control panel" \ No newline at end of file diff --git a/src/main/resources/locales/es.yml b/src/main/resources/locales/es.yml new file mode 100644 index 0000000..4289070 --- /dev/null +++ b/src/main/resources/locales/es.yml @@ -0,0 +1,19 @@ +--- +controlpanel: + messages: + import-count: Paneles de control importados [number] + errors: + no-load: "&4No se puede cargar el archivo controlPanels.yml: [message]" + no-file: "&4Archivo Missing controlPanels.yml" + no-valid-panels: "&4No se pudo encontrar ningún panel de control válido." + no-valid-panels-op: "&4No se pudo encontrar ningún panel de control válido. Deberías + intentar importarlo usando / [admin] cp import" + commands: + admin: + help: + description: muestra los comandos de ayuda del administrador + import: + parameters: "[overwrite]" + description: importa paneles de control desde controlPanels.yml + help: + description: abre el panel de control diff --git a/src/main/resources/locales/fr.yml b/src/main/resources/locales/fr.yml new file mode 100644 index 0000000..b6bce4a --- /dev/null +++ b/src/main/resources/locales/fr.yml @@ -0,0 +1,23 @@ +--- +controlpanel: + messages: + import-count: " [number] panneaux de commande importés." + errors: + no-load: "&4Impossible de charger le fichier controlPanels.yml: [message]" + no-file: "&4Fichier controlPanels.yml manquant" + no-valid-panels: "&4Impossible de trouver un panneau de configuration valide.\n" + no-valid-panels-op: "&4Impossible de trouver un panneau de configuration valide. + Vous devriez essayer de l'importer en utilisant /[admin] cp import" + commands: + admin: + help: + description: affiche les commandes d'aide de l'administrateur + import: + parameters: "[overwrite]" + description: |2- + + importe les panneaux de contrôle de controlPanels.yml + help: + description: |2- + + ouvre le panneau de configuration diff --git a/src/main/resources/locales/lv.yml b/src/main/resources/locales/lv.yml new file mode 100644 index 0000000..63ab403 --- /dev/null +++ b/src/main/resources/locales/lv.yml @@ -0,0 +1,19 @@ +--- +controlpanel: + messages: + import-count: Importēti [number] vadības paneļi + errors: + no-load: "&4Nevar ielādēt controlPanels.yml failu: [message]" + no-file: "&4Pietrūkst fails controlPanels.yml" + no-valid-panels: "&4Nevarēja atrast derīgu vadības paneli." + no-valid-panels-op: "&4Nevarētja atrast derīgu vadības paneli. Tev vajadzētu tos + ielādēt izmantojot /[admin] cp import" + commands: + admin: + import: + parameters: "[overwrite]" + description: ielādē vadības paneļus no controlPanels.yml + help: + description: parāda administratora palīdzības komandas + help: + description: atver vadības paneli