Release 1.17.3 (#1827)

* Version 1.17.1

* Add homes command to default player options.

* Implements better online player counter. (#1791)

Current one is based on online player count when bStats sends data to the server.
This one will send data about amount of players who logged in the server.

* Add Boxed Gamemode to the list. (#1793)

* Add Bank addon (#1792)

* Add Holographic Displays as SoftDepend for AOneBlock (#1794)

* Custom date time format support for /<admin> info <player> (#1783)

Fixes #1720

* Parent/sub-flag support, split up and designate CONTAINER flag as parent flag (#1784)

* Split CONTAINER flag into multiple flags

CONTAINER split into
- CONTAINER (Chest/Minecart Chest)
- BARREL (Barrel)
- COMPOSTER (Composter)
- FLOWER_POT (Flower Pot)
- SHULKER_BOX (Shulker Box)
- TRAPPED_CHEST (Trapped Chest)

Fixes #1777

* Add subflag support

* Create container parent flag, chest subflag

* Remove extra string from when CHEST was CONTAINER

* Fix incorrect flag specified on fired event in IslandToggleClick

* Add missing world subflag event firing

* Remove extra import

* Add world setting flag for island visitors keep inventory (#1785)

* Implement 3 bar charts: addons, gamemodes, hooks (#1790)

BStats supports sending Bar chart data, however, it does not display it via their site directly.
It can be called manually, to view.

PieChart does not work very well for addons and hooks. BarChart however allows viewing each addon separately. 

This change allows sending data to the server about bar charts.

* Update action versions

* Declare distribution - adopt

* Cache Java Maven files differently.

* Try dependency for shade snapshot

* Add support for Minecraft 1.17.1

* Fix test by incrementing listener value check

There is a new listener now.

* Spigot 1.17.1

* Fix test for InventoryListener

* Re-order repos.

* Downgrading to 1.17 Spigot for now to enable building.

* Try pluginRespositories tag in POM to enable Github actions

* Version 1.17.2

* Add alternative Addon#getIslandManager method (#1797)

We have already done this to the main BentoBox class. But add-ons missed it, and it still has this weird structure: Addon#getIslands().getIslands().

This will just add the same method as it is already in BentoBox class.

* Fix bucket dupe (#1806)

Fix https://discord.com/channels/272499714048524288/310623455462686720/867790395442462760

* Removes unneeded exploit protection code for skulls (#1810)

This no longer seems to be required with 1.17.1

https://github.com/BentoBoxWorld/BSkyBlock/issues/430

* Quote filename of addon that cannot be loaded.

Provides a better understanding of which addon failed.

* Remove update when pasting chest.

* Version 1.17.3

* Java upgrade (#1814)

* Version 1.17.3

* Use Map.of and List.of instead of Immutable collections

* Replace explicit type argument.

* Replace lamba with method reference

* Replace condition with Objects.requireNonNullElseGet

* Use String.repeat

* Use new switch expressions

* Use instanceof patten variables which are more compact

* Fuse toUnmodifiableList into stream and return it.

* Remove unnecessary toString() calls.

* Remove unnecessary public

* Extracted common part from if

* Simplify conditional expressions

* Remove unused IOExceptions

* Cast to long

* Use Map.putAll

* Use primitives

* Clarify what is null or not

* Addedd @Serial annotation introduced with Java 14.

* Use Optional.isEmpty instead of !isPresent

* Use flatMap then ifPresent

* Just use Arrays.stream

* Swap map and filter for null with Objects::nonNull

* Use expression lambda

* Update JavaDoc version to 16

* Make spawn protection area square instead of circle.

https://github.com/BentoBoxWorld/BentoBox/issues/1819

* Improve ItemParser code. (#1821)

* Improve ItemParser code.

Add ability to parse text if Material is just a single string.
Add ability to parse player heads.

Add comments to the code.

* Fixes failing test.

* Added deprecation suppression.

* Added Pladdon to loadbefore.

* Prevent bucket duping when scooping obsidian Part 35

https://github.com/BentoBoxWorld/BentoBox/issues/1825

* Warns visitors that PVP is active if they teleport to an island

https://github.com/BentoBoxWorld/BentoBox/issues/1780

* Prevents repeated portaling when nether is disabled. (#1826)

* Prevents repeated portaling when nether is disabled.

https://github.com/BentoBoxWorld/BentoBox/issues/1782

* Fix test

* Hex pr 1820 (#1822)

* Fix @since for subflags. (#1831)

* Add Citizens to Softdepend list for NPC support (#1834)

* Fix color test.

* Set the default game mode when player makes island.

* Corrects JavaDocs link

Fixes https://github.com/BentoBoxWorld/BentoBox/issues/1838

* Update to 3.3.1-SNAPSHOT for shade plugin.

* Package info files for better JavaDocs (#1839)

* Added package info files for better JavaDocs

* Implement Flag icon changing via Locales file. (#1829)

This commit contains 2 changes:
- An option for Flag to use icon that is defined in locales after "icon" string.
- An option for ItemParser to parse icon or return given value, if parsing was not successful.

The flag option is not ideal, but it is simpler and easier to maintain then adding new config section where icons can be changed, as the locales file already contains a lot of info about each flag.

* Support glowing ink in blueprints (#1842)

Fixes https://github.com/BentoBoxWorld/BentoBox/issues/1837

* Panel template (#1841)

* Implement basic functionality to read data from template panels.

Create TemplateReader class that has static method which generates a PanelTemplateRecord. This record contains every necessary information from user created template file so everyone could use it to generate a functional panel.
These classes are just for reading templates and do not create actual panel.

* Add template clearing via bentobox reload command.

* Fix version command to show addon state.

* Fix German flag banner description.

Added more debug around error reporting.

* Make variables final if they can be. (#1843)

* Make variables final if they can be.

* Do not use final so that test can pass.

For testing, we use a trick to set this variable, but it won't work if
it is final. Right now, I'd like to keep the test.

* Static code analysis (#1844)

* StringBuffer (Java 5) may be declared as StringBuilder

* Replace map with flatMap

* Use instanceof naming

* No need to specify paramter types.

* Remove verbose code

* Fix JavaDoc issues

* Make internal class a record.

* Remove unused import.

* Make internal class a record.

* Made internal class a record.

* Removed unused import

* Fix typos

* Fix typo in test.

* Prevent home teleport when already home teleporting

Home teleporting is async so it's possible to issue the command multiple
times. This puts a flag in so that if a playeer is mid-teleport, then
issuing the go command will just repeat the text that the player is
teleporting home.

https://github.com/BentoBoxWorld/BentoBox/issues/1637

* Extracted island info from the Island object.

This is in preparation to have a different info for players than admins.

https://github.com/BentoBoxWorld/BentoBox/issues/1501

* Slimmer trimmer island info for players.

https://github.com/BentoBoxWorld/BentoBox/issues/1501

* Fixes coop and trust invites when team invites are not allowed

If a member or sub-owner has coop or trust invite capability and not
team invite rank, and confirm invites is on, then the invites were not
working.

Fixes https://github.com/BentoBoxWorld/BentoBox/issues/1452

* Remove invulnerable visitor protection if island is in PVP mode

We now have alerts if a player teleports to a PVP island.

https://github.com/BentoBoxWorld/BentoBox/issues/668

* Fixes NPE in admin tp if nether or end worlds don't exist.

* Adds arrow sound when teleporting into a PVP island.

* Fix PVPListenerTest

Visitors are no longer invincible when on a PVP island.

* Fix test failure in InvincibleVisitorsListenerTest

* Minor JavaDoc correction.

* Go command was not working second time.

* Prevents console errors for missing icons in locale files.

* Fixes PlAddon disabling issue. (#1847)

There was a bug that did not unload PlAddon classes from JVM.
This change should fix it, as it will unload PlAddons via pluginLoader.

* Clear code - set worlds once on construction.

* Fixes a bug when Potion metadata was not applied (#1849)

There was missing potion meta data applying after creating an item. 
This affects both: Potions and Tipped arrows.

* Adds force-shown lines to the template. (#1850)

* Adds force-shown lines to the template.
Fixes a bug when template type was not selected correctly.
Changes "name" to "title" for border and background icons.

* Create TemplatedPanel and TemplatedPanelBuilder.

TemplatedPanel uses PanelTemplateRecord to create a functional panel.
Builder contains button builder callbacks that are used to create buttons with custom data.

* Use final vars for lambdas to avoid them changing async

The processes could run async, especially when chunk loading took a
while, so using fields that could change would result in blocks being
missed, especially in the nether or end. Switching to final vars avoids
this.

Fixes https://github.com/BentoBoxWorld/BentoBox/issues/1840

* Remove travis.

Co-authored-by: BONNe <bonne@bonne.id.lv>
Co-authored-by: Fredthedoggy <45927799+Fredthedoggy@users.noreply.github.com>
Co-authored-by: Justin <jstnf@users.noreply.github.com>
Co-authored-by: gecko10000 <60494179+levtey@users.noreply.github.com>
This commit is contained in:
tastybento 2021-09-10 18:39:16 -07:00 committed by GitHub
parent 1a7b7cdeb7
commit a6d70d6e90
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
224 changed files with 3501 additions and 1639 deletions

View File

@ -1,23 +0,0 @@
language: java
sudo: false
addons:
sonarcloud:
organization: "bentobox-world"
jdk:
- openjdk8
- openjdk11
matrix:
allow_failures:
- jdk: openjdk11
script:
- mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent install
- wget https://github.com/sormuras/bach/raw/master/install-jdk.sh && . ./install-jdk.sh -F 11 -L GPL
- mvn sonar:sonar -Dsonar.projectKey=BentoBoxWorld_BentoBox
cache:
directories:
- '$HOME/.m2/repository'
- '$HOME/.sonar/cache'

View File

@ -38,7 +38,7 @@ There are also plenty of other official or community-made Addons you can try and
## Documentation
* Start reading: [https://docs.bentobox.world](https://docs.bentobox.world)
* For developers: [Javadocs](https://bentoboxworld.github.io/BentoBox/)
* For developers: [Javadocs](https://ci.codemc.io/job/BentoBoxWorld/job/BentoBox/ws/target/apidocs/index.html)
## Downloads

View File

@ -83,7 +83,7 @@
<!-- Do not change unless you want different name for local builds. -->
<build.number>-LOCAL</build.number>
<!-- This allows to change between versions. -->
<build.version>1.17.2</build.version>
<build.version>1.17.3</build.version>
</properties>
<!-- Profiles will allow to automatically change build version. -->
@ -353,7 +353,7 @@
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<source>8</source>
<source>16</source>
<show>private</show>
<failOnError>false</failOnError>
<additionalJOption>-Xdoclint:none</additionalJOption>
@ -387,7 +387,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.3.0-SNAPSHOT</version>
<version>3.3.1-SNAPSHOT</version>
<configuration>
<minimizeJar>true</minimizeJar>
<relocations>

View File

@ -263,7 +263,7 @@ public class BentoBox extends JavaPlugin {
}
private void fireCriticalError(String message, String error) {
logError("*****************CRITIAL ERROR!******************");
logError("*****************CRITICAL ERROR!******************");
logError(message);
logError(error + " Disabling BentoBox...");
logError("*************************************************");

View File

@ -40,7 +40,7 @@ public abstract class Addon {
private FileConfiguration config;
private File dataFolder;
private File file;
private Map<String, AddonRequestHandler> requestHandlers = new HashMap<>();
private final Map<String, AddonRequestHandler> requestHandlers = new HashMap<>();
protected Addon() {
state = State.DISABLED;

View File

@ -30,8 +30,8 @@ import world.bentobox.bentobox.managers.AddonsManager;
public class AddonClassLoader extends URLClassLoader {
private final Map<String, Class<?>> classes = new HashMap<>();
private Addon addon;
private AddonsManager loader;
private final Addon addon;
private final AddonsManager loader;
public AddonClassLoader(AddonsManager addonsManager, YamlConfiguration data, File jarFile, ClassLoader parent)
throws InvalidAddonInheritException,

View File

@ -163,9 +163,12 @@ public final class AddonDescription {
}
public static class Builder {
private @NonNull String main;
private @NonNull String name;
private @NonNull String version;
private @NonNull
final String main;
private @NonNull
final String name;
private @NonNull
final String version;
private @NonNull String description = "";
private @NonNull List<String> authors = new ArrayList<>();
private @NonNull List<String> dependencies = new ArrayList<>();

View File

@ -54,4 +54,13 @@ public abstract class Pladdon extends JavaPlugin {
}
}
/**
* This method enables marks pladdons as enabled.
* By default, enable status is not set because onEnable and onLoad is not triggered.
*/
public void setEnabled() {
this.setEnabled(true);
}
}

View File

@ -1,10 +1,13 @@
package world.bentobox.bentobox.api.addons.exceptions;
import java.io.Serial;
public abstract class AddonException extends Exception {
/**
*
*/
@Serial
private static final long serialVersionUID = 4203162022348693854L;
protected AddonException(String errorMessage){

View File

@ -1,8 +1,11 @@
package world.bentobox.bentobox.api.addons.exceptions;
import java.io.Serial;
public class AddonRequestException extends AddonException
{
private static final long serialVersionUID = -5698456013070166174L;
@Serial
private static final long serialVersionUID = -5698456013070166174L;
public AddonRequestException(String errorMessage) {
super(errorMessage);

View File

@ -1,5 +1,7 @@
package world.bentobox.bentobox.api.addons.exceptions;
import java.io.Serial;
/**
* @since 1.11.0
*/
@ -8,6 +10,7 @@ public class InvalidAddonDescriptionException extends AddonException {
/**
*
*/
@Serial
private static final long serialVersionUID = 7741502900847049986L;
public InvalidAddonDescriptionException(String errorMessage) {

View File

@ -1,5 +1,6 @@
package world.bentobox.bentobox.api.addons.exceptions;
import java.io.Serial;
import java.util.logging.Level;
import org.bukkit.Bukkit;
@ -9,6 +10,7 @@ public class InvalidAddonFormatException extends AddonException {
/**
*
*/
@Serial
private static final long serialVersionUID = 7741502900847049986L;
public InvalidAddonFormatException(String errorMessage) {

View File

@ -1,10 +1,13 @@
package world.bentobox.bentobox.api.addons.exceptions;
import java.io.Serial;
public class InvalidAddonInheritException extends AddonException {
/**
*
*/
@Serial
private static final long serialVersionUID = -5847358994397613244L;
public InvalidAddonInheritException(String errorMessage) {

View File

@ -0,0 +1,10 @@
/**
* This package covers Addon exceptions
* <p>
* These exceptions can be thrown when the addon is loaded.
* </p>
*
* @since 1.0
* @author tastybento
*/
package world.bentobox.bentobox.api.addons.exceptions;

View File

@ -0,0 +1,12 @@
/**
* This package covers all addon-specific API
* <p>
* The Addon class and the associated Pladdon are like Bukkit plugins
* but contain extra API specific for BentoBox games.
* </p>
*
* @since 1.0
* @author tastybento
*
*/
package world.bentobox.bentobox.api.addons;

View File

@ -20,7 +20,7 @@ public class AddonRequestBuilder
{
private String addonName;
private String requestLabel;
private Map<String, Object> metaData = new HashMap<>();
private final Map<String, Object> metaData = new HashMap<>();
/**
* Define the addon you wish to request.

View File

@ -0,0 +1,13 @@
/**
* API to enable plugins to request data from addons.
* <p>
* Addons can expose data that they want to expose. To access it, call this class with the appropriate addon name,
* the label for the data that is requested and if required, a map of key-value pairs that will be given to the addon.
*
* <b>Note</b> Since BentoBox 1.17.0, Addons can be declared as Pladdons and be loaded by the Bukkit classloader. This
* enables Plugins to access Addon methods directly so this API is not required.
* </p>
*
* @author tastybento
*/
package world.bentobox.bentobox.api.addons.request;

View File

@ -33,7 +33,7 @@ import world.bentobox.bentobox.managers.PlayersManager;
import world.bentobox.bentobox.util.Util;
/**
* BSB composite command
* BentoBox composite command. Provides an abstract implementation of a command.
* @author tastybento
* @author Poslovitch
*/
@ -78,12 +78,12 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Map of sub commands
*/
private Map<String, CompositeCommand> subCommands;
private final Map<String, CompositeCommand> subCommands;
/**
* Map of aliases for subcommands
*/
private Map<String, CompositeCommand> subCommandAliases;
private final Map<String, CompositeCommand> subCommandAliases;
/**
* The command chain from the very top, e.g., island team promote
*/
@ -93,7 +93,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
* The prefix to be used in this command
*/
@Nullable
private String permissionPrefix;
private final String permissionPrefix;
/**
* The world that this command operates in. This is an overworld and will cover any associated nether or end
@ -104,17 +104,17 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* The addon creating this command, if any
*/
private Addon addon;
private final Addon addon;
/**
* The top level label
*/
private String topLabel;
private final String topLabel;
/**
* Cool down tracker
*/
private Map<String, Map<String, Long>> cooldowns = new HashMap<>();
private final Map<String, Map<String, Long>> cooldowns = new HashMap<>();
/**
* Top level command
@ -144,7 +144,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
// Run setup
setup();
if (!getSubCommand("help").isPresent() && !label.equals("help")) {
if (getSubCommand("help").isEmpty() && !label.equals("help")) {
new DefaultHelpCommand(this);
}
}
@ -204,11 +204,11 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
p = p.getParent();
index++;
}
setDescription(COMMANDS + reference.toString() + ".description");
setParametersHelp(COMMANDS + reference.toString() + ".parameters");
setDescription(COMMANDS + reference + ".description");
setParametersHelp(COMMANDS + reference + ".parameters");
setup();
// If this command does not define its own help class, then use the default help command
if (!getSubCommand("help").isPresent() && !label.equals("help")) {
if (getSubCommand("help").isEmpty() && !label.equals("help")) {
new DefaultHelpCommand(this);
}
}
@ -278,7 +278,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
// get the subcommand corresponding to the arg
if (subCommand.hasSubCommands()) {
Optional<CompositeCommand> sub = subCommand.getSubCommand(arg);
if (!sub.isPresent()) {
if (sub.isEmpty()) {
return subCommand;
}
// Step down one
@ -602,7 +602,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
return options;
}
// Add any tab completion from the subcommand
options.addAll(command.tabComplete(User.getInstance(sender), alias, new LinkedList<>(Arrays.asList(args))).orElseGet(() -> new ArrayList<>()));
options.addAll(command.tabComplete(User.getInstance(sender), alias, new LinkedList<>(Arrays.asList(args))).orElseGet(ArrayList::new));
if (command.hasSubCommands()) {
options.addAll(getSubCommandLabels(sender, command));
}
@ -701,7 +701,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
* @since 1.5.0
*/
public void setCooldown(String uniqueId, String targetUUID, int timeInSeconds) {
cooldowns.computeIfAbsent(uniqueId, k -> new HashMap<>()).put(targetUUID, System.currentTimeMillis() + timeInSeconds * 1000);
cooldowns.computeIfAbsent(uniqueId, k -> new HashMap<>()).put(targetUUID, System.currentTimeMillis() + timeInSeconds * 1000L);
}
/**
@ -711,7 +711,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
* @param timeInSeconds - time in seconds to cool down
*/
public void setCooldown(UUID uniqueId, UUID targetUUID, int timeInSeconds) {
cooldowns.computeIfAbsent(uniqueId.toString(), k -> new HashMap<>()).put(targetUUID == null ? null : targetUUID.toString(), System.currentTimeMillis() + timeInSeconds * 1000);
cooldowns.computeIfAbsent(uniqueId.toString(), k -> new HashMap<>()).put(targetUUID == null ? null : targetUUID.toString(), System.currentTimeMillis() + timeInSeconds * 1000L);
}
/**

View File

@ -20,7 +20,7 @@ public abstract class ConfirmableCommand extends CompositeCommand {
/**
* Confirmation tracker
*/
private static Map<User, Confirmer> toBeConfirmed = new HashMap<>();
private static final Map<User, Confirmer> toBeConfirmed = new HashMap<>();
/**
* Top level command
@ -61,9 +61,9 @@ public abstract class ConfirmableCommand extends CompositeCommand {
public void askConfirmation(User user, String message, Runnable confirmed) {
// Check for pending confirmations
if (toBeConfirmed.containsKey(user)) {
if (toBeConfirmed.get(user).getTopLabel().equals(getTopLabel()) && toBeConfirmed.get(user).getLabel().equalsIgnoreCase(getLabel())) {
toBeConfirmed.get(user).getTask().cancel();
Bukkit.getScheduler().runTask(getPlugin(), toBeConfirmed.get(user).getRunnable());
if (toBeConfirmed.get(user).topLabel().equals(getTopLabel()) && toBeConfirmed.get(user).label().equalsIgnoreCase(getLabel())) {
toBeConfirmed.get(user).task().cancel();
Bukkit.getScheduler().runTask(getPlugin(), toBeConfirmed.get(user).runnable());
toBeConfirmed.remove(user);
return;
} else {
@ -97,51 +97,9 @@ public abstract class ConfirmableCommand extends CompositeCommand {
}
/**
* Holds the data to run once the confirmation is given
* @author tastybento
* Record to hold the data to run once the confirmation is given
*
*/
private class Confirmer {
private final String topLabel;
private final String label;
private final Runnable runnable;
private final BukkitTask task;
/**
* @param label - command label
* @param runnable - runnable to run when confirmed
* @param task - task ID to cancel when confirmed
*/
Confirmer(String topLabel, String label, Runnable runnable, BukkitTask task) {
this.topLabel = topLabel;
this.label = label;
this.runnable = runnable;
this.task = task;
}
/**
* @return the topLabel
*/
public String getTopLabel() {
return topLabel;
}
/**
* @return the label
*/
public String getLabel() {
return label;
}
/**
* @return the runnable
*/
public Runnable getRunnable() {
return runnable;
}
/**
* @return the task
*/
public BukkitTask getTask() {
return task;
}
}
private record Confirmer (String topLabel, String label, Runnable runnable, BukkitTask task) { }
}

View File

@ -26,20 +26,20 @@ public abstract class DelayedTeleportCommand extends CompositeCommand implements
/**
* User monitor map
*/
private static Map<UUID, DelayedCommand> toBeMonitored = new HashMap<>();
private static final Map<UUID, DelayedCommand> toBeMonitored = new HashMap<>();
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onPlayerMove(PlayerMoveEvent e) {
UUID uuid = e.getPlayer().getUniqueId();
// Only check x,y,z
if (toBeMonitored.containsKey(uuid) && !e.getTo().toVector().equals(toBeMonitored.get(uuid).getLocation().toVector())) {
if (toBeMonitored.containsKey(uuid) && !e.getTo().toVector().equals(toBeMonitored.get(uuid).location().toVector())) {
moved(uuid);
}
}
private void moved(UUID uuid) {
// Player moved
toBeMonitored.get(uuid).getTask().cancel();
toBeMonitored.get(uuid).task().cancel();
toBeMonitored.remove(uuid);
// Player has another outstanding confirmation request that will now be cancelled
User.getInstance(uuid).notify("commands.delay.moved-so-command-cancelled");
@ -103,7 +103,7 @@ public abstract class DelayedTeleportCommand extends CompositeCommand implements
UUID uuid = user.getUniqueId();
if (toBeMonitored.containsKey(uuid)) {
// A double request - clear out the old one
toBeMonitored.get(uuid).getTask().cancel();
toBeMonitored.get(uuid).task().cancel();
toBeMonitored.remove(uuid);
// Player has another outstanding confirmation request that will now be cancelled
user.sendMessage("commands.delay.previous-command-cancelled");
@ -116,7 +116,7 @@ public abstract class DelayedTeleportCommand extends CompositeCommand implements
user.sendMessage("commands.delay.stand-still", "[seconds]", String.valueOf(getSettings().getDelayTime()));
// Set up the run task
BukkitTask task = Bukkit.getScheduler().runTaskLater(getPlugin(), () -> {
Bukkit.getScheduler().runTask(getPlugin(), toBeMonitored.get(uuid).getRunnable());
Bukkit.getScheduler().runTask(getPlugin(), toBeMonitored.get(uuid).runnable());
toBeMonitored.remove(uuid);
}, getPlugin().getSettings().getDelayTime() * 20L);
@ -135,42 +135,8 @@ public abstract class DelayedTeleportCommand extends CompositeCommand implements
/**
* Holds the data to run once the confirmation is given
* @author tastybento
*
*/
private class DelayedCommand {
private final Runnable runnable;
private final BukkitTask task;
private final Location location;
/**
* @param runnable - runnable to run when confirmed
* @param task - task ID to cancel when confirmed
* @param location - location
*/
DelayedCommand(Runnable runnable, BukkitTask task, Location location) {
this.runnable = runnable;
this.task = task;
this.location = location;
}
/**
* @return the runnable
*/
public Runnable getRunnable() {
return runnable;
}
/**
* @return the task
*/
public BukkitTask getTask() {
return task;
}
/**
* @return the location
*/
public Location getLocation() {
return location;
}
}
private record DelayedCommand(Runnable runnable, BukkitTask task, Location location) {}
}

View File

@ -9,6 +9,7 @@ import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.IslandInfo;
import world.bentobox.bentobox.util.Util;
public class AdminInfoCommand extends CompositeCommand {
@ -34,10 +35,8 @@ public class AdminInfoCommand extends CompositeCommand {
}
// If there are no args, then the player wants info on the island at this location
if (args.isEmpty()) {
if (!getIslands().getIslandAt(user.getLocation()).map(i -> i.showInfo(user)).orElse(false)) {
user.sendMessage("commands.admin.info.no-island");
return false;
}
getIslands().getIslandAt(user.getLocation()).ifPresentOrElse(i -> new IslandInfo(i).showAdminInfo(user), () ->
user.sendMessage("commands.admin.info.no-island"));
return true;
}
// Get target player
@ -49,7 +48,7 @@ public class AdminInfoCommand extends CompositeCommand {
// Show info for this player
Island island = getIslands().getIsland(getWorld(), targetUUID);
if (island != null) {
island.showInfo(user);
new IslandInfo(island).showAdminInfo(user);
if (!getIslands().getQuarantinedIslandByUser(getWorld(), targetUUID).isEmpty()) {
user.sendMessage("commands.admin.info.islands-in-trash");
}

View File

@ -20,7 +20,7 @@ import world.bentobox.bentobox.util.Util;
*/
public class AdminResetFlagsCommand extends ConfirmableCommand {
private List<String> options;
private final List<String> options;
public AdminResetFlagsCommand(CompositeCommand parent) {
super(parent, "resetflags");

View File

@ -59,7 +59,7 @@ public class AdminSetProtectionCenterCommand extends ConfirmableCommand
return false;
}
Optional<Island> optionalIsland = getIslands().getIslandAt(targetLoc);
if (!optionalIsland.isPresent()) {
if (optionalIsland.isEmpty()) {
user.sendMessage("commands.admin.setspawnpoint.no-island-here");
return false;
}

View File

@ -38,8 +38,8 @@ public class AdminSettingsCommand extends CompositeCommand {
private final List<String> PROTECTION_FLAG_NAMES;
private Island island;
private final List<String> SETTING_FLAG_NAMES;
private List<String> WORLD_SETTING_FLAG_NAMES;
private @NonNull Optional<Flag> flag;
private final List<String> WORLD_SETTING_FLAG_NAMES;
private @NonNull Optional<Flag> flag = Optional.empty();
private boolean activeState;
private int rank;
@ -258,20 +258,14 @@ public class AdminSettingsCommand extends CompositeCommand {
}
} else if (args.size() == 4) {
// Get flag in previous argument
options = getPlugin().getFlagsManager().getFlag(args.get(2).toUpperCase(Locale.ENGLISH)).map(f -> {
switch (f.getType()) {
case PROTECTION:
return getPlugin().getRanksManager()
.getRanks().entrySet().stream()
.filter(en -> en.getValue() > RanksManager.BANNED_RANK && en.getValue() <= RanksManager.OWNER_RANK)
.map(Entry::getKey)
.map(user::getTranslation).collect(Collectors.toList());
case SETTING:
return Arrays.asList(active, disabled);
default:
return Collections.<String>emptyList();
}
options = getPlugin().getFlagsManager().getFlag(args.get(2).toUpperCase(Locale.ENGLISH)).map(f -> switch (f.getType()) {
case PROTECTION -> getPlugin().getRanksManager()
.getRanks().entrySet().stream()
.filter(en -> en.getValue() > RanksManager.BANNED_RANK && en.getValue() <= RanksManager.OWNER_RANK)
.map(Entry::getKey)
.map(user::getTranslation).collect(Collectors.toList());
case SETTING -> Arrays.asList(active, disabled);
default -> Collections.<String>emptyList();
}).orElse(Collections.emptyList());
}
return Optional.of(Util.tabLimit(options, lastArg));

View File

@ -63,7 +63,7 @@ public class AdminSwitchtoCommand extends ConfirmableCommand {
public boolean execute(User user, String label, List<String> args) {
if (NumberUtils.isDigits(args.get(1))) {
try {
Integer n = Integer.valueOf(args.get(1));
int n = Integer.parseInt(args.get(1));
if (n < 1 || n > islands.size()) {
user.sendMessage("commands.admin.switchto.out-of-range", TextVariables.NUMBER, String.valueOf(islands.size()), TextVariables.LABEL, getTopLabel());
return false;

View File

@ -87,6 +87,10 @@ public class AdminTeleportCommand extends CompositeCommand {
world = getPlugin().getIWM().getEndWorld(getWorld());
}
Location warpSpot = getSpot(world);
if (world == null || warpSpot == null) {
user.sendMessage("general.errors.no-safe-location-found");
return false;
}
// Otherwise, ask the admin to go to a safe spot
String failureMessage = user.getTranslation("commands.admin.tp.manual", "[location]", warpSpot.getBlockX() + " " + warpSpot.getBlockY() + " "

View File

@ -7,6 +7,7 @@ import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.IslandInfo;
public class AdminTrashCommand extends CompositeCommand {
@ -63,7 +64,7 @@ public class AdminTrashCommand extends CompositeCommand {
user.sendMessage("commands.admin.trash.title");
for (int i = 0; i < islands.size(); i++) {
user.sendMessage("commands.admin.trash.count", TextVariables.NUMBER, String.valueOf(i+1));
islands.get(i).showInfo(user);
new IslandInfo(islands.get(i)).showInfo(user);
}
user.sendMessage("commands.admin.trash.use-switch", TextVariables.LABEL, getTopLabel());
user.sendMessage("commands.admin.trash.use-emptytrash", TextVariables.LABEL, getTopLabel());

View File

@ -22,7 +22,7 @@ public class AdminVersionCommand extends CompositeCommand {
@Override
public boolean execute(User user, String label, List<String> args) {
user.sendMessage("commands.bentobox.version.addon-syntax", TextVariables.NAME, getAddon().getDescription().getName(),
TextVariables.VERSION, getAddon().getDescription().getVersion());
TextVariables.VERSION, getAddon().getDescription().getVersion(), "[state]", getAddon().getState().name());
return true;
}

View File

@ -61,7 +61,7 @@ public abstract class DefaultAdminCommand extends CompositeCommand {
new AdminTeamDisbandCommand(this);
new AdminTeamSetownerCommand(this);
new AdminTeamFixCommand(this);
// Schems
// Blueprints
new AdminBlueprintCommand(this);
// Register/unregister islands
new AdminRegisterCommand(this);
@ -96,7 +96,7 @@ public abstract class DefaultAdminCommand extends CompositeCommand {
/**
* Defines what will be executed when this command is run.
* @see world.bentobox.bentobox.api.commands.BentoBoxCommand#execute(User, String, List&lt;String&gt;)
* @see world.bentobox.bentobox.api.commands.BentoBoxCommand#execute(User, String, List)
*/
@Override
public boolean execute(User user, String label, List<String> args) {

View File

@ -70,7 +70,7 @@ public class AdminPurgeCommand extends CompositeCommand implements Listener {
islands.clear();
this.user = user;
try {
Integer days = Integer.parseInt(args.get(0));
int days = Integer.parseInt(args.get(0));
if (days < 1) {
user.sendMessage("commands.admin.purge.days-one-or-more");
return false;

View File

@ -24,7 +24,7 @@ public class AdminRangeDisplayCommand extends CompositeCommand {
private static final String HIDE = "hide";
// Map of users to which ranges must be displayed
private Map<User, Integer> displayRanges = new HashMap<>();
private final Map<User, Integer> displayRanges = new HashMap<>();
public AdminRangeDisplayCommand(CompositeCommand parent) {
super(parent, DISPLAY, SHOW, HIDE);
@ -46,29 +46,15 @@ public class AdminRangeDisplayCommand extends CompositeCommand {
if (!displayRanges.containsKey(user)) {
switch (label) {
case DISPLAY:
case SHOW:
showZones(user);
break;
case HIDE:
user.sendMessage("commands.admin.range.display.already-off");
break;
default:
showHelp(this, user);
break;
case DISPLAY, SHOW -> showZones(user);
case HIDE -> user.sendMessage("commands.admin.range.display.already-off");
default -> showHelp(this, user);
}
} else {
switch (label) {
case DISPLAY:
case HIDE:
hideZones(user);
break;
case SHOW:
user.sendMessage("commands.admin.range.display.already-on");
break;
default:
showHelp(this, user);
break;
case DISPLAY, HIDE -> hideZones(user);
case SHOW -> user.sendMessage("commands.admin.range.display.already-on");
default -> showHelp(this, user);
}
}

View File

@ -10,6 +10,7 @@ import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.bentobox.util.IslandInfo;
import world.bentobox.bentobox.util.Util;
public class AdminTeamAddCommand extends CompositeCommand {
@ -49,7 +50,10 @@ public class AdminTeamAddCommand extends CompositeCommand {
}
if (getIslands().inTeam(getWorld(), ownerUUID) && !getIslands().getOwner(getWorld(), ownerUUID).equals(ownerUUID)) {
user.sendMessage("commands.admin.team.add.name-not-owner", TextVariables.NAME, args.get(0));
getIslands().getIsland(getWorld(), ownerUUID).showMembers(user);
Island island = getIslands().getIsland(getWorld(), ownerUUID);
if (island != null) {
new IslandInfo(island).showMembers(user);
}
return false;
}
if (getIslands().inTeam(getWorld(), targetUUID)) {

View File

@ -13,6 +13,7 @@ import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.bentobox.util.IslandInfo;
import world.bentobox.bentobox.util.Util;
/**
@ -59,10 +60,12 @@ public class AdminTeamKickCommand extends CompositeCommand {
@Override
public boolean execute(User user, String label, @NonNull List<String> args) {
Island island = getIslands().getIsland(getWorld(), targetUUID);
if (island == null) {
return false;
}
if (targetUUID.equals(island.getOwner())) {
user.sendMessage("commands.admin.team.kick.cannot-kick-owner");
island.showMembers(user);
new IslandInfo(island).showMembers(user);
return false;
}
User target = User.getInstance(targetUUID);

View File

@ -32,6 +32,12 @@ public class IslandGoCommand extends DelayedTeleportCommand {
@Override
public boolean canExecute(User user, String label, List<String> args) {
// Check if mid-teleport
if (getIslands().isGoingHome(user)) {
// Tell them again that it's in progress
user.sendMessage("commands.island.go.teleport");
return false;
}
// Check if the island is reserved
Island island = getIslands().getIsland(getWorld(), user.getUniqueId());
if (island == null) {

View File

@ -9,6 +9,7 @@ import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.IslandInfo;
import world.bentobox.bentobox.util.Util;
/**
@ -37,7 +38,7 @@ public class IslandInfoCommand extends CompositeCommand {
}
// If there are no args, then the player wants info on the island at this location
if (args.isEmpty()) {
if (!getIslands().getIslandAt(user.getLocation()).map(i -> i.showInfo(user)).orElse(false)) {
if (!getIslands().getIslandAt(user.getLocation()).map(i -> new IslandInfo(i).showInfo(user)).orElse(false)) {
user.sendMessage("commands.admin.info.no-island");
return false;
}
@ -56,10 +57,10 @@ public class IslandInfoCommand extends CompositeCommand {
return false;
}
// Show info for this player
island.showInfo(user);
new IslandInfo(island).showInfo(user);
return true;
}
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";

View File

@ -75,10 +75,9 @@ public class Invite {
if (obj == null) {
return false;
}
if (!(obj instanceof Invite)) {
if (!(obj instanceof Invite other)) {
return false;
}
Invite other = (Invite) obj;
return Objects.equals(invitee, other.invitee) && Objects.equals(inviter, other.inviter) && type == other.type;
}
}

View File

@ -29,7 +29,7 @@ public class IslandTeamCommand extends CompositeCommand {
* Invited list. Key is the invited party, value is the invite.
* @since 1.8.0
*/
private Map<UUID, Invite> inviteMap;
private final Map<UUID, Invite> inviteMap;
public IslandTeamCommand(CompositeCommand parent) {
super(parent, "team");

View File

@ -22,7 +22,7 @@ import world.bentobox.bentobox.util.Util;
*/
public class IslandTeamCoopCommand extends CompositeCommand {
private IslandTeamCommand itc;
private final IslandTeamCommand itc;
private @Nullable UUID targetUUID;
public IslandTeamCoopCommand(IslandTeamCommand parentCommand) {

View File

@ -20,7 +20,7 @@ import world.bentobox.bentobox.util.Util;
*/
public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
private IslandTeamCommand itc;
private final IslandTeamCommand itc;
private UUID playerUUID;
private UUID prospectiveOwnerUUID;
@ -50,16 +50,17 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
user.sendMessage("commands.island.team.invite.errors.invalid-invite");
return false;
}
// Check rank to of inviter
Island island = getIslands().getIsland(getWorld(), prospectiveOwnerUUID);
String inviteUsage = getParent().getSubCommand("invite").map(CompositeCommand::getUsage).orElse("");
if (island == null || island.getRank(prospectiveOwnerUUID) < island.getRankCommand(inviteUsage)) {
user.sendMessage("commands.island.team.invite.errors.invalid-invite");
itc.removeInvite(playerUUID);
return false;
}
Invite invite = itc.getInvite(playerUUID);
if (invite.getType().equals(Type.TEAM)) {
// Check rank to of inviter
Island island = getIslands().getIsland(getWorld(), prospectiveOwnerUUID);
String inviteUsage = getParent().getSubCommand("invite").map(CompositeCommand::getUsage).orElse("");
if (island == null || island.getRank(prospectiveOwnerUUID) < island.getRankCommand(inviteUsage)) {
user.sendMessage("commands.island.team.invite.errors.invalid-invite");
itc.removeInvite(playerUUID);
return false;
}
// Check if player is already in a team
if (getIslands().inTeam(getWorld(), playerUUID)) {
user.sendMessage("commands.island.team.invite.errors.you-already-are-in-team");
@ -82,14 +83,10 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
// Get the invite
Invite invite = itc.getInvite(playerUUID);
switch (invite.getType()) {
case COOP:
askConfirmation(user, () -> acceptCoopInvite(user, invite));
break;
case TRUST:
askConfirmation(user, () -> acceptTrustInvite(user, invite));
break;
default:
askConfirmation(user, user.getTranslation("commands.island.team.invite.accept.confirmation"), () -> acceptTeamInvite(user, invite));
case COOP -> askConfirmation(user, () -> acceptCoopInvite(user, invite));
case TRUST -> askConfirmation(user, () -> acceptTrustInvite(user, invite));
default -> askConfirmation(user, user.getTranslation("commands.island.team.invite.accept.confirmation"),
() -> acceptTeamInvite(user, invite));
}
return true;
}

View File

@ -20,7 +20,7 @@ import world.bentobox.bentobox.util.Util;
public class IslandTeamInviteCommand extends CompositeCommand {
private IslandTeamCommand itc;
private final IslandTeamCommand itc;
private @Nullable User invitedPlayer;
public IslandTeamInviteCommand(IslandTeamCommand parent) {
@ -51,15 +51,9 @@ public class IslandTeamInviteCommand extends CompositeCommand {
Invite invite = itc.getInvite(playerUUID);
String name = getPlayers().getName(playerUUID);
switch (invite.getType()) {
case COOP:
user.sendMessage("commands.island.team.invite.name-has-invited-you.coop", TextVariables.NAME, name);
break;
case TRUST:
user.sendMessage("commands.island.team.invite.name-has-invited-you.trust", TextVariables.NAME, name);
break;
default:
user.sendMessage("commands.island.team.invite.name-has-invited-you", TextVariables.NAME, name);
break;
case COOP -> user.sendMessage("commands.island.team.invite.name-has-invited-you.coop", TextVariables.NAME, name);
case TRUST -> user.sendMessage("commands.island.team.invite.name-has-invited-you.trust", TextVariables.NAME, name);
default -> user.sendMessage("commands.island.team.invite.name-has-invited-you", TextVariables.NAME, name);
}
return true;
}

View File

@ -11,7 +11,7 @@ import world.bentobox.bentobox.api.user.User;
public class IslandTeamInviteRejectCommand extends CompositeCommand {
private IslandTeamCommand itc;
private final IslandTeamCommand itc;
public IslandTeamInviteRejectCommand(IslandTeamCommand islandTeamCommand) {
super(islandTeamCommand, "reject");

View File

@ -0,0 +1,18 @@
/**
* API for BentoBox commands
*/
/**
* <p>
* The workhorse class is the abstract class CompositeCommand. It provides all the functionality for
* a command including automatic help, sub-commands, convenience methods, etc. See examples of how to use
* it in the sub-folders admin and island.
* </p>
* <p>
* The package also includes abstract confirmable command and delayed teleport command classes. These can
* be extended for commands that need them. There is also a default help command. Commands can implement
* their own custom help if required, but most of the time it is not.
* </p>
* @author tastybento
*
*/
package world.bentobox.bentobox.api.commands;

View File

@ -22,8 +22,8 @@ import world.bentobox.bentobox.database.yaml.YamlDatabase;
*/
public class Config<T> {
private AbstractDatabaseHandler<T> handler;
private Logger logger;
private final AbstractDatabaseHandler<T> handler;
private final Logger logger;
private Addon addon;
public Config(BentoBox plugin, Class<T> type) {

View File

@ -100,29 +100,21 @@ public class AddonEvent {
}
private AddonBaseEvent getDeprecatedEvent() {
switch (reason) {
case ENABLE:
return new AddonEnableEvent(addon, keyValues);
case DISABLE:
return new AddonDisableEvent(addon, keyValues);
case LOAD:
return new AddonLoadEvent(addon, keyValues);
default:
return new AddonGeneralEvent(addon, keyValues);
}
return switch (reason) {
case ENABLE -> new AddonEnableEvent(addon, keyValues);
case DISABLE -> new AddonDisableEvent(addon, keyValues);
case LOAD -> new AddonLoadEvent(addon, keyValues);
default -> new AddonGeneralEvent(addon, keyValues);
};
}
private AddonBaseEvent getEvent() {
switch (reason) {
case ENABLE:
return new world.bentobox.bentobox.api.events.addon.AddonEnableEvent(addon, keyValues);
case DISABLE:
return new world.bentobox.bentobox.api.events.addon.AddonDisableEvent(addon, keyValues);
case LOAD:
return new world.bentobox.bentobox.api.events.addon.AddonLoadEvent(addon, keyValues);
default:
return new world.bentobox.bentobox.api.events.addon.AddonGeneralEvent(addon, keyValues);
}
return switch (reason) {
case ENABLE -> new world.bentobox.bentobox.api.events.addon.AddonEnableEvent(addon, keyValues);
case DISABLE -> new world.bentobox.bentobox.api.events.addon.AddonDisableEvent(addon, keyValues);
case LOAD -> new world.bentobox.bentobox.api.events.addon.AddonLoadEvent(addon, keyValues);
default -> new world.bentobox.bentobox.api.events.addon.AddonGeneralEvent(addon, keyValues);
};
}
/**

View File

@ -801,103 +801,58 @@ public class IslandEvent extends IslandBaseEvent {
* @return deprecated event
*/
private IslandBaseEvent getDeprecatedEvent() {
switch (reason) {
case EXPEL:
return new IslandExpelEvent(island, player, admin, location);
case BAN:
return new IslandBanEvent(island, player, admin, location);
case PRECREATE:
return new IslandPreCreateEvent(player);
case CREATE:
return new IslandCreateEvent(island, player, admin, location, blueprintBundle);
case CREATED:
return new IslandCreatedEvent(island, player, admin, location);
case DELETE:
return new IslandDeleteEvent(island, player, admin, location);
case DELETE_CHUNKS:
return new IslandDeleteChunksEvent(island, player, admin, location, deletedIslandInfo);
case DELETED:
return new IslandDeletedEvent(island, player, admin, location, deletedIslandInfo);
case ENTER:
return new IslandEnterEvent(island, player, admin, location, oldIsland, rawEvent);
case EXIT:
return new IslandExitEvent(island, player, admin, location, oldIsland, rawEvent);
case LOCK:
return new IslandLockEvent(island, player, admin, location);
case RESET:
return new IslandResetEvent(island, player, admin, location, blueprintBundle, oldIsland);
case RESETTED:
return new IslandResettedEvent(island, player, admin, location, oldIsland);
case UNBAN:
return new IslandUnbanEvent(island, player, admin, location);
case UNLOCK:
return new IslandUnlockEvent(island, player, admin, location);
case REGISTERED:
return new IslandRegisteredEvent(island, player, admin, location);
case UNREGISTERED:
return new IslandUnregisteredEvent(island, player, admin, location);
case RANGE_CHANGE:
return new IslandProtectionRangeChangeEvent(island, player, admin, location, newRange, oldRange);
case PRECLEAR:
return new IslandPreclearEvent(island, player, admin, location, oldIsland);
case RESERVED:
return new IslandReservedEvent(island, player, admin, location);
case RANK_CHANGE:
return new IslandRankChangeEvent(island, player, admin, location, oldRank, newRank);
default:
return new IslandGeneralEvent(island, player, admin, location);
}
return switch (reason) {
case EXPEL -> new IslandExpelEvent(island, player, admin, location);
case BAN -> new IslandBanEvent(island, player, admin, location);
case PRECREATE -> new IslandPreCreateEvent(player);
case CREATE -> new IslandCreateEvent(island, player, admin, location, blueprintBundle);
case CREATED -> new IslandCreatedEvent(island, player, admin, location);
case DELETE -> new IslandDeleteEvent(island, player, admin, location);
case DELETE_CHUNKS -> new IslandDeleteChunksEvent(island, player, admin, location, deletedIslandInfo);
case DELETED -> new IslandDeletedEvent(island, player, admin, location, deletedIslandInfo);
case ENTER -> new IslandEnterEvent(island, player, admin, location, oldIsland, rawEvent);
case EXIT -> new IslandExitEvent(island, player, admin, location, oldIsland, rawEvent);
case LOCK -> new IslandLockEvent(island, player, admin, location);
case RESET -> new IslandResetEvent(island, player, admin, location, blueprintBundle, oldIsland);
case RESETTED -> new IslandResettedEvent(island, player, admin, location, oldIsland);
case UNBAN -> new IslandUnbanEvent(island, player, admin, location);
case UNLOCK -> new IslandUnlockEvent(island, player, admin, location);
case REGISTERED -> new IslandRegisteredEvent(island, player, admin, location);
case UNREGISTERED -> new IslandUnregisteredEvent(island, player, admin, location);
case RANGE_CHANGE -> new IslandProtectionRangeChangeEvent(island, player, admin, location, newRange, oldRange);
case PRECLEAR -> new IslandPreclearEvent(island, player, admin, location, oldIsland);
case RESERVED -> new IslandReservedEvent(island, player, admin, location);
case RANK_CHANGE -> new IslandRankChangeEvent(island, player, admin, location, oldRank, newRank);
default -> new IslandGeneralEvent(island, player, admin, location);
};
}
private IslandBaseEvent getEvent() {
switch (reason) {
case EXPEL:
return new world.bentobox.bentobox.api.events.island.IslandExpelEvent(island, player, admin, location);
case BAN:
return new world.bentobox.bentobox.api.events.island.IslandBanEvent(island, player, admin, location);
case PRECREATE:
return new world.bentobox.bentobox.api.events.island.IslandPreCreateEvent(player);
case CREATE:
return new world.bentobox.bentobox.api.events.island.IslandCreateEvent(island, player, admin, location, blueprintBundle);
case CREATED:
return new world.bentobox.bentobox.api.events.island.IslandCreatedEvent(island, player, admin, location);
case DELETE:
return new world.bentobox.bentobox.api.events.island.IslandDeleteEvent(island, player, admin, location);
case DELETE_CHUNKS:
return new world.bentobox.bentobox.api.events.island.IslandDeleteChunksEvent(island, player, admin, location, deletedIslandInfo);
case DELETED:
return new world.bentobox.bentobox.api.events.island.IslandDeletedEvent(island, player, admin, location, deletedIslandInfo);
case ENTER:
return new world.bentobox.bentobox.api.events.island.IslandEnterEvent(island, player, admin, location, oldIsland, rawEvent);
case EXIT:
return new world.bentobox.bentobox.api.events.island.IslandExitEvent(island, player, admin, location, oldIsland, rawEvent);
case LOCK:
return new world.bentobox.bentobox.api.events.island.IslandLockEvent(island, player, admin, location);
case RESET:
return new world.bentobox.bentobox.api.events.island.IslandResetEvent(island, player, admin, location, blueprintBundle, oldIsland);
case RESETTED:
return new world.bentobox.bentobox.api.events.island.IslandResettedEvent(island, player, admin, location, oldIsland);
case UNBAN:
return new world.bentobox.bentobox.api.events.island.IslandUnbanEvent(island, player, admin, location);
case UNLOCK:
return new world.bentobox.bentobox.api.events.island.IslandUnlockEvent(island, player, admin, location);
case REGISTERED:
return new world.bentobox.bentobox.api.events.island.IslandRegisteredEvent(island, player, admin, location);
case UNREGISTERED:
return new world.bentobox.bentobox.api.events.island.IslandUnregisteredEvent(island, player, admin, location);
case RANGE_CHANGE:
return new world.bentobox.bentobox.api.events.island.IslandProtectionRangeChangeEvent(island, player, admin, location, newRange, oldRange);
case PRECLEAR:
return new world.bentobox.bentobox.api.events.island.IslandPreclearEvent(island, player, admin, location, oldIsland);
case RESERVED:
return new world.bentobox.bentobox.api.events.island.IslandReservedEvent(island, player, admin, location);
case RANK_CHANGE:
return new world.bentobox.bentobox.api.events.island.IslandRankChangeEvent(island, player, admin, location, oldRank, newRank);
case NEW_ISLAND:
return new IslandNewIslandEvent(island, player, admin, location);
default:
return new world.bentobox.bentobox.api.events.island.IslandGeneralEvent(island, player, admin, location);
}
return switch (reason) {
case EXPEL -> new world.bentobox.bentobox.api.events.island.IslandExpelEvent(island, player, admin, location);
case BAN -> new world.bentobox.bentobox.api.events.island.IslandBanEvent(island, player, admin, location);
case PRECREATE -> new world.bentobox.bentobox.api.events.island.IslandPreCreateEvent(player);
case CREATE -> new world.bentobox.bentobox.api.events.island.IslandCreateEvent(island, player, admin, location, blueprintBundle);
case CREATED -> new world.bentobox.bentobox.api.events.island.IslandCreatedEvent(island, player, admin, location);
case DELETE -> new world.bentobox.bentobox.api.events.island.IslandDeleteEvent(island, player, admin, location);
case DELETE_CHUNKS -> new world.bentobox.bentobox.api.events.island.IslandDeleteChunksEvent(island, player, admin, location, deletedIslandInfo);
case DELETED -> new world.bentobox.bentobox.api.events.island.IslandDeletedEvent(island, player, admin, location, deletedIslandInfo);
case ENTER -> new world.bentobox.bentobox.api.events.island.IslandEnterEvent(island, player, admin, location, oldIsland, rawEvent);
case EXIT -> new world.bentobox.bentobox.api.events.island.IslandExitEvent(island, player, admin, location, oldIsland, rawEvent);
case LOCK -> new world.bentobox.bentobox.api.events.island.IslandLockEvent(island, player, admin, location);
case RESET -> new world.bentobox.bentobox.api.events.island.IslandResetEvent(island, player, admin, location, blueprintBundle, oldIsland);
case RESETTED -> new world.bentobox.bentobox.api.events.island.IslandResettedEvent(island, player, admin, location, oldIsland);
case UNBAN -> new world.bentobox.bentobox.api.events.island.IslandUnbanEvent(island, player, admin, location);
case UNLOCK -> new world.bentobox.bentobox.api.events.island.IslandUnlockEvent(island, player, admin, location);
case REGISTERED -> new world.bentobox.bentobox.api.events.island.IslandRegisteredEvent(island, player, admin, location);
case UNREGISTERED -> new world.bentobox.bentobox.api.events.island.IslandUnregisteredEvent(island, player, admin, location);
case RANGE_CHANGE -> new world.bentobox.bentobox.api.events.island.IslandProtectionRangeChangeEvent(island, player, admin, location, newRange, oldRange);
case PRECLEAR -> new world.bentobox.bentobox.api.events.island.IslandPreclearEvent(island, player, admin, location, oldIsland);
case RESERVED -> new world.bentobox.bentobox.api.events.island.IslandReservedEvent(island, player, admin, location);
case RANK_CHANGE -> new world.bentobox.bentobox.api.events.island.IslandRankChangeEvent(island, player, admin, location, oldRank, newRank);
case NEW_ISLAND -> new IslandNewIslandEvent(island, player, admin, location);
default -> new world.bentobox.bentobox.api.events.island.IslandGeneralEvent(island, player, admin, location);
};
}
/**

View File

@ -0,0 +1,8 @@
/**
* API for all the events that BentoBox generates
*/
/**
* @author tastybento
*
*/
package world.bentobox.bentobox.api.events;

View File

@ -237,57 +237,35 @@ public class TeamEvent {
}
private IslandBaseEvent getDeprecatedEvent() {
switch (reason) {
case JOIN:
return new TeamJoinEvent(island, player, admin, location);
case JOINED:
return new TeamJoinedEvent(island, player, admin, location);
case INVITE:
return new TeamInviteEvent(island, player, admin, location);
case LEAVE:
return new TeamLeaveEvent(island, player, admin, location);
case REJECT:
return new TeamRejectEvent(island, player, admin, location);
case KICK:
return new TeamKickEvent(island, player, admin, location);
case SETOWNER:
return new TeamSetownerEvent(island, player, admin, location);
case INFO:
return new TeamInfoEvent(island, player, admin, location);
case DELETE:
return new TeamDeleteEvent(island, player, admin, location);
case UNINVITE:
return new TeamUninviteEvent(island, player, admin, location);
default:
return new TeamGeneralEvent(island, player, admin, location);
}
return switch (reason) {
case JOIN -> new TeamJoinEvent(island, player, admin, location);
case JOINED -> new TeamJoinedEvent(island, player, admin, location);
case INVITE -> new TeamInviteEvent(island, player, admin, location);
case LEAVE -> new TeamLeaveEvent(island, player, admin, location);
case REJECT -> new TeamRejectEvent(island, player, admin, location);
case KICK -> new TeamKickEvent(island, player, admin, location);
case SETOWNER -> new TeamSetownerEvent(island, player, admin, location);
case INFO -> new TeamInfoEvent(island, player, admin, location);
case DELETE -> new TeamDeleteEvent(island, player, admin, location);
case UNINVITE -> new TeamUninviteEvent(island, player, admin, location);
default -> new TeamGeneralEvent(island, player, admin, location);
};
}
private IslandBaseEvent getEvent() {
switch (reason) {
case JOIN:
return new world.bentobox.bentobox.api.events.team.TeamJoinEvent(island, player, admin, location);
case JOINED:
return new world.bentobox.bentobox.api.events.team.TeamJoinedEvent(island, player, admin, location);
case INVITE:
return new world.bentobox.bentobox.api.events.team.TeamInviteEvent(island, player, admin, location);
case LEAVE:
return new world.bentobox.bentobox.api.events.team.TeamLeaveEvent(island, player, admin, location);
case REJECT:
return new world.bentobox.bentobox.api.events.team.TeamRejectEvent(island, player, admin, location);
case KICK:
return new world.bentobox.bentobox.api.events.team.TeamKickEvent(island, player, admin, location);
case SETOWNER:
return new world.bentobox.bentobox.api.events.team.TeamSetownerEvent(island, player, admin, location);
case INFO:
return new world.bentobox.bentobox.api.events.team.TeamInfoEvent(island, player, admin, location);
case DELETE:
return new world.bentobox.bentobox.api.events.team.TeamDeleteEvent(island, player, admin, location);
case UNINVITE:
return new world.bentobox.bentobox.api.events.team.TeamUninviteEvent(island, player, admin, location);
default:
return new world.bentobox.bentobox.api.events.team.TeamGeneralEvent(island, player, admin, location);
}
return switch (reason) {
case JOIN -> new world.bentobox.bentobox.api.events.team.TeamJoinEvent(island, player, admin, location);
case JOINED -> new world.bentobox.bentobox.api.events.team.TeamJoinedEvent(island, player, admin, location);
case INVITE -> new world.bentobox.bentobox.api.events.team.TeamInviteEvent(island, player, admin, location);
case LEAVE -> new world.bentobox.bentobox.api.events.team.TeamLeaveEvent(island, player, admin, location);
case REJECT -> new world.bentobox.bentobox.api.events.team.TeamRejectEvent(island, player, admin, location);
case KICK -> new world.bentobox.bentobox.api.events.team.TeamKickEvent(island, player, admin, location);
case SETOWNER -> new world.bentobox.bentobox.api.events.team.TeamSetownerEvent(island, player, admin, location);
case INFO -> new world.bentobox.bentobox.api.events.team.TeamInfoEvent(island, player, admin, location);
case DELETE -> new world.bentobox.bentobox.api.events.team.TeamDeleteEvent(island, player, admin, location);
case UNINVITE -> new world.bentobox.bentobox.api.events.team.TeamUninviteEvent(island, player, admin, location);
default -> new world.bentobox.bentobox.api.events.team.TeamGeneralEvent(island, player, admin, location);
};
}
/**

View File

@ -25,6 +25,8 @@ import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.bentobox.util.ItemParser;
public class Flag implements Comparable<Flag> {
@ -51,7 +53,8 @@ public class Flag implements Comparable<Flag> {
*/
WORLD_SETTING(Material.GRASS_BLOCK);
private @NonNull Material icon;
private @NonNull
final Material icon;
Type(@NonNull Material icon) {
this.icon = icon;
@ -91,14 +94,11 @@ public class Flag implements Comparable<Flag> {
* @return next ranking mode
*/
public Mode getNext() {
switch(this) {
case ADVANCED:
return EXPERT;
case BASIC:
return ADVANCED;
default:
return BASIC;
}
return switch (this) {
case ADVANCED -> EXPERT;
case BASIC -> ADVANCED;
default -> BASIC;
};
}
/**
@ -107,14 +107,11 @@ public class Flag implements Comparable<Flag> {
* @return true if ranked greater
*/
public boolean isGreaterThan(Mode rank) {
switch(this) {
case EXPERT:
return rank.equals(BASIC) || rank.equals(ADVANCED);
case ADVANCED:
return rank.equals(BASIC);
default:
return false;
}
return switch (this) {
case EXPERT -> rank.equals(BASIC) || rank.equals(ADVANCED);
case ADVANCED -> rank.equals(BASIC);
default -> false;
};
}
}
@ -207,12 +204,12 @@ public class Flag implements Comparable<Flag> {
// Subflag support
if (hasSubflags()) {
subflags.stream()
.filter(subflag -> subflag.getType().equals(Type.WORLD_SETTING) || subflag.getType().equals(Type.PROTECTION))
.forEach(subflag -> BentoBox.getInstance()
.getIWM()
.getWorldSettings(world)
.getWorldFlags()
.put(subflag.getID(), setting));
.filter(subflag -> subflag.getType().equals(Type.WORLD_SETTING) || subflag.getType().equals(Type.PROTECTION))
.forEach(subflag -> BentoBox.getInstance()
.getIWM()
.getWorldSettings(world)
.getWorldFlags()
.put(subflag.getID(), setting));
}
// Save config file
@ -222,7 +219,7 @@ public class Flag implements Comparable<Flag> {
/**
* Set the original status of this flag for locations outside of island spaces.
* May be overriden by the the setting for this world.
* May be overridden by the setting for this world.
* Does not affect subflags.
* @param defaultSetting - true means it is allowed. false means it is not allowed
*/
@ -300,10 +297,9 @@ public class Flag implements Comparable<Flag> {
if (obj == null) {
return false;
}
if (!(obj instanceof Flag)) {
if (!(obj instanceof Flag other)) {
return false;
}
Flag other = (Flag) obj;
if (id == null) {
if (other.id != null) {
return false;
@ -322,6 +318,13 @@ public class Flag implements Comparable<Flag> {
return PROTECTION_FLAGS + this.id + ".name";
}
/**
* @return a locale reference for the icon of this protection flag
*/
public String getIconReference() {
return PROTECTION_FLAGS + this.id + ".icon";
}
/**
* @return a locale reference for the description of this protection flag
*/
@ -384,7 +387,7 @@ public class Flag implements Comparable<Flag> {
}
// Start the flag conversion
PanelItemBuilder pib = new PanelItemBuilder()
.icon(new ItemStack(icon))
.icon(ItemParser.parse(user.getTranslationOrNothing(this.getIconReference()), new ItemStack(icon)))
.name(user.getTranslation("protection.panel.flag-item.name-layout", TextVariables.NAME, user.getTranslation(getNameReference())))
.clickHandler(clickHandler)
.invisible(invisible);
@ -392,16 +395,11 @@ public class Flag implements Comparable<Flag> {
pib.description(user.getTranslation("protection.panel.flag-item.menu-layout", TextVariables.DESCRIPTION, user.getTranslation(getDescriptionReference())));
return pib.build();
}
switch(getType()) {
case PROTECTION:
return createProtectionFlag(plugin, user, island, pib).build();
case SETTING:
return createSettingFlag(user, island, pib).build();
case WORLD_SETTING:
return createWorldSettingFlag(user, pib).build();
default:
return pib.build();
}
return switch (getType()) {
case PROTECTION -> createProtectionFlag(plugin, user, island, pib).build();
case SETTING -> createSettingFlag(user, island, pib).build();
case WORLD_SETTING -> createWorldSettingFlag(user, pib).build();
};
}
private PanelItemBuilder createWorldSettingFlag(User user, PanelItemBuilder pib) {
@ -484,8 +482,8 @@ public class Flag implements Comparable<Flag> {
*/
public static class Builder {
// Mandatory fields
private String id;
private Material icon;
private final String id;
private final Material icon;
// Listener
private Listener listener;
@ -514,7 +512,7 @@ public class Flag implements Comparable<Flag> {
private Mode mode = Mode.EXPERT;
// Subflags
private Set<Flag> subflags;
private final Set<Flag> subflags;
/**
* Builder for making flags
@ -638,7 +636,7 @@ public class Flag implements Comparable<Flag> {
* Take extra care to ensure that subflags have the same number of possible values as the parent flag.
* @param flags all Flags that are subflags
* @return Builder - flag builder
* @since 1.17.0
* @since 1.17.1
*/
public Builder subflags(Flag... flags) {
this.subflags.addAll(Arrays.asList(flags));
@ -653,17 +651,9 @@ public class Flag implements Comparable<Flag> {
// If no clickHandler has been set, then apply default ones
if (clickHandler == null) {
switch (type) {
case SETTING:
clickHandler = new IslandToggleClick(id);
break;
case WORLD_SETTING:
clickHandler = new WorldToggleClick(id);
break;
case PROTECTION:
// Default option
default:
clickHandler = new CycleClick(id);
break;
case SETTING -> clickHandler = new IslandToggleClick(id);
case WORLD_SETTING -> clickHandler = new WorldToggleClick(id);
default -> clickHandler = new CycleClick(id);
}
}

View File

@ -4,6 +4,7 @@ import java.util.Optional;
import java.util.UUID;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
@ -18,6 +19,7 @@ import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.metadata.MetaDataValue;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.lists.Flags;
import world.bentobox.bentobox.managers.IslandWorldManager;
import world.bentobox.bentobox.managers.IslandsManager;
import world.bentobox.bentobox.util.Util;
@ -253,4 +255,22 @@ public abstract class FlagListener implements Listener {
protected IslandWorldManager getIWM() {
return plugin.getIWM();
}
/**
* Check if PVP is allowed here or not
* @param location location where action is taking
* @return true if PVP is allowed, false if not
*/
protected boolean PVPAllowed(Location location) {
return plugin.getIslands().getIslandAt(location).map(i -> i.isAllowed(this.getFlag(location.getWorld()))).orElse(false);
}
protected Flag getFlag(World w) {
return switch (w.getEnvironment()) {
case NETHER -> Flags.PVP_NETHER;
case THE_END -> Flags.PVP_END;
default -> Flags.PVP_OVERWORLD;
};
}
}

View File

@ -23,8 +23,8 @@ import world.bentobox.bentobox.util.Util;
*/
public class IslandToggleClick implements ClickHandler {
private BentoBox plugin = BentoBox.getInstance();
private String id;
private final BentoBox plugin = BentoBox.getInstance();
private final String id;
/**
* @param id - the flag ID that this click listener is associated with

View File

@ -0,0 +1,12 @@
/**
* This package contains click listener classes used when clicking on icons in settings
*
* <p>
* CycleClick will cycle through different settings. IslandLock is specific to locking or unlocking an island.
* IslandToggleClick is a toggle-based selection. WorldToggleClick toggles world settings.
*/
/**
* @author tastybento
*
*/
package world.bentobox.bentobox.api.flags.clicklisteners;

View File

@ -0,0 +1,16 @@
/**
* This package contains the API for protection and settings flags
*
* <p>
* New flags should use the Flag.Builder to create a flag. Listeners for flag related events should
* extend the abstract FlagListener class.
* </p>
* <p>
* Click listeners are different types of listeners specific to clicking on the settings UI in which the
* flags are shown.
* </p>
*
* @author tastybento
*
*/
package world.bentobox.bentobox.api.flags;

View File

@ -0,0 +1,12 @@
/**
* Provides API to enable BentoBox to hook into other plugins.
*
* <p>
* This is used to hook into plugins like Placeholder API
* </p>
*/
/**
* @author Poslovich
*
*/
package world.bentobox.bentobox.api.hooks;

View File

@ -20,16 +20,16 @@ public class BentoBoxLocale {
private static final String UNKNOWN = "unknown";
private Locale locale;
private YamlConfiguration config;
private ItemStack banner;
private List<String> authors;
private final Locale locale;
private final YamlConfiguration config;
private final ItemStack banner;
private final List<String> authors;
/**
* List of available prefixes in this locale.
* @since 1.12.0
*/
private Set<String> prefixes;
private final Set<String> prefixes;
public BentoBoxLocale(Locale locale, YamlConfiguration config) {
this.locale = locale;

View File

@ -0,0 +1,11 @@
/**
* API for localization.
*
* <p>
* BentoBoxLocale holds all the information required to specify a locale in BentoBox.
* TextVariables contains a static constants that are used as place holders in user messaging.
* </p>
* @author tastybento
*
*/
package world.bentobox.bentobox.api.localization;

View File

@ -47,7 +47,7 @@ public class LogEntry {
public static class Builder {
private long timestamp;
private String type;
private final String type;
private Map<String, String> data;
public Builder(@NonNull String type) {

View File

@ -13,13 +13,13 @@ public interface MetaDataAble {
/**
* @return the metaData
*/
public Optional<Map<String, MetaDataValue>> getMetaData();
Optional<Map<String, MetaDataValue>> getMetaData();
/**
* @param metaData the metaData to set
* @since 1.15.4
*/
public void setMetaData(Map<String, MetaDataValue> metaData);
void setMetaData(Map<String, MetaDataValue> metaData);
/**
* Get meta data by key

View File

@ -29,7 +29,7 @@ public class MetaDataValue {
@Expose
private Boolean booleanValue;
@Expose
private @NonNull String stringValue;
private String stringValue;
/**
* Initialize this meta data value
@ -85,6 +85,6 @@ public class MetaDataValue {
@NonNull
public String asString() {
return stringValue;
return stringValue == null ? "" : stringValue;
}
}

View File

@ -0,0 +1,11 @@
/**
* Metadata API for storing data in BentoBox database objects
* <p>
* This API draws on the same concepts as Bukkit's meta data API.
* Classes capable of storing meta data will implement the MetaDataAble interface.
* MetaDataValue provides the actual object in which data is stored.
/* </p>
* @author tastybento
* @since 1.15.4
*/
package world.bentobox.bentobox.api.metadata;

View File

@ -80,15 +80,9 @@ public class Panel implements HeadRequester, InventoryHolder {
// Create panel
switch (type) {
case INVENTORY:
inventory = Bukkit.createInventory(null, fixSize(size), name);
break;
case HOPPER:
inventory = Bukkit.createInventory(null, InventoryType.HOPPER, name);
break;
case DROPPER:
inventory = Bukkit.createInventory(null, InventoryType.DROPPER, name);
break;
case INVENTORY -> inventory = Bukkit.createInventory(null, fixSize(size), name);
case HOPPER -> inventory = Bukkit.createInventory(null, InventoryType.HOPPER, name);
case DROPPER -> inventory = Bukkit.createInventory(null, InventoryType.DROPPER, name);
}
// Fill the inventory and return

View File

@ -29,7 +29,7 @@ public class PanelItem {
private String name;
private boolean glow;
private ItemMeta meta;
private String playerHeadName;
private final String playerHeadName;
private boolean invisible;
public PanelItem(PanelItemBuilder builtItem) {

View File

@ -31,7 +31,8 @@ public class TabbedPanel extends Panel implements PanelListener {
private static final String PROTECTION_PANEL = "protection.panel.";
private static final long ITEMS_PER_PAGE = 36;
private final TabbedPanelBuilder tpb;
private @NonNull BentoBox plugin = BentoBox.getInstance();
private @NonNull
final BentoBox plugin = BentoBox.getInstance();
private int activeTab;
private int activePage;
private boolean closed;
@ -146,7 +147,7 @@ public class TabbedPanel extends Panel implements PanelListener {
}
}
// Add any subsidiary icons
tab.getTabIcons().forEach(items::put);
items.putAll(tab.getTabIcons());
}
private void setupFooter(TreeMap<Integer, PanelItem> items) {

View File

@ -0,0 +1,527 @@
//
// Created by BONNe
// Copyright - 2021
//
package world.bentobox.bentobox.api.panels;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import java.util.*;
import java.util.function.BiFunction;
import world.bentobox.bentobox.BentoBox;
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.panels.reader.PanelTemplateRecord;
import world.bentobox.bentobox.api.user.User;
/**
* This class creates a new Panel from the template record.
* @author BONNe
* @since 1.17.3
*/
public class TemplatedPanel extends Panel
{
/**
* TemplatedPanel constructor class which generates functional panel.
* @param builder Builder that contains all information about the panel that must be generated.
*/
public TemplatedPanel(@NonNull TemplatedPanelBuilder builder)
{
this.user = builder.getUser();
this.setWorld(builder.getWorld());
this.setListener(builder.getListener());
this.panelTemplate = builder.getPanelTemplate();
// Init type creators
this.typeCreators = new HashMap<>(builder.getObjectCreatorMap());
this.typeIndex = new HashMap<>(builder.getObjectCreatorMap().size());
this.typeSlotMap = new HashMap<>(builder.getObjectCreatorMap().size());
if (this.panelTemplate == null)
{
BentoBox.getInstance().logError("Cannot generate panel because template is not loaded.");
}
else
{
this.generatePanel();
}
}
/**
* This method generates the panel from the template.
*/
private void generatePanel()
{
Map<Integer, PanelItem> items = switch (this.panelTemplate.type())
{
case INVENTORY -> this.populateInventoryPanel();
case HOPPER -> this.populateHopperPanel();
case DROPPER -> this.populateDropperPanel();
};
super.makePanel(this.user.getTranslation(this.panelTemplate.title()),
items,
items.keySet().stream().max(Comparator.naturalOrder()).orElse(9),
this.user,
this.getListener().orElse(null),
this.panelTemplate.type());
}
/**
* This method creates map with item indexes and their icons that will be added into
* Inventory Panel.
* @return Map that contains indexes linked to the correct panel item.
*/
@NonNull
private Map<Integer, PanelItem> populateInventoryPanel()
{
// Init item array with the max available size.
PanelItem[][] itemArray = new PanelItem[6][9];
// Analyze the GUI button layout a bit.
for (int i = 0; i < this.panelTemplate.content().length; i++)
{
for (int k = 0; k < this.panelTemplate.content()[i].length; k++)
{
ItemTemplateRecord record = this.panelTemplate.content()[i][k];
if (record != null && record.dataMap().containsKey("type"))
{
String type = String.valueOf(record.dataMap().get("type"));
int counter = this.typeSlotMap.computeIfAbsent(type, key -> 1);
this.typeSlotMap.put(type, counter + 1);
}
}
}
// Make buttons for the GUI
for (int i = 0; i < this.panelTemplate.content().length; i++)
{
for (int k = 0; k < this.panelTemplate.content()[i].length; k++)
{
itemArray[i][k] = this.makeButton(this.panelTemplate.content()[i][k]);
}
}
// After items are created, remove empty lines.
boolean[] showLine = this.panelTemplate.forcedRows();
for (int i = 0; i < this.panelTemplate.content().length; i++)
{
boolean emptyLine = true;
for (int k = 0; emptyLine && k < this.panelTemplate.content()[i].length; k++)
{
emptyLine = itemArray[i][k] == null;
}
// Do not generate fallback for "empty" lines.
showLine[i] = showLine[i] || !emptyLine;
}
// Now fill the border.
if (this.panelTemplate.border() != null)
{
PanelItem template = this.makeTemplate(this.panelTemplate.border());
// Hard codded 6
for (int i = 0; i < 6; i++)
{
if (i == 0 || i == 5)
{
// Fill first and last row completely with border.
for (int k = 0; k < 9; k++)
{
if (itemArray[i][k] == null)
{
itemArray[i][k] = template;
}
}
}
else
{
// Fill first and last element in row with border.
if (itemArray[i][0] == null)
{
itemArray[i][0] = template;
}
if (itemArray[i][8] == null)
{
itemArray[i][8] = template;
}
}
}
showLine[0] = true;
showLine[5] = true;
}
// Now fill the background.
if (this.panelTemplate.background() != null)
{
PanelItem template = this.makeTemplate(this.panelTemplate.background());
for (int i = 0; i < 6; i++)
{
for (int k = 0; k < 9; k++)
{
if (itemArray[i][k] == null)
{
itemArray[i][k] = template;
}
}
}
}
// Now place all panel items with their indexes into item map.
Map<Integer, PanelItem> itemMap = new HashMap<>(6 * 9);
int correctIndex = 0;
for (int i = 0; i < itemArray.length; i++)
{
final boolean iterate = showLine[i];
for (int k = 0; iterate && k < itemArray[i].length; k++)
{
if (itemArray[i][k] != null)
{
itemMap.put(correctIndex, itemArray[i][k]);
}
correctIndex++;
}
}
return itemMap;
}
/**
* This method creates map with item indexes and their icons that will be added into
* hopper Panel.
* @return Map that contains indexes linked to the correct panel item.
*/
@NonNull
private Map<Integer, PanelItem> populateHopperPanel()
{
// Init item array with the max available size.
PanelItem[] itemArray = new PanelItem[5];
// Analyze the template
for (int i = 0; i < 5; i++)
{
ItemTemplateRecord record = this.panelTemplate.content()[0][i];
if (record != null && record.dataMap().containsKey("type"))
{
String type = String.valueOf(record.dataMap().get("type"));
int counter = this.typeSlotMap.computeIfAbsent(type, key -> 1);
this.typeSlotMap.put(type, counter + 1);
}
}
// Make buttons
for (int i = 0; i < 5; i++)
{
itemArray[i] = this.makeButton(this.panelTemplate.content()[0][i]);
}
// Now fill the background.
if (this.panelTemplate.background() != null)
{
PanelItem template = this.makeTemplate(this.panelTemplate.background());
for (int i = 0; i < 5; i++)
{
if (itemArray[i] == null)
{
itemArray[i] = template;
}
}
}
// Now place all panel items with their indexes into item map.
Map<Integer, PanelItem> itemMap = new HashMap<>(5);
int correctIndex = 0;
for (PanelItem panelItem : itemArray)
{
if (panelItem != null)
{
itemMap.put(correctIndex, panelItem);
}
correctIndex++;
}
return itemMap;
}
/**
* This method creates map with item indexes and their icons that will be added into
* dropper Panel.
* @return Map that contains indexes linked to the correct panel item.
*/
@NonNull
private Map<Integer, PanelItem> populateDropperPanel()
{
// Analyze the template
for (int i = 0; i < 3; i++)
{
for (int k = 0; k < 3; k++)
{
ItemTemplateRecord record = this.panelTemplate.content()[i][k];
if (record != null && record.dataMap().containsKey("type"))
{
String type = String.valueOf(record.dataMap().get("type"));
int counter = this.typeSlotMap.computeIfAbsent(type, key -> 1);
this.typeSlotMap.put(type, counter + 1);
}
}
}
// Init item array with the max available size.
PanelItem[][] itemArray = new PanelItem[3][3];
// Make buttons
for (int i = 0; i < 3; i++)
{
for (int k = 0; k < 3; k++)
{
itemArray[i][k] = this.makeButton(this.panelTemplate.content()[i][k]);
}
}
// Now fill the background.
if (this.panelTemplate.background() != null)
{
PanelItem template = this.makeTemplate(this.panelTemplate.background());
for (int i = 0; i < 3; i++)
{
for (int k = 0; k < 3; k++)
{
if (itemArray[i][k] == null)
{
itemArray[i][k] = template;
}
}
}
}
// Init item map with the max available size.
Map<Integer, PanelItem> itemMap = new HashMap<>(9);
int correctIndex = 0;
for (int i = 0; i < 3; i++)
{
for (int k = 0; k < 3; k++)
{
if (itemArray[i][k] != null)
{
itemMap.put(correctIndex, itemArray[i][k]);
}
correctIndex++;
}
}
return itemMap;
}
/**
* This method passes button creation from given record template.
* @param record Template of the button that must be created.
* @return PanelItem of the template, otherwise null.
*/
@Nullable
private PanelItem makeButton(@Nullable ItemTemplateRecord record)
{
if (record == null)
{
// Immediate exit if record is null.
return null;
}
if (record.dataMap().containsKey("type"))
{
// If dataMap is not null, and it is not empty, then pass button to the object creator function.
return this.makeAddonButton(record);
}
else
{
PanelItemBuilder itemBuilder = new PanelItemBuilder();
if (record.icon() != null)
{
itemBuilder.icon(record.icon().clone());
}
if (record.title() != null)
{
itemBuilder.name(this.user.getTranslation(record.title()));
}
if (record.description() != null)
{
itemBuilder.description(this.user.getTranslation(record.description()));
}
// If there are generic click handlers that could be added, then this is a place
// where to process them.
// Click Handlers are managed by custom addon buttons.
return itemBuilder.build();
}
}
/**
* This method passes button to the type creator, if that exists.
* @param record Template of the button that must be created.
* @return PanelItem of the button created by typeCreator, otherwise null.
*/
@Nullable
private PanelItem makeAddonButton(@NonNull ItemTemplateRecord record)
{
// Get object type.
String type = String.valueOf(record.dataMap().getOrDefault("type", ""));
if (!this.typeCreators.containsKey(type))
{
// There are no object with a given type.
return this.makeFallBack(record.fallback());
}
BiFunction<ItemTemplateRecord, ItemSlot, PanelItem> buttonBuilder = this.typeCreators.get(type);
// Get next slot index.
ItemSlot itemSlot = this.typeIndex.containsKey(type) ?
this.typeIndex.get(type) :
new ItemSlot(0, this.typeSlotMap);
this.typeIndex.put(type, itemSlot.nextItemSlot());
// Try to get next object.
PanelItem item = buttonBuilder.apply(record, itemSlot);
return item == null ? this.makeFallBack(record.fallback()) : item;
}
/**
* This method creates a fall back button for given record.
* @param record Record which fallback must be created.
* @return PanelItem if fallback was creates successfully, otherwise null.
*/
@Nullable
private PanelItem makeFallBack(@Nullable ItemTemplateRecord record)
{
return record == null ? null : this.makeButton(record.fallback());
}
/**
* This method translates template record into a panel item.
* @param record Record that must be translated.
* @return PanelItem that contains all information from the record.
*/
private PanelItem makeTemplate(PanelTemplateRecord.TemplateItem record)
{
PanelItemBuilder itemBuilder = new PanelItemBuilder();
// Read icon only if it is not null.
if (record.icon() != null)
{
itemBuilder.icon(record.icon().clone());
}
// Read title only if it is not null.
if (record.title() != null)
{
itemBuilder.name(this.user.getTranslation(record.title()));
}
// Read description only if it is not null.
if (record.description() != null)
{
itemBuilder.description(this.user.getTranslation(record.description()));
}
// Click Handlers are managed by custom addon buttons.
return itemBuilder.build();
}
// ---------------------------------------------------------------------
// Section: Classes
// ---------------------------------------------------------------------
/**
* This record contains current slot object and map that links types with a number of slots in
* panel with it.
* Some buttons need information about all types, like previous/next.
* @param slot Index of object in current panel.
* @param amountMap Map that links types with number of objects in panel.
*/
public record ItemSlot(int slot, Map<String, Integer> amountMap)
{
/**
* This method returns new record object with iterative slot index.
* @return New ItemSlot object that has increased slot index by 1.
*/
ItemSlot nextItemSlot()
{
return new ItemSlot(this.slot() + 1, this.amountMap());
}
}
// ---------------------------------------------------------------------
// Section: Variables
// ---------------------------------------------------------------------
/**
* The GUI template record.
*/
private final PanelTemplateRecord panelTemplate;
/**
* The user who opens the GUI.
*/
private final User user;
/**
* This map links custom types with their info object.
*/
private final Map<String, BiFunction<ItemTemplateRecord, ItemSlot, PanelItem>> typeCreators;
/**
* Stores the item slot information for each type.
*/
private final Map<String, ItemSlot> typeIndex;
/**
* Stores the number of items with given type in whole panel.
*/
private final Map<String, Integer> typeSlotMap;
}

View File

@ -3,13 +3,14 @@ package world.bentobox.bentobox.api.panels.builders;
import java.util.SortedMap;
import java.util.TreeMap;
import org.bukkit.ChatColor;
import org.bukkit.World;
import world.bentobox.bentobox.api.panels.Panel;
import world.bentobox.bentobox.api.panels.PanelItem;
import world.bentobox.bentobox.api.panels.PanelListener;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.util.Util;
/**
* Builds panels
@ -26,7 +27,7 @@ public class PanelBuilder {
private World world;
public PanelBuilder name(String name) {
this.name = ChatColor.translateAlternateColorCodes('&', name);
this.name = Util.translateColorCodes(name);
return this;
}

View File

@ -5,13 +5,14 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.api.panels.PanelItem;
import world.bentobox.bentobox.api.panels.PanelItem.ClickHandler;
import world.bentobox.bentobox.util.Util;
public class PanelItemBuilder {
private ItemStack icon = new ItemStack(Material.AIR);
@ -59,7 +60,7 @@ public class PanelItemBuilder {
}
public PanelItemBuilder name(@Nullable String name) {
this.name = name != null ? ChatColor.translateAlternateColorCodes('&', name) : null;
this.name = name != null ? Util.translateColorCodes(name) : null;
return this;
}

View File

@ -0,0 +1,203 @@
//
// Created by BONNe
// Copyright - 2021
//
package world.bentobox.bentobox.api.panels.builders;
import org.bukkit.World;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;
import world.bentobox.bentobox.api.panels.PanelItem;
import world.bentobox.bentobox.api.panels.PanelListener;
import world.bentobox.bentobox.api.panels.TemplatedPanel;
import world.bentobox.bentobox.api.panels.reader.ItemTemplateRecord;
import world.bentobox.bentobox.api.panels.reader.PanelTemplateRecord;
import world.bentobox.bentobox.api.panels.reader.TemplateReader;
import world.bentobox.bentobox.api.user.User;
/**
* Builds {@link TemplatedPanel}'s
* @author BONNe
* @since 1.17.3
*/
public class TemplatedPanelBuilder
{
// ---------------------------------------------------------------------
// Section: Builder
// ---------------------------------------------------------------------
/**
* Adds the template that must be loaded for Template panel builder.
*
* @param guiName the gui name
* @param dataFolder the data folder
* @return the template panel builder
*/
public TemplatedPanelBuilder template(String guiName, File dataFolder)
{
this.panelTemplate = TemplateReader.readTemplatePanel(guiName, dataFolder);
return this;
}
/**
* Adds the user for template panel builder.
*
* @param user the user
* @return the template panel builder
*/
public TemplatedPanelBuilder user(User user)
{
this.user = user;
return this;
}
/**
* Adds the world for template panel builder.
*
* @param world the world
* @return the template panel builder
*/
public TemplatedPanelBuilder world(World world)
{
this.world = world;
return this;
}
/**
* Adds the panel listener for template panel builder.
*
* @param listener the listener
* @return the template panel builder
*/
public TemplatedPanelBuilder listener(PanelListener listener)
{
this.listener = listener;
return this;
}
/**
* Registers new button type builder for template panel builder.
*
* @param type the type
* @param buttonCreator the button creator
* @return the template panel builder
*/
public TemplatedPanelBuilder registerTypeBuilder(String type, BiFunction<ItemTemplateRecord, TemplatedPanel.ItemSlot, PanelItem> buttonCreator)
{
this.objectCreatorMap.put(type, buttonCreator);
return this;
}
/**
* Build templated panel.
*
* @return the templated panel
*/
public TemplatedPanel build()
{
return new TemplatedPanel(this);
}
// ---------------------------------------------------------------------
// Section: Getters
// ---------------------------------------------------------------------
/**
* Gets panel template.
*
* @return the panel template
*/
public PanelTemplateRecord getPanelTemplate()
{
return this.panelTemplate;
}
/**
* Gets user.
*
* @return the user
*/
public User getUser()
{
return this.user;
}
/**
* Gets world.
*
* @return the world
*/
public World getWorld()
{
return this.world;
}
/**
* Gets listener.
*
* @return the listener
*/
public PanelListener getListener()
{
return this.listener;
}
/**
* Gets object creator map.
*
* @return the object creator map
*/
public Map<String, BiFunction<ItemTemplateRecord, TemplatedPanel.ItemSlot, PanelItem>> getObjectCreatorMap()
{
return this.objectCreatorMap;
}
// ---------------------------------------------------------------------
// Section: Variables
// ---------------------------------------------------------------------
/**
* The GUI template record.
*/
private PanelTemplateRecord panelTemplate;
/**
* The user who opens the GUI.
*/
private User user;
/**
* The world where GUI operates.
*/
private World world;
/**
* Panel Listener
*/
private PanelListener listener;
/**
* Map that links objects with their panel item creators.
*/
private final Map<String, BiFunction<ItemTemplateRecord, TemplatedPanel.ItemSlot, PanelItem>> objectCreatorMap = new HashMap<>();
}

View File

@ -0,0 +1,21 @@
/**
* API for GUI panel creation and usage.
*
* <p>
* BentoBox provides an API that enables Addon developers to display a GUI.
* There is the basic Panel and the more advanced TabbedPanel. Both use Builders
* to make them. For examples, look at the Settings classes.
* </p>
* <p>
* Clicking on a panel item is handled by classes that implement the PanelListener interface.
* When a click is made, the listener is called with various parameters available. It is possible
* to have an overall listener for the whole panel and also individual listeners just for the
* icon that was clicked.
* <p>
* The tabbed panel contains Tabs that are accessible via icons at the top of the panel.
* </p>
*
* @author tastybento
* @since 1.7.0
*/
package world.bentobox.bentobox.api.panels;

View File

@ -0,0 +1,89 @@
//
// Created by BONNe
// Copyright - 2021
//
package world.bentobox.bentobox.api.panels.reader;
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.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* This Record contains all necessary information about Item Template that can be used to craft panel item.
*
* @param icon ItemStack of the Item
* @param title Title of the item
* @param description Lore message of the item
* @param actions List of Actions for a button
* @param dataMap DataMap that links additional objects for a button.
* @param fallback FallBack item if current one is not possible to generate.
*
* @since 1.17.3
*/
public record ItemTemplateRecord(@Nullable ItemStack icon,
@Nullable String title,
@Nullable String description,
@NonNull List<ActionRecords> actions,
@NonNull Map<String, Object> dataMap,
@Nullable ItemTemplateRecord fallback)
{
/**
* Instantiates a new Item template record without actions and data map.
*
* @param icon the icon
* @param title the title
* @param description the description
* @param fallback the fallback
*/
public ItemTemplateRecord(ItemStack icon, String title, String description, ItemTemplateRecord fallback)
{
this(icon, title, description, new ArrayList<>(6), new HashMap<>(0), fallback);
}
/**
* This method adds given object associated with key into data map.
* @param key Key value of object.
* @param data Data that is associated with a key.
*/
public void addData(String key, Object data)
{
this.dataMap.put(key, data);
}
/**
* Add action to the actions list.
*
* @param actionData the action data
*/
public void addAction(ActionRecords actionData)
{
this.actions.add(actionData);
}
// ---------------------------------------------------------------------
// Section: Classes
// ---------------------------------------------------------------------
/**
* The Action Records holds data about each action.
*
* @param clickType the click type
* @param actionType the string that represents action type
* @param content the content of the action
* @param tooltip the tooltip of action
*/
public record ActionRecords(ClickType clickType, String actionType, String content, String tooltip) {}
}

View File

@ -0,0 +1,79 @@
//
// Created by BONNe
// Copyright - 2021
//
package world.bentobox.bentobox.api.panels.reader;
import org.bukkit.inventory.ItemStack;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.api.panels.Panel;
/**
* This is template object for the panel reader. It contains data that can exist in the panel.
* PanelBuilder will use this to build panel.
*
* @param type the type of GUI
* @param title the title of GUI
* @param border the border block for GUI
* @param background the background block for GUI.
* @param forcedRows the array of boolean that indicate which rows must be force loaded.
* @param content The 2D array of ItemTemplateRecords
*
* @since 1.17.3
*/
public record PanelTemplateRecord(Panel.Type type,
@Nullable String title,
@Nullable TemplateItem border,
@Nullable TemplateItem background,
boolean[] forcedRows,
@NonNull ItemTemplateRecord[][] content)
{
/**
* Instantiates a new Panel template record with empty content.
*
* @param type the type
* @param title the title
* @param border the border
* @param background the background
* @param forcedRows the forced rows array
*/
public PanelTemplateRecord(Panel.Type type, String title, TemplateItem border, TemplateItem background, boolean[] forcedRows)
{
this(type, title, border, background, forcedRows, new ItemTemplateRecord[6][9]);
}
/**
* This method adds give item template record in given slot.
* @param rowIndex row index of content array
* @param columnIndex column index of content array.
* @param panelItemTemplate item template record that must be added.
*/
public void addButtonTemplate(int rowIndex, int columnIndex, ItemTemplateRecord panelItemTemplate)
{
this.content[rowIndex][columnIndex] = panelItemTemplate;
}
// ---------------------------------------------------------------------
// Section: Classes
// ---------------------------------------------------------------------
/**
* This record contains info about border and background item.
*/
public record TemplateItem(ItemStack icon, String title, String description)
{
public TemplateItem(ItemStack icon)
{
this(icon, null, null);
}
}
}

View File

@ -0,0 +1,364 @@
//
// Created by BONNe
// Copyright - 2021
//
package world.bentobox.bentobox.api.panels.reader;
import com.google.common.base.Enums;
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.event.inventory.ClickType;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import world.bentobox.bentobox.api.panels.Panel;
import world.bentobox.bentobox.util.ItemParser;
/**
* This class manages Template file reading, creating PanelTemplateRecord object and storing it internally.
* This class just reads and returns given panel template. It does not create a functional panel.
*
* @since 1.17.3
*/
public class TemplateReader
{
/**
* Read template panel panel template record.
*
* @param panelName the panel name
* @param panelLocation the panel location directory
* @return the panel template record
*/
public static PanelTemplateRecord readTemplatePanel(@NonNull String panelName, @NonNull File panelLocation)
{
if (!panelLocation.exists())
{
// Return null because folder does not exist.
return null;
}
File file = new File(panelLocation, panelName.endsWith(".yml") ? panelName : panelName + ".yml");
if (!file.exists())
{
// Return as file does not exist.
return null;
}
// Check if panel is already crafted.
if (TemplateReader.loadedPanels.containsKey(file.getAbsolutePath()))
{
return TemplateReader.loadedPanels.get(file.getAbsolutePath());
}
PanelTemplateRecord record;
try
{
// Load config
YamlConfiguration config = new YamlConfiguration();
config.load(file);
// Read panel
record = readPanelTemplate(config.getConfigurationSection(panelName));
// Put panel into memory
TemplateReader.loadedPanels.put(file.getAbsolutePath(), record);
}
catch (IOException | InvalidConfigurationException e)
{
record = null;
}
return record;
}
/**
* This method reads panel template from given configuration section.
* @param configurationSection Section that contains panel template data.
* @return Panel Template.
*/
private static PanelTemplateRecord readPanelTemplate(@Nullable ConfigurationSection configurationSection)
{
if (configurationSection == null)
{
// No data to return.
return null;
}
String title = configurationSection.getString("title");
Panel.Type type =
Enums.getIfPresent(Panel.Type.class, configurationSection.getString("type", "INVENTORY")).
or(Panel.Type.INVENTORY);
PanelTemplateRecord.TemplateItem borderItem = null;
// Read Border Icon.
if (configurationSection.isConfigurationSection("border"))
{
// Process border icon if it contains more options.
ConfigurationSection borderSection = configurationSection.getConfigurationSection("border");
if (borderSection != null)
{
borderItem = new PanelTemplateRecord.TemplateItem(
ItemParser.parse((borderSection.getString("icon", Material.AIR.name()))),
borderSection.getString("title", null),
borderSection.getString("description", null));
}
}
else if (configurationSection.isString("border"))
{
// Process border icon if it contains only icon.
borderItem = new PanelTemplateRecord.TemplateItem(
ItemParser.parse((configurationSection.getString("border", Material.AIR.name()))));
}
PanelTemplateRecord.TemplateItem backgroundItem = null;
// Read Background block
if (configurationSection.isConfigurationSection("background"))
{
// Process border icon if it contains more options.
ConfigurationSection backgroundSection = configurationSection.getConfigurationSection("background");
if (backgroundSection != null)
{
backgroundItem = new PanelTemplateRecord.TemplateItem(
ItemParser.parse((backgroundSection.getString("icon", Material.AIR.name()))),
backgroundSection.getString("title", null),
backgroundSection.getString("description", null));
}
}
else if (configurationSection.isString("background"))
{
// Process background icon if it contains only icon.
backgroundItem = new PanelTemplateRecord.TemplateItem(
ItemParser.parse((configurationSection.getString("background", Material.AIR.name()))));
}
// Read reusable
Map<String, ItemTemplateRecord> panelItemDataMap = new HashMap<>();
ConfigurationSection reusable = configurationSection.getConfigurationSection("reusable");
if (reusable != null)
{
// Add all reusables to the local storage.
reusable.getKeys(false).forEach(key ->
readPanelItemTemplate(reusable.getConfigurationSection(key), key, panelItemDataMap));
}
// Read content
boolean[] forcedRows = readForcedRows(configurationSection);
// Create template record.
PanelTemplateRecord template = new PanelTemplateRecord(type, title, borderItem, backgroundItem, forcedRows);
// Read content
ConfigurationSection content = configurationSection.getConfigurationSection("content");
if (content == null)
{
// Return empty template.
return template;
}
for (int rowIndex = 0; rowIndex < 6; rowIndex++)
{
// Read each line.
if (content.isConfigurationSection(String.valueOf(rowIndex + 1)))
{
ConfigurationSection line = content.getConfigurationSection(String.valueOf(rowIndex + 1));
if (line != null)
{
// Populate existing lines with items.
for (int columnIndex = 0; columnIndex < 9; columnIndex++)
{
if (line.isConfigurationSection(String.valueOf(columnIndex + 1)))
{
// If it contains a section, then build a new button template from it.
template.addButtonTemplate(rowIndex,
columnIndex,
readPanelItemTemplate(line.getConfigurationSection(String.valueOf(columnIndex + 1))));
}
else if (line.isString(String.valueOf(columnIndex + 1)))
{
// If it contains just a single word, assume it is a reusable.
template.addButtonTemplate(rowIndex,
columnIndex,
panelItemDataMap.get(line.getString(String.valueOf(columnIndex + 1))));
}
}
}
}
}
// Garbage collector.
panelItemDataMap.clear();
return template;
}
/**
* This method reads force shown rows that must be always displayed.
* @param section Configuration section that contains force-shown path.
* @return boolean array that contains which lines are force loaded.
*/
private static boolean[] readForcedRows(@Nullable ConfigurationSection section)
{
boolean[] forceShow = new boolean[6];
if (section != null && section.contains("force-shown"))
{
if (section.isInt("force-shown"))
{
int value = section.getInt("force-shown");
if (value > 0 && value < 7)
{
forceShow[value-1] = true;
}
}
else if (section.isList("force-shown"))
{
section.getIntegerList("force-shown").forEach(number -> {
if (number > 0 && number < 7)
{
forceShow[number-1] = true;
}
});
}
}
return forceShow;
}
/**
* This method creates PanelItemTemplate from a given configuration section.
* @param section Section that should contain all information about the panel item template.
* @return PanelItemTemplate that should represent button from a section.
*/
@Nullable
private static ItemTemplateRecord readPanelItemTemplate(@Nullable ConfigurationSection section)
{
return readPanelItemTemplate(section, null, null);
}
/**
* This method creates PanelItemTemplate from a given configuration section.
* @param section Section that should contain all information about the panel item template.
* @return PanelItemTemplate that should represent button from a section.
*/
@Nullable
private static ItemTemplateRecord readPanelItemTemplate(@Nullable ConfigurationSection section,
String itemKey,
Map<String, ItemTemplateRecord> reusableItemMap)
{
if (section == null)
{
// No section, no item.
return null;
}
ItemTemplateRecord fallback;
if (section.isConfigurationSection("fallback"))
{
fallback = readPanelItemTemplate(section.getConfigurationSection("fallback"));
}
else if (section.isString("fallback") && reusableItemMap != null)
{
fallback = reusableItemMap.get(section.getString("fallback"));
}
else
{
fallback = null;
}
// Create Item Record
ItemTemplateRecord itemRecord = new ItemTemplateRecord(ItemParser.parse(section.getString("icon")),
section.getString("title", null),
section.getString("description", null),
fallback);
// Read data
if (section.isConfigurationSection("data"))
{
ConfigurationSection dataSection = section.getConfigurationSection("data");
if (dataSection != null)
{
dataSection.getKeys(false).forEach(key -> itemRecord.addData(key, dataSection.get(key)));
}
}
// Read Click data
if (section.isConfigurationSection("actions"))
{
ConfigurationSection actionSection = section.getConfigurationSection("actions");
if (actionSection != null)
{
actionSection.getKeys(false).forEach(actionKey -> {
ClickType clickType = Enums.getIfPresent(ClickType.class, actionKey.toUpperCase()).orNull();
if (clickType != null)
{
ConfigurationSection actionDataSection = actionSection.getConfigurationSection(actionKey);
if (actionDataSection != null)
{
ItemTemplateRecord.ActionRecords actionData =
new ItemTemplateRecord.ActionRecords(clickType,
actionDataSection.getString("type"),
actionDataSection.getString("content"),
actionDataSection.getString("tooltip"));
itemRecord.addAction(actionData);
}
}
});
}
}
// Add item to the map
if (reusableItemMap != null && itemKey != null)
{
reusableItemMap.put(itemKey, itemRecord);
}
return itemRecord;
}
/**
* This method clears loaded panels from the cache.
*/
public static void clearPanels()
{
loadedPanels.clear();
}
/**
* This map contains already read panels and their location.
* This improves performance for GUI opening, with a some memory usage.
*/
private static final Map<String, PanelTemplateRecord> loadedPanels = new HashMap<>();
}

View File

@ -0,0 +1,7 @@
/**
* API for placeholder replacement.
*
* @since 1.5.0
* @author Poslovitch
*/
package world.bentobox.bentobox.api.placeholders;

View File

@ -3,7 +3,7 @@ package world.bentobox.bentobox.api.placeholders.placeholderapi;
import world.bentobox.bentobox.api.addons.Addon;
public class AddonPlaceholderExpansion extends BasicPlaceholderExpansion {
private Addon addon;
private final Addon addon;
public AddonPlaceholderExpansion(Addon addon) {
this.addon = addon;

View File

@ -16,7 +16,7 @@ import world.bentobox.bentobox.api.user.User;
*/
abstract class BasicPlaceholderExpansion extends PlaceholderExpansion {
@NonNull
private Map<@NonNull String, @NonNull PlaceholderReplacer> placeholders;
private final Map<@NonNull String, @NonNull PlaceholderReplacer> placeholders;
BasicPlaceholderExpansion() {
super();

View File

@ -3,7 +3,7 @@ package world.bentobox.bentobox.api.placeholders.placeholderapi;
import world.bentobox.bentobox.BentoBox;
public class BentoBoxPlaceholderExpansion extends BasicPlaceholderExpansion {
private BentoBox plugin;
private final BentoBox plugin;
public BentoBoxPlaceholderExpansion(BentoBox plugin) {
super();

View File

@ -9,7 +9,7 @@ import com.google.common.cache.LoadingCache;
/**
* Utilities class that helps to avoid spamming the User with potential repeated messages
* @author Poslovitch
* @author Poslovitch, tastybento
*/
public class Notifier {
@ -18,17 +18,19 @@ public class Notifier {
*/
private static final int NOTIFICATION_DELAY = 4;
private record Notification(String message, long time) {}
private final LoadingCache<User, Notification> notificationCache = CacheBuilder.newBuilder()
.expireAfterAccess(NOTIFICATION_DELAY, TimeUnit.SECONDS)
.maximumSize(500)
.build(
new CacheLoader<User, Notification>() {
new CacheLoader<>() {
@Override
public Notification load(User user) {
return new Notification(null, 0);
}
}
);
);
/**
* Sends message to a user only if the message hasn't been sent recently
@ -41,7 +43,7 @@ public class Notifier {
Notification lastNotification = notificationCache.get(user);
long now = System.currentTimeMillis();
if (now >= lastNotification.getTime() + (NOTIFICATION_DELAY * 1000) || !message.equals(lastNotification.getMessage())) {
if (now >= lastNotification.time() + (NOTIFICATION_DELAY * 1000) || !message.equals(lastNotification.message())) {
notificationCache.put(user, new Notification(message, now));
user.sendRawMessage(message);
return true;
@ -52,21 +54,4 @@ public class Notifier {
}
}
private class Notification {
private final String message;
private final long time;
private Notification(String message, long time) {
this.message = message;
this.time = time;
}
public String getMessage() {
return message;
}
public long getTime() {
return time;
}
}
}

View File

@ -47,7 +47,7 @@ import world.bentobox.bentobox.util.Util;
*/
public class User implements MetaDataAble {
private static Map<UUID, User> users = new HashMap<>();
private static final Map<UUID, User> users = new HashMap<>();
/**
* Clears all users from the user list
@ -136,7 +136,7 @@ public class User implements MetaDataAble {
private static BentoBox plugin = BentoBox.getInstance();
@Nullable
private Player player;
private final Player player;
private OfflinePlayer offlinePlayer;
private final UUID playerUUID;
@Nullable
@ -410,7 +410,7 @@ public class User implements MetaDataAble {
translation = plugin.getPlaceholdersManager().replacePlaceholders(player, translation);
}
return Util.stripSpaceAfterColorCodes(ChatColor.translateAlternateColorCodes('&', translation));
return Util.translateColorCodes(translation);
}
}
@ -618,10 +618,9 @@ public class User implements MetaDataAble {
if (obj == null) {
return false;
}
if (!(obj instanceof User)) {
if (!(obj instanceof User other)) {
return false;
}
User other = (User) obj;
if (playerUUID == null) {
return other.playerUUID == null;
} else return playerUUID.equals(other.playerUUID);

View File

@ -0,0 +1,18 @@
/**
* API for BentoBox Users
*
* <p>
* BentoBox has extended the Bukkit Player class to become a User with the primary reason
* to enable localized messaging. Apart from that a User can be an OfflinePlayer, a Player,
* or even the Console. Users are used throughout the BentoBox code base, e.g., for commands
* instead of Players. It is possible to get a Player from a User if the User is a player.
* </p>
* <p>
* Notifier is a special kind of messaging class that prevents spamming of messages to a
* user in chat. It is useful for cases when the same message may be generated many times, e.g., in
* a protection scenario.
* </p>
* @author tastybento
*
*/
package world.bentobox.bentobox.api.user;

View File

@ -63,10 +63,10 @@ public class BlueprintClipboard {
private boolean copying;
private int index;
private int lastPercentage;
private Map<Vector, List<BlueprintEntity>> bpEntities = new LinkedHashMap<>();
private Map<Vector, BlueprintBlock> bpAttachable = new LinkedHashMap<>();
private Map<Vector, BlueprintBlock> bpBlocks = new LinkedHashMap<>();
private BentoBox plugin = BentoBox.getInstance();
private final Map<Vector, List<BlueprintEntity>> bpEntities = new LinkedHashMap<>();
private final Map<Vector, BlueprintBlock> bpAttachable = new LinkedHashMap<>();
private final Map<Vector, BlueprintBlock> bpBlocks = new LinkedHashMap<>();
private final BentoBox plugin = BentoBox.getInstance();
/**
* Create a clipboard for blueprint
@ -190,48 +190,7 @@ public class BlueprintClipboard {
Vector pos = new Vector(x, y, z);
// Set entities
List<BlueprintEntity> bpEnts = new ArrayList<>();
for (LivingEntity entity: entities) {
BlueprintEntity bpe = new BlueprintEntity();
bpe.setType(entity.getType());
bpe.setCustomName(entity.getCustomName());
if (entity instanceof Villager) {
setVillager(entity, bpe);
}
if (entity instanceof Colorable) {
Colorable c = (Colorable)entity;
if (c.getColor() != null) {
bpe.setColor(c.getColor());
}
}
if (entity instanceof Tameable) {
bpe.setTamed(((Tameable)entity).isTamed());
}
if (entity instanceof ChestedHorse) {
bpe.setChest(((ChestedHorse)entity).isCarryingChest());
}
// Only set if child. Most animals are adults
if (entity instanceof Ageable && !((Ageable)entity).isAdult()) {
bpe.setAdult(false);
}
if (entity instanceof AbstractHorse) {
AbstractHorse horse = (AbstractHorse)entity;
bpe.setDomestication(horse.getDomestication());
bpe.setInventory(new HashMap<>());
for (int i = 0; i < horse.getInventory().getSize(); i++) {
ItemStack item = horse.getInventory().getItem(i);
if (item != null) {
bpe.getInventory().put(i, item);
}
}
}
if (entity instanceof Horse) {
Horse horse = (Horse)entity;
bpe.setStyle(horse.getStyle());
}
bpEnts.add(bpe);
}
List<BlueprintEntity> bpEnts = setEntities(entities);
// Store
if (!bpEnts.isEmpty()) {
bpEntities.put(pos, bpEnts);
@ -242,22 +201,31 @@ public class BlueprintClipboard {
return true;
}
BlueprintBlock b = bluePrintBlock(pos, block);
if (b != null) {
this.bpBlocks.put(pos, b);
}
return true;
}
private BlueprintBlock bluePrintBlock(Vector pos, Block block) {
// Block state
BlockState blockState = block.getState();
BlueprintBlock b = new BlueprintBlock(block.getBlockData().getAsString());
// Biome
b.setBiome(block.getBiome());
// Signs
if (blockState instanceof Sign) {
Sign sign = (Sign)blockState;
if (blockState instanceof Sign sign) {
b.setSignLines(Arrays.asList(sign.getLines()));
b.setGlowingText(sign.isGlowingText());
}
// Set block data
if (blockState.getData() instanceof Attachable) {
// Placeholder for attachment
bpBlocks.put(pos, new BlueprintBlock("minecraft:air"));
bpAttachable.put(pos, b);
return true;
return null;
}
if (block.getType().equals(Material.BEDROCK)) {
@ -272,9 +240,8 @@ public class BlueprintClipboard {
}
// Chests
if (blockState instanceof InventoryHolder) {
if (blockState instanceof InventoryHolder ih) {
b.setInventory(new HashMap<>());
InventoryHolder ih = (InventoryHolder)blockState;
for (int i = 0; i < ih.getInventory().getSize(); i++) {
ItemStack item = ih.getInventory().getItem(i);
if (item != null) {
@ -283,17 +250,8 @@ public class BlueprintClipboard {
}
}
if (blockState instanceof CreatureSpawner) {
CreatureSpawner spawner = (CreatureSpawner)blockState;
BlueprintCreatureSpawner cs = new BlueprintCreatureSpawner();
cs.setSpawnedType(spawner.getSpawnedType());
cs.setDelay(spawner.getDelay());
cs.setMaxNearbyEntities(spawner.getMaxNearbyEntities());
cs.setMaxSpawnDelay(spawner.getMaxSpawnDelay());
cs.setMinSpawnDelay(spawner.getMinSpawnDelay());
cs.setRequiredPlayerRange(spawner.getRequiredPlayerRange());
cs.setSpawnRange(spawner.getSpawnRange());
b.setCreatureSpawner(cs);
if (blockState instanceof CreatureSpawner spawner) {
b.setCreatureSpawner(getSpawner(spawner));
}
// Banners
@ -301,8 +259,62 @@ public class BlueprintClipboard {
b.setBannerPatterns(((Banner) blockState).getPatterns());
}
this.bpBlocks.put(pos, b);
return true;
return b;
}
private BlueprintCreatureSpawner getSpawner(CreatureSpawner spawner) {
BlueprintCreatureSpawner cs = new BlueprintCreatureSpawner();
cs.setSpawnedType(spawner.getSpawnedType());
cs.setDelay(spawner.getDelay());
cs.setMaxNearbyEntities(spawner.getMaxNearbyEntities());
cs.setMaxSpawnDelay(spawner.getMaxSpawnDelay());
cs.setMinSpawnDelay(spawner.getMinSpawnDelay());
cs.setRequiredPlayerRange(spawner.getRequiredPlayerRange());
cs.setSpawnRange(spawner.getSpawnRange());
return cs;
}
private List<BlueprintEntity> setEntities(Collection<LivingEntity> entities) {
List<BlueprintEntity> bpEnts = new ArrayList<>();
for (LivingEntity entity: entities) {
BlueprintEntity bpe = new BlueprintEntity();
bpe.setType(entity.getType());
bpe.setCustomName(entity.getCustomName());
if (entity instanceof Villager) {
setVillager(entity, bpe);
}
if (entity instanceof Colorable c) {
if (c.getColor() != null) {
bpe.setColor(c.getColor());
}
}
if (entity instanceof Tameable) {
bpe.setTamed(((Tameable)entity).isTamed());
}
if (entity instanceof ChestedHorse) {
bpe.setChest(((ChestedHorse)entity).isCarryingChest());
}
// Only set if child. Most animals are adults
if (entity instanceof Ageable && !((Ageable)entity).isAdult()) {
bpe.setAdult(false);
}
if (entity instanceof AbstractHorse horse) {
bpe.setDomestication(horse.getDomestication());
bpe.setInventory(new HashMap<>());
for (int i = 0; i < horse.getInventory().getSize(); i++) {
ItemStack item = horse.getInventory().getItem(i);
if (item != null) {
bpe.getInventory().put(i, item);
}
}
}
if (entity instanceof Horse horse) {
bpe.setStyle(horse.getStyle());
}
bpEnts.add(bpe);
}
return bpEnts;
}
/**

View File

@ -13,7 +13,6 @@ import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
@ -73,7 +72,7 @@ public class BlueprintPaster {
private static final Map<String, String> BLOCK_CONVERSION = ImmutableMap.of("sign", "oak_sign", "wall_sign", "oak_wall_sign");
private BentoBox plugin;
private final BentoBox plugin;
// The minimum block position (x,y,z)
private Location pos1;
// The maximum block position (x,y,z)
@ -86,19 +85,19 @@ public class BlueprintPaster {
* The Blueprint to paste.
*/
@NonNull
private Blueprint blueprint;
private final Blueprint blueprint;
/**
* The Location to paste to.
*/
@NonNull
private Location location;
private final Location location;
/**
* Island related to this paste, may be null.
*/
@Nullable
private Island island;
private final Island island;
/**
* Paste a clipboard to a location and run task
@ -291,37 +290,39 @@ public class BlueprintPaster {
// Get the block state
BlockState bs = block.getState();
// Signs
if (bs instanceof org.bukkit.block.Sign) {
writeSign(block, bpBlock.getSignLines());
if (bs instanceof org.bukkit.block.Sign sign) {
writeSign(block, bpBlock.getSignLines(), bpBlock.isGlowingText());
}
// Chests, in general
if (bs instanceof InventoryHolder) {
Inventory ih = ((InventoryHolder)bs).getInventory();
// Double chests are pasted as two blocks so inventory is filled twice. This code stops over filling for the first block.
// Double chests are pasted as two blocks so inventory is filled twice.
// This code stops over-filling for the first block.
bpBlock.getInventory().forEach(ih::setItem);
}
// Mob spawners
if (bs instanceof CreatureSpawner) {
CreatureSpawner spawner = ((CreatureSpawner) bs);
BlueprintCreatureSpawner s = bpBlock.getCreatureSpawner();
spawner.setSpawnedType(s.getSpawnedType());
spawner.setMaxNearbyEntities(s.getMaxNearbyEntities());
spawner.setMaxSpawnDelay(s.getMaxSpawnDelay());
spawner.setMinSpawnDelay(s.getMinSpawnDelay());
spawner.setDelay(s.getDelay());
spawner.setRequiredPlayerRange(s.getRequiredPlayerRange());
spawner.setSpawnRange(s.getSpawnRange());
bs.update(true, false);
if (bs instanceof CreatureSpawner spawner) {
setSpawner(spawner, bpBlock.getCreatureSpawner());
}
// Banners
if (bs instanceof Banner && bpBlock.getBannerPatterns() != null) {
Banner banner = (Banner) bs;
if (bs instanceof Banner banner && bpBlock.getBannerPatterns() != null) {
bpBlock.getBannerPatterns().removeIf(Objects::isNull);
banner.setPatterns(bpBlock.getBannerPatterns());
banner.update(true, false);
}
}
private void setSpawner(CreatureSpawner spawner, BlueprintCreatureSpawner s) {
spawner.setSpawnedType(s.getSpawnedType());
spawner.setMaxNearbyEntities(s.getMaxNearbyEntities());
spawner.setMaxSpawnDelay(s.getMaxSpawnDelay());
spawner.setMinSpawnDelay(s.getMinSpawnDelay());
spawner.setDelay(s.getDelay());
spawner.setRequiredPlayerRange(s.getRequiredPlayerRange());
spawner.setSpawnRange(s.getSpawnRange());
spawner.update(true, false);
}
/**
* Sets any entity that is in this location
* @param location - location
@ -386,7 +387,7 @@ public class BlueprintPaster {
}
}
private void writeSign(final Block block, final List<String> lines) {
private void writeSign(final Block block, final List<String> lines, boolean glow) {
BlockFace bf;
if (block.getType().name().contains("WALL_SIGN")) {
WallSign wallSign = (WallSign)block.getBlockData();
@ -416,7 +417,7 @@ public class BlueprintPaster {
// Get the addon that is operating in this world
String addonName = plugin.getIWM().getAddon(island.getWorld()).map(addon -> addon.getDescription().getName().toLowerCase(Locale.ENGLISH)).orElse("");
for (int i = 0; i < 4; i++) {
s.setLine(i, ChatColor.translateAlternateColorCodes('&', plugin.getLocalesManager().getOrDefault(User.getInstance(island.getOwner()),
s.setLine(i, Util.translateColorCodes(plugin.getLocalesManager().getOrDefault(User.getInstance(island.getOwner()),
addonName + ".sign.line" + i,"").replace(TextVariables.NAME, name)));
}
} else {
@ -425,6 +426,7 @@ public class BlueprintPaster {
s.setLine(i, lines.get(i));
}
}
s.setGlowingText(glow);
// Update the sign
s.update();
}

View File

@ -3,7 +3,6 @@ package world.bentobox.bentobox.blueprints.conversation;
import java.util.ArrayList;
import java.util.List;
import org.bukkit.ChatColor;
import org.bukkit.conversations.ConversationContext;
import org.bukkit.conversations.Prompt;
import org.bukkit.conversations.StringPrompt;
@ -13,6 +12,8 @@ import world.bentobox.bentobox.api.addons.GameModeAddon;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBundle;
import world.bentobox.bentobox.util.Util;
/**
* Collects a description
@ -22,8 +23,8 @@ import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBundle;
public class DescriptionPrompt extends StringPrompt {
private static final String DESCRIPTION = "description";
private GameModeAddon addon;
private BlueprintBundle bb;
private final GameModeAddon addon;
private final BlueprintBundle bb;
public DescriptionPrompt(GameModeAddon addon, BlueprintBundle bb) {
this.addon = addon;
@ -57,7 +58,7 @@ public class DescriptionPrompt extends StringPrompt {
if (context.getSessionData(DESCRIPTION) != null) {
desc = ((List<String>) context.getSessionData(DESCRIPTION));
}
desc.add(ChatColor.translateAlternateColorCodes('&', input));
desc.add(Util.translateColorCodes(input));
context.setSessionData(DESCRIPTION, desc);
return this;
}

View File

@ -15,8 +15,8 @@ import world.bentobox.bentobox.panels.BlueprintManagementPanel;
public class DescriptionSuccessPrompt extends MessagePrompt {
private GameModeAddon addon;
private BlueprintBundle bb;
private final GameModeAddon addon;
private final BlueprintBundle bb;
/**
* @param addon game mode addon

View File

@ -15,12 +15,14 @@ import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.blueprints.Blueprint;
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBundle;
import world.bentobox.bentobox.managers.BlueprintsManager;
import world.bentobox.bentobox.util.Util;
public class NamePrompt extends StringPrompt {
private GameModeAddon addon;
private final GameModeAddon addon;
@Nullable
private BlueprintBundle bb;
private final BlueprintBundle bb;
@Nullable
private Blueprint bp;
@ -45,7 +47,7 @@ public class NamePrompt extends StringPrompt {
public Prompt acceptInput(ConversationContext context, String input) {
User user = User.getInstance((Player)context.getForWhom());
// Convert color codes
input = ChatColor.translateAlternateColorCodes('&', input);
input = Util.translateColorCodes(input);
if (ChatColor.stripColor(input).length() > 32) {
context.getForWhom().sendRawMessage("Too long");
return this;

View File

@ -17,9 +17,9 @@ import world.bentobox.bentobox.panels.BlueprintManagementPanel;
public class NameSuccessPrompt extends MessagePrompt {
private GameModeAddon addon;
private final GameModeAddon addon;
private BlueprintBundle bb;
private Blueprint bp;
private final Blueprint bp;
/**
* Handles the name processing
@ -42,7 +42,6 @@ public class NameSuccessPrompt extends MessagePrompt {
if (bp != null) {
BentoBox.getInstance().getBlueprintsManager().renameBlueprint(addon, bp, name);
new BlueprintManagementPanel(BentoBox.getInstance(), user, addon).openBB(bb);
return user.getTranslation("commands.admin.blueprint.management.description.success");
} else {
// Blueprint Bundle
if (bb == null) {
@ -61,8 +60,8 @@ public class NameSuccessPrompt extends MessagePrompt {
new BlueprintManagementPanel(BentoBox.getInstance(), user, addon).openPanel();
// Set the name
// if successfully
return user.getTranslation("commands.admin.blueprint.management.description.success");
}
return user.getTranslation("commands.admin.blueprint.management.description.success");
}
@Override

View File

@ -34,6 +34,8 @@ public class BlueprintBlock {
*/
@Expose
private List<Pattern> bannerPatterns;
@Expose
private boolean glowingText;
public BlueprintBlock(String blockData) {
this.blockData = blockData;
@ -124,4 +126,20 @@ public class BlueprintBlock {
public void setBiome(Biome biome) {
this.biome = biome;
}
/**
* @return the glowingText
*/
public boolean isGlowingText() {
return glowingText;
}
/**
* @param glowingText the glowingText to set
*/
public void setGlowingText(boolean glowingText) {
this.glowingText = glowingText;
}
}

View File

@ -75,8 +75,7 @@ public class BlueprintEntity {
((Ageable)e).setBaby();
}
}
if (e instanceof AbstractHorse) {
AbstractHorse horse = (AbstractHorse)e;
if (e instanceof AbstractHorse horse) {
if (domestication != null) horse.setDomestication(domestication);
if (inventory != null) {
inventory.forEach(horse.getInventory()::setItem);

View File

@ -0,0 +1,10 @@
/**
* This package contains non-API classes that handle Blueprints.
*
* Blueprints are BentoBox's version of schematics, but contain
* additional useful info.
*
* @author tastybento
*
*/
package world.bentobox.bentobox.blueprints;

View File

@ -49,12 +49,12 @@ public class BlueprintClipboardFormat implements ClipboardFormat {
}
@Override
public ClipboardReader getReader(InputStream inputStream) throws IOException {
public ClipboardReader getReader(InputStream inputStream) {
return new BlueprintClipboardReader(inputStream);
}
@Override
public ClipboardWriter getWriter(OutputStream outputStream) throws IOException {
public ClipboardWriter getWriter(OutputStream outputStream) {
return new BlueprintClipboardWriter(outputStream);
}

View File

@ -1,6 +1,5 @@
package world.bentobox.bentobox.blueprints.worldedit;
import java.io.IOException;
import java.io.InputStream;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
@ -12,19 +11,19 @@ import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader;
*/
public class BlueprintClipboardReader implements ClipboardReader {
private InputStream inputStream;
private final InputStream inputStream;
public BlueprintClipboardReader(InputStream inputStream) {
this.inputStream = inputStream;
}
@Override
public Clipboard read() throws IOException {
public Clipboard read() {
throw new UnsupportedOperationException(); // TODO
}
@Override
public void close() throws IOException {
public void close() {
throw new UnsupportedOperationException(); // TODO
}

View File

@ -1,6 +1,5 @@
package world.bentobox.bentobox.blueprints.worldedit;
import java.io.IOException;
import java.io.OutputStream;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
@ -12,18 +11,18 @@ import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter;
*/
public class BlueprintClipboardWriter implements ClipboardWriter {
private OutputStream outputStream;
private final OutputStream outputStream;
public BlueprintClipboardWriter(OutputStream outputStream) {
this.outputStream = outputStream;
}
@Override
public void write(Clipboard clipboard) throws IOException {
public void write(Clipboard clipboard) {
throw new UnsupportedOperationException(); // TODO
}
@Override
public void close() throws IOException {
public void close() {
throw new UnsupportedOperationException(); // TODO
}

View File

@ -18,7 +18,7 @@ public class BlueprintSchematicConverter {
private File blueprintFile;
public BlueprintSchematicConverter(File blueprintFile) {
if(!BentoBox.getInstance().getHooks().getHook("WorldEdit").isPresent()) {
if(BentoBox.getInstance().getHooks().getHook("WorldEdit").isEmpty()) {
BentoBox.getInstance().logError("WorldEdit must be installed to use that class !");
return;
}

View File

@ -7,6 +7,7 @@ import org.bukkit.Bukkit;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
import world.bentobox.bentobox.api.events.BentoBoxReadyEvent;
import world.bentobox.bentobox.api.panels.reader.TemplateReader;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.commands.reload.BentoBoxReloadLocalesCommand;
import world.bentobox.bentobox.listeners.PanelListenerManager;
@ -45,6 +46,8 @@ public class BentoBoxReloadCommand extends ConfirmableCommand {
// Close all open panels
PanelListenerManager.closeAllPanels();
// Clear all template panels.
TemplateReader.clearPanels();
// Reload settings
getPlugin().loadSettings();

View File

@ -0,0 +1,7 @@
/**
* The package contains non-API commands for BentoBox itself.
*
* @author tastybento
*
*/
package world.bentobox.bentobox.commands;

View File

@ -21,8 +21,8 @@ import world.bentobox.bentobox.api.addons.Addon;
*/
public class Database<T> {
private AbstractDatabaseHandler<T> handler;
private Logger logger;
private final AbstractDatabaseHandler<T> handler;
private final Logger logger;
private static DatabaseSetup databaseSetup = DatabaseSetup.getDatabase();
/**

View File

@ -17,7 +17,7 @@ import world.bentobox.bentobox.database.DatabaseConnector;
*/
public abstract class AbstractJSONDatabaseHandler<T> extends AbstractDatabaseHandler<T> {
private Gson gson;
private final Gson gson;
/**
* Constructor

View File

@ -6,7 +6,7 @@ import world.bentobox.bentobox.database.DatabaseSetup;
public class JSONDatabase implements DatabaseSetup {
private JSONDatabaseConnector connector = new JSONDatabaseConnector(BentoBox.getInstance());
private final JSONDatabaseConnector connector = new JSONDatabaseConnector(BentoBox.getInstance());
/* (non-Javadoc)
* @see world.bentobox.bentobox.database.DatabaseSetup#getHandler(java.lang.Class)

View File

@ -23,11 +23,11 @@ public class JSONDatabaseConnector implements DatabaseConnector {
@NonNull
public String getUniqueId(String tableName) {
UUID uuid = UUID.randomUUID();
File file = new File(dataFolder, tableName + File.separator + uuid.toString() + JSON);
File file = new File(dataFolder, tableName + File.separator + uuid + JSON);
int limit = 0;
while (file.exists() && limit++ < MAX_LOOPS) {
uuid = UUID.randomUUID();
file = new File(dataFolder, tableName + File.separator + uuid.toString() + JSON);
file = new File(dataFolder, tableName + File.separator + uuid + JSON);
}
return uuid.toString();
}

View File

@ -15,7 +15,7 @@ import world.bentobox.bentobox.api.flags.Flag;
public class FlagTypeAdapter extends TypeAdapter<Flag> {
private BentoBox plugin;
private final BentoBox plugin;
public FlagTypeAdapter(BentoBox plugin) {
this.plugin = plugin;
@ -42,7 +42,7 @@ public class FlagTypeAdapter extends TypeAdapter<Flag> {
// Flags can end up null if an addon that created one is removed or if a flag name was changed
if (f == null) {
// Create a temporary flag with a unique key. It will be immediately deleted after loading
f = new Flag.Builder("NULL_FLAG_"+ UUID.randomUUID().toString(), Material.STONE).build();
f = new Flag.Builder("NULL_FLAG_"+ UUID.randomUUID(), Material.STONE).build();
}
return f;
}

View File

@ -0,0 +1,12 @@
/**
* These are GSON adapters used to serialize and deserialize various data types.
* <p>
* The {@link world.bentobox.bentobox.database.json.adapters.BukkitObjectTypeAdapter}
* is a catch-all adapter that uses the built-in Bukkit serialization capabilities. Before
* we knew about this, there were other ones built, like for Location, that have to remain
* for backwards compatibility reasons.
* </p>
* @author tastybento
*
*/
package world.bentobox.bentobox.database.json.adapters;

View File

@ -40,7 +40,7 @@ public class MongoDBDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
private static final String MONGO_ID = "_id";
private MongoCollection<Document> collection;
private DatabaseConnector dbConnecter;
private final DatabaseConnector dbConnecter;
/**
* Handles the connection to the database and creation of the initial database schema (tables) for
@ -142,12 +142,11 @@ public class MongoDBDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
completableFuture.complete(false);
return completableFuture;
}
if (!(instance instanceof DataObject)) {
if (!(instance instanceof DataObject dataObj)) {
plugin.logError("This class is not a DataObject: " + instance.getClass().getName());
completableFuture.complete(false);
return completableFuture;
}
DataObject dataObj = (DataObject)instance;
try {
Gson gson = getGson();
String toStore = gson.toJson(instance);

View File

@ -1,8 +1,6 @@
package world.bentobox.bentobox.database.objects;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.HashSet;
@ -23,7 +21,6 @@ import org.bukkit.World;
import org.bukkit.World.Environment;
import org.bukkit.entity.Player;
import org.bukkit.util.BoundingBox;
import org.bukkit.util.Vector;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.jetbrains.annotations.NotNull;
@ -37,7 +34,6 @@ import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.configuration.WorldSettings;
import world.bentobox.bentobox.api.events.island.IslandEvent;
import world.bentobox.bentobox.api.flags.Flag;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.logs.LogEntry;
import world.bentobox.bentobox.api.metadata.MetaDataAble;
import world.bentobox.bentobox.api.metadata.MetaDataValue;
@ -49,6 +45,7 @@ import world.bentobox.bentobox.database.objects.adapters.LogEntryListAdapter;
import world.bentobox.bentobox.lists.Flags;
import world.bentobox.bentobox.managers.IslandWorldManager;
import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.bentobox.util.IslandInfo;
import world.bentobox.bentobox.util.Pair;
import world.bentobox.bentobox.util.Util;
@ -1080,71 +1077,21 @@ public class Island implements DataObject, MetaDataAble {
* Shows info of this island to this user.
* @param user the User who is requesting it
* @return always true
* @deprecated Use {@link IslandInfo#showInfo(User) instead}
*/
@Deprecated
public boolean showInfo(User user) {
BentoBox plugin = BentoBox.getInstance();
user.sendMessage("commands.admin.info.title");
user.sendMessage("commands.admin.info.island-uuid", "[uuid]", this.getUniqueId());
if (getOwner() == null) {
user.sendMessage("commands.admin.info.unowned");
} else {
user.sendMessage("commands.admin.info.owner", "[owner]", plugin.getPlayers().getName(getOwner()), "[uuid]", getOwner().toString());
// Fixes #getLastPlayed() returning 0 when it is the owner's first connection.
long lastPlayed = (Bukkit.getServer().getOfflinePlayer(getOwner()).getLastPlayed() != 0) ?
Bukkit.getServer().getOfflinePlayer(getOwner()).getLastPlayed() : Bukkit.getServer().getOfflinePlayer(getOwner()).getFirstPlayed();
String formattedDate;
try {
String dateTimeFormat = plugin.getLocalesManager().get("commands.admin.info.last-login-date-time-format");
formattedDate = new SimpleDateFormat(dateTimeFormat).format(new Date(lastPlayed));
} catch (NullPointerException | IllegalArgumentException ignored) {
formattedDate = new Date(lastPlayed).toString();
}
user.sendMessage("commands.admin.info.last-login","[date]", formattedDate);
user.sendMessage("commands.admin.info.deaths", "[number]", String.valueOf(plugin.getPlayers().getDeaths(getWorld(), getOwner())));
String resets = String.valueOf(plugin.getPlayers().getResets(getWorld(), getOwner()));
String total = plugin.getIWM().getResetLimit(getWorld()) < 0 ? "Unlimited" : String.valueOf(plugin.getIWM().getResetLimit(getWorld()));
user.sendMessage("commands.admin.info.resets-left", "[number]", resets, "[total]", total);
// Show team members
showMembers(user);
}
Vector location = getProtectionCenter().toVector();
user.sendMessage("commands.admin.info.island-protection-center", TextVariables.XYZ, Util.xyz(location));
user.sendMessage("commands.admin.info.island-center", TextVariables.XYZ, Util.xyz(getCenter().toVector()));
user.sendMessage("commands.admin.info.island-coords", "[xz1]", Util.xyz(new Vector(this.getMinX(), 0, getMinZ())), "[xz2]", Util.xyz(new Vector(this.getMaxX(), 0, getMaxZ())));
user.sendMessage("commands.admin.info.protection-range", "[range]", String.valueOf(getProtectionRange()));
user.sendMessage("commands.admin.info.max-protection-range", "[range]", String.valueOf(getMaxEverProtectionRange()));
user.sendMessage("commands.admin.info.protection-coords", "[xz1]", Util.xyz(new Vector(this.getMinProtectedX(), 0, getMinProtectedZ())), "[xz2]", Util.xyz(new Vector(this.getMaxProtectedX(), 0, getMaxProtectedZ())));
if (spawn) {
user.sendMessage("commands.admin.info.is-spawn");
}
if (!getBanned().isEmpty()) {
user.sendMessage("commands.admin.info.banned-players");
getBanned().forEach(u -> user.sendMessage("commands.admin.info.banned-format", TextVariables.NAME, plugin.getPlayers().getName(u)));
}
if (getPurgeProtected()) {
user.sendMessage("commands.admin.info.purge-protected");
}
return true;
return new IslandInfo(this).showInfo(user);
}
/**
* Shows the members of this island to this user.
* @param user the User who is requesting it
* @deprecated Use {@link IslandInfo#showMembers(User) instead}
*/
@Deprecated
public void showMembers(User user) {
BentoBox plugin = BentoBox.getInstance();
user.sendMessage("commands.admin.info.team-members-title");
members.forEach((u, i) -> {
if (owner.equals(u)) {
user.sendMessage("commands.admin.info.team-owner-format", TextVariables.NAME, plugin.getPlayers().getName(u)
, "[rank]", user.getTranslation(plugin.getRanksManager().getRank(i)));
} else if (i > RanksManager.VISITOR_RANK){
user.sendMessage("commands.admin.info.team-member-format", TextVariables.NAME, plugin.getPlayers().getName(u)
, "[rank]", user.getTranslation(plugin.getRanksManager().getRank(i)));
}
});
new IslandInfo(this).showMembers(user);
}
/**
@ -1346,7 +1293,7 @@ public class Island implements DataObject, MetaDataAble {
* @param flag - Flag to cooldown
*/
public void setCooldown(Flag flag) {
cooldowns.put(flag, flag.getCooldown() * 1000 + System.currentTimeMillis());
cooldowns.put(flag, flag.getCooldown() * 1000L + System.currentTimeMillis());
setChanged();
}

Some files were not shown because too many files have changed in this diff Show More