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 ## Documentation
* Start reading: [https://docs.bentobox.world](https://docs.bentobox.world) * 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 ## Downloads

View File

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

View File

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

View File

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

View File

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

View File

@ -163,9 +163,12 @@ public final class AddonDescription {
} }
public static class Builder { public static class Builder {
private @NonNull String main; private @NonNull
private @NonNull String name; final String main;
private @NonNull String version; private @NonNull
final String name;
private @NonNull
final String version;
private @NonNull String description = ""; private @NonNull String description = "";
private @NonNull List<String> authors = new ArrayList<>(); private @NonNull List<String> authors = new ArrayList<>();
private @NonNull List<String> dependencies = 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; package world.bentobox.bentobox.api.addons.exceptions;
import java.io.Serial;
public abstract class AddonException extends Exception { public abstract class AddonException extends Exception {
/** /**
* *
*/ */
@Serial
private static final long serialVersionUID = 4203162022348693854L; private static final long serialVersionUID = 4203162022348693854L;
protected AddonException(String errorMessage){ protected AddonException(String errorMessage){

View File

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

View File

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

View File

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

View File

@ -1,10 +1,13 @@
package world.bentobox.bentobox.api.addons.exceptions; package world.bentobox.bentobox.api.addons.exceptions;
import java.io.Serial;
public class InvalidAddonInheritException extends AddonException { public class InvalidAddonInheritException extends AddonException {
/** /**
* *
*/ */
@Serial
private static final long serialVersionUID = -5847358994397613244L; private static final long serialVersionUID = -5847358994397613244L;
public InvalidAddonInheritException(String errorMessage) { 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 addonName;
private String requestLabel; 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. * 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; import world.bentobox.bentobox.util.Util;
/** /**
* BSB composite command * BentoBox composite command. Provides an abstract implementation of a command.
* @author tastybento * @author tastybento
* @author Poslovitch * @author Poslovitch
*/ */
@ -78,12 +78,12 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/** /**
* Map of sub commands * Map of sub commands
*/ */
private Map<String, CompositeCommand> subCommands; private final Map<String, CompositeCommand> subCommands;
/** /**
* Map of aliases for 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 * 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 * The prefix to be used in this command
*/ */
@Nullable @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 * 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 * The addon creating this command, if any
*/ */
private Addon addon; private final Addon addon;
/** /**
* The top level label * The top level label
*/ */
private String topLabel; private final String topLabel;
/** /**
* Cool down tracker * 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 * Top level command
@ -144,7 +144,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
// Run setup // Run setup
setup(); setup();
if (!getSubCommand("help").isPresent() && !label.equals("help")) { if (getSubCommand("help").isEmpty() && !label.equals("help")) {
new DefaultHelpCommand(this); new DefaultHelpCommand(this);
} }
} }
@ -204,11 +204,11 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
p = p.getParent(); p = p.getParent();
index++; index++;
} }
setDescription(COMMANDS + reference.toString() + ".description"); setDescription(COMMANDS + reference + ".description");
setParametersHelp(COMMANDS + reference.toString() + ".parameters"); setParametersHelp(COMMANDS + reference + ".parameters");
setup(); setup();
// If this command does not define its own help class, then use the default help command // 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); new DefaultHelpCommand(this);
} }
} }
@ -278,7 +278,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
// get the subcommand corresponding to the arg // get the subcommand corresponding to the arg
if (subCommand.hasSubCommands()) { if (subCommand.hasSubCommands()) {
Optional<CompositeCommand> sub = subCommand.getSubCommand(arg); Optional<CompositeCommand> sub = subCommand.getSubCommand(arg);
if (!sub.isPresent()) { if (sub.isEmpty()) {
return subCommand; return subCommand;
} }
// Step down one // Step down one
@ -602,7 +602,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
return options; return options;
} }
// Add any tab completion from the subcommand // 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()) { if (command.hasSubCommands()) {
options.addAll(getSubCommandLabels(sender, command)); options.addAll(getSubCommandLabels(sender, command));
} }
@ -701,7 +701,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
* @since 1.5.0 * @since 1.5.0
*/ */
public void setCooldown(String uniqueId, String targetUUID, int timeInSeconds) { 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 * @param timeInSeconds - time in seconds to cool down
*/ */
public void setCooldown(UUID uniqueId, UUID targetUUID, int timeInSeconds) { 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 * Confirmation tracker
*/ */
private static Map<User, Confirmer> toBeConfirmed = new HashMap<>(); private static final Map<User, Confirmer> toBeConfirmed = new HashMap<>();
/** /**
* Top level command * Top level command
@ -61,9 +61,9 @@ public abstract class ConfirmableCommand extends CompositeCommand {
public void askConfirmation(User user, String message, Runnable confirmed) { public void askConfirmation(User user, String message, Runnable confirmed) {
// Check for pending confirmations // Check for pending confirmations
if (toBeConfirmed.containsKey(user)) { if (toBeConfirmed.containsKey(user)) {
if (toBeConfirmed.get(user).getTopLabel().equals(getTopLabel()) && toBeConfirmed.get(user).getLabel().equalsIgnoreCase(getLabel())) { if (toBeConfirmed.get(user).topLabel().equals(getTopLabel()) && toBeConfirmed.get(user).label().equalsIgnoreCase(getLabel())) {
toBeConfirmed.get(user).getTask().cancel(); toBeConfirmed.get(user).task().cancel();
Bukkit.getScheduler().runTask(getPlugin(), toBeConfirmed.get(user).getRunnable()); Bukkit.getScheduler().runTask(getPlugin(), toBeConfirmed.get(user).runnable());
toBeConfirmed.remove(user); toBeConfirmed.remove(user);
return; return;
} else { } else {
@ -97,51 +97,9 @@ public abstract class ConfirmableCommand extends CompositeCommand {
} }
/** /**
* Holds the data to run once the confirmation is given * Record to hold the data to run once the confirmation is given
* @author tastybento
* *
*/ */
private class Confirmer { private record Confirmer (String topLabel, String label, Runnable runnable, BukkitTask task) { }
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;
}
}
} }

View File

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

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.localization.TextVariables;
import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.IslandInfo;
import world.bentobox.bentobox.util.Util; import world.bentobox.bentobox.util.Util;
public class AdminInfoCommand extends CompositeCommand { 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 there are no args, then the player wants info on the island at this location
if (args.isEmpty()) { if (args.isEmpty()) {
if (!getIslands().getIslandAt(user.getLocation()).map(i -> i.showInfo(user)).orElse(false)) { getIslands().getIslandAt(user.getLocation()).ifPresentOrElse(i -> new IslandInfo(i).showAdminInfo(user), () ->
user.sendMessage("commands.admin.info.no-island"); user.sendMessage("commands.admin.info.no-island"));
return false;
}
return true; return true;
} }
// Get target player // Get target player
@ -49,7 +48,7 @@ public class AdminInfoCommand extends CompositeCommand {
// Show info for this player // Show info for this player
Island island = getIslands().getIsland(getWorld(), targetUUID); Island island = getIslands().getIsland(getWorld(), targetUUID);
if (island != null) { if (island != null) {
island.showInfo(user); new IslandInfo(island).showAdminInfo(user);
if (!getIslands().getQuarantinedIslandByUser(getWorld(), targetUUID).isEmpty()) { if (!getIslands().getQuarantinedIslandByUser(getWorld(), targetUUID).isEmpty()) {
user.sendMessage("commands.admin.info.islands-in-trash"); 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 { public class AdminResetFlagsCommand extends ConfirmableCommand {
private List<String> options; private final List<String> options;
public AdminResetFlagsCommand(CompositeCommand parent) { public AdminResetFlagsCommand(CompositeCommand parent) {
super(parent, "resetflags"); super(parent, "resetflags");

View File

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

View File

@ -38,8 +38,8 @@ public class AdminSettingsCommand extends CompositeCommand {
private final List<String> PROTECTION_FLAG_NAMES; private final List<String> PROTECTION_FLAG_NAMES;
private Island island; private Island island;
private final List<String> SETTING_FLAG_NAMES; private final List<String> SETTING_FLAG_NAMES;
private List<String> WORLD_SETTING_FLAG_NAMES; private final List<String> WORLD_SETTING_FLAG_NAMES;
private @NonNull Optional<Flag> flag; private @NonNull Optional<Flag> flag = Optional.empty();
private boolean activeState; private boolean activeState;
private int rank; private int rank;
@ -258,20 +258,14 @@ public class AdminSettingsCommand extends CompositeCommand {
} }
} else if (args.size() == 4) { } else if (args.size() == 4) {
// Get flag in previous argument // Get flag in previous argument
options = getPlugin().getFlagsManager().getFlag(args.get(2).toUpperCase(Locale.ENGLISH)).map(f -> { options = getPlugin().getFlagsManager().getFlag(args.get(2).toUpperCase(Locale.ENGLISH)).map(f -> switch (f.getType()) {
switch (f.getType()) { case PROTECTION -> getPlugin().getRanksManager()
case PROTECTION: .getRanks().entrySet().stream()
return getPlugin().getRanksManager() .filter(en -> en.getValue() > RanksManager.BANNED_RANK && en.getValue() <= RanksManager.OWNER_RANK)
.getRanks().entrySet().stream() .map(Entry::getKey)
.filter(en -> en.getValue() > RanksManager.BANNED_RANK && en.getValue() <= RanksManager.OWNER_RANK) .map(user::getTranslation).collect(Collectors.toList());
.map(Entry::getKey) case SETTING -> Arrays.asList(active, disabled);
.map(user::getTranslation).collect(Collectors.toList()); default -> Collections.<String>emptyList();
case SETTING:
return Arrays.asList(active, disabled);
default:
return Collections.<String>emptyList();
}
}).orElse(Collections.emptyList()); }).orElse(Collections.emptyList());
} }
return Optional.of(Util.tabLimit(options, lastArg)); 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) { public boolean execute(User user, String label, List<String> args) {
if (NumberUtils.isDigits(args.get(1))) { if (NumberUtils.isDigits(args.get(1))) {
try { try {
Integer n = Integer.valueOf(args.get(1)); int n = Integer.parseInt(args.get(1));
if (n < 1 || n > islands.size()) { if (n < 1 || n > islands.size()) {
user.sendMessage("commands.admin.switchto.out-of-range", TextVariables.NUMBER, String.valueOf(islands.size()), TextVariables.LABEL, getTopLabel()); user.sendMessage("commands.admin.switchto.out-of-range", TextVariables.NUMBER, String.valueOf(islands.size()), TextVariables.LABEL, getTopLabel());
return false; return false;

View File

@ -87,6 +87,10 @@ public class AdminTeleportCommand extends CompositeCommand {
world = getPlugin().getIWM().getEndWorld(getWorld()); world = getPlugin().getIWM().getEndWorld(getWorld());
} }
Location warpSpot = getSpot(world); 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 // Otherwise, ask the admin to go to a safe spot
String failureMessage = user.getTranslation("commands.admin.tp.manual", "[location]", warpSpot.getBlockX() + " " + warpSpot.getBlockY() + " " 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.localization.TextVariables;
import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.IslandInfo;
public class AdminTrashCommand extends CompositeCommand { public class AdminTrashCommand extends CompositeCommand {
@ -63,7 +64,7 @@ public class AdminTrashCommand extends CompositeCommand {
user.sendMessage("commands.admin.trash.title"); user.sendMessage("commands.admin.trash.title");
for (int i = 0; i < islands.size(); i++) { for (int i = 0; i < islands.size(); i++) {
user.sendMessage("commands.admin.trash.count", TextVariables.NUMBER, String.valueOf(i+1)); 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-switch", TextVariables.LABEL, getTopLabel());
user.sendMessage("commands.admin.trash.use-emptytrash", 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 @Override
public boolean execute(User user, String label, List<String> args) { public boolean execute(User user, String label, List<String> args) {
user.sendMessage("commands.bentobox.version.addon-syntax", TextVariables.NAME, getAddon().getDescription().getName(), 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; return true;
} }

View File

@ -61,7 +61,7 @@ public abstract class DefaultAdminCommand extends CompositeCommand {
new AdminTeamDisbandCommand(this); new AdminTeamDisbandCommand(this);
new AdminTeamSetownerCommand(this); new AdminTeamSetownerCommand(this);
new AdminTeamFixCommand(this); new AdminTeamFixCommand(this);
// Schems // Blueprints
new AdminBlueprintCommand(this); new AdminBlueprintCommand(this);
// Register/unregister islands // Register/unregister islands
new AdminRegisterCommand(this); new AdminRegisterCommand(this);
@ -96,7 +96,7 @@ public abstract class DefaultAdminCommand extends CompositeCommand {
/** /**
* Defines what will be executed when this command is run. * 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 @Override
public boolean execute(User user, String label, List<String> args) { 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(); islands.clear();
this.user = user; this.user = user;
try { try {
Integer days = Integer.parseInt(args.get(0)); int days = Integer.parseInt(args.get(0));
if (days < 1) { if (days < 1) {
user.sendMessage("commands.admin.purge.days-one-or-more"); user.sendMessage("commands.admin.purge.days-one-or-more");
return false; return false;

View File

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

View File

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

View File

@ -32,6 +32,12 @@ public class IslandGoCommand extends DelayedTeleportCommand {
@Override @Override
public boolean canExecute(User user, String label, List<String> args) { 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 // Check if the island is reserved
Island island = getIslands().getIsland(getWorld(), user.getUniqueId()); Island island = getIslands().getIsland(getWorld(), user.getUniqueId());
if (island == null) { 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.localization.TextVariables;
import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.IslandInfo;
import world.bentobox.bentobox.util.Util; 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 there are no args, then the player wants info on the island at this location
if (args.isEmpty()) { 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"); user.sendMessage("commands.admin.info.no-island");
return false; return false;
} }
@ -56,7 +57,7 @@ public class IslandInfoCommand extends CompositeCommand {
return false; return false;
} }
// Show info for this player // Show info for this player
island.showInfo(user); new IslandInfo(island).showInfo(user);
return true; return true;
} }

View File

@ -75,10 +75,9 @@ public class Invite {
if (obj == null) { if (obj == null) {
return false; return false;
} }
if (!(obj instanceof Invite)) { if (!(obj instanceof Invite other)) {
return false; return false;
} }
Invite other = (Invite) obj;
return Objects.equals(invitee, other.invitee) && Objects.equals(inviter, other.inviter) && type == other.type; 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. * Invited list. Key is the invited party, value is the invite.
* @since 1.8.0 * @since 1.8.0
*/ */
private Map<UUID, Invite> inviteMap; private final Map<UUID, Invite> inviteMap;
public IslandTeamCommand(CompositeCommand parent) { public IslandTeamCommand(CompositeCommand parent) {
super(parent, "team"); super(parent, "team");

View File

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

View File

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

View File

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

View File

@ -11,7 +11,7 @@ import world.bentobox.bentobox.api.user.User;
public class IslandTeamInviteRejectCommand extends CompositeCommand { public class IslandTeamInviteRejectCommand extends CompositeCommand {
private IslandTeamCommand itc; private final IslandTeamCommand itc;
public IslandTeamInviteRejectCommand(IslandTeamCommand islandTeamCommand) { public IslandTeamInviteRejectCommand(IslandTeamCommand islandTeamCommand) {
super(islandTeamCommand, "reject"); 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> { public class Config<T> {
private AbstractDatabaseHandler<T> handler; private final AbstractDatabaseHandler<T> handler;
private Logger logger; private final Logger logger;
private Addon addon; private Addon addon;
public Config(BentoBox plugin, Class<T> type) { public Config(BentoBox plugin, Class<T> type) {

View File

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

View File

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

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() { private IslandBaseEvent getDeprecatedEvent() {
switch (reason) { return switch (reason) {
case JOIN: case JOIN -> new TeamJoinEvent(island, player, admin, location);
return new TeamJoinEvent(island, player, admin, location); case JOINED -> new TeamJoinedEvent(island, player, admin, location);
case JOINED: case INVITE -> new TeamInviteEvent(island, player, admin, location);
return new TeamJoinedEvent(island, player, admin, location); case LEAVE -> new TeamLeaveEvent(island, player, admin, location);
case INVITE: case REJECT -> new TeamRejectEvent(island, player, admin, location);
return new TeamInviteEvent(island, player, admin, location); case KICK -> new TeamKickEvent(island, player, admin, location);
case LEAVE: case SETOWNER -> new TeamSetownerEvent(island, player, admin, location);
return new TeamLeaveEvent(island, player, admin, location); case INFO -> new TeamInfoEvent(island, player, admin, location);
case REJECT: case DELETE -> new TeamDeleteEvent(island, player, admin, location);
return new TeamRejectEvent(island, player, admin, location); case UNINVITE -> new TeamUninviteEvent(island, player, admin, location);
case KICK: default -> new TeamGeneralEvent(island, player, admin, location);
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);
}
} }
private IslandBaseEvent getEvent() { private IslandBaseEvent getEvent() {
switch (reason) { return switch (reason) {
case JOIN: case JOIN -> new world.bentobox.bentobox.api.events.team.TeamJoinEvent(island, player, admin, location);
return 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 JOINED: case INVITE -> new world.bentobox.bentobox.api.events.team.TeamInviteEvent(island, player, admin, location);
return new world.bentobox.bentobox.api.events.team.TeamJoinedEvent(island, player, admin, location); case LEAVE -> new world.bentobox.bentobox.api.events.team.TeamLeaveEvent(island, player, admin, location);
case INVITE: case REJECT -> new world.bentobox.bentobox.api.events.team.TeamRejectEvent(island, player, admin, location);
return new world.bentobox.bentobox.api.events.team.TeamInviteEvent(island, player, admin, location); case KICK -> new world.bentobox.bentobox.api.events.team.TeamKickEvent(island, player, admin, location);
case LEAVE: case SETOWNER -> new world.bentobox.bentobox.api.events.team.TeamSetownerEvent(island, player, admin, location);
return new world.bentobox.bentobox.api.events.team.TeamLeaveEvent(island, player, admin, location); case INFO -> new world.bentobox.bentobox.api.events.team.TeamInfoEvent(island, player, admin, location);
case REJECT: case DELETE -> new world.bentobox.bentobox.api.events.team.TeamDeleteEvent(island, player, admin, location);
return new world.bentobox.bentobox.api.events.team.TeamRejectEvent(island, player, admin, location); case UNINVITE -> new world.bentobox.bentobox.api.events.team.TeamUninviteEvent(island, player, admin, location);
case KICK: default -> new world.bentobox.bentobox.api.events.team.TeamGeneralEvent(island, player, admin, location);
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);
}
} }
/** /**

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.api.user.User;
import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.RanksManager; import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.bentobox.util.ItemParser;
public class Flag implements Comparable<Flag> { public class Flag implements Comparable<Flag> {
@ -51,7 +53,8 @@ public class Flag implements Comparable<Flag> {
*/ */
WORLD_SETTING(Material.GRASS_BLOCK); WORLD_SETTING(Material.GRASS_BLOCK);
private @NonNull Material icon; private @NonNull
final Material icon;
Type(@NonNull Material icon) { Type(@NonNull Material icon) {
this.icon = icon; this.icon = icon;
@ -91,14 +94,11 @@ public class Flag implements Comparable<Flag> {
* @return next ranking mode * @return next ranking mode
*/ */
public Mode getNext() { public Mode getNext() {
switch(this) { return switch (this) {
case ADVANCED: case ADVANCED -> EXPERT;
return EXPERT; case BASIC -> ADVANCED;
case BASIC: default -> BASIC;
return ADVANCED; };
default:
return BASIC;
}
} }
/** /**
@ -107,14 +107,11 @@ public class Flag implements Comparable<Flag> {
* @return true if ranked greater * @return true if ranked greater
*/ */
public boolean isGreaterThan(Mode rank) { public boolean isGreaterThan(Mode rank) {
switch(this) { return switch (this) {
case EXPERT: case EXPERT -> rank.equals(BASIC) || rank.equals(ADVANCED);
return rank.equals(BASIC) || rank.equals(ADVANCED); case ADVANCED -> rank.equals(BASIC);
case ADVANCED: default -> false;
return rank.equals(BASIC); };
default:
return false;
}
} }
} }
@ -207,12 +204,12 @@ public class Flag implements Comparable<Flag> {
// Subflag support // Subflag support
if (hasSubflags()) { if (hasSubflags()) {
subflags.stream() subflags.stream()
.filter(subflag -> subflag.getType().equals(Type.WORLD_SETTING) || subflag.getType().equals(Type.PROTECTION)) .filter(subflag -> subflag.getType().equals(Type.WORLD_SETTING) || subflag.getType().equals(Type.PROTECTION))
.forEach(subflag -> BentoBox.getInstance() .forEach(subflag -> BentoBox.getInstance()
.getIWM() .getIWM()
.getWorldSettings(world) .getWorldSettings(world)
.getWorldFlags() .getWorldFlags()
.put(subflag.getID(), setting)); .put(subflag.getID(), setting));
} }
// Save config file // 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. * 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. * Does not affect subflags.
* @param defaultSetting - true means it is allowed. false means it is not allowed * @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) { if (obj == null) {
return false; return false;
} }
if (!(obj instanceof Flag)) { if (!(obj instanceof Flag other)) {
return false; return false;
} }
Flag other = (Flag) obj;
if (id == null) { if (id == null) {
if (other.id != null) { if (other.id != null) {
return false; return false;
@ -322,6 +318,13 @@ public class Flag implements Comparable<Flag> {
return PROTECTION_FLAGS + this.id + ".name"; 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 * @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 // Start the flag conversion
PanelItemBuilder pib = new PanelItemBuilder() 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()))) .name(user.getTranslation("protection.panel.flag-item.name-layout", TextVariables.NAME, user.getTranslation(getNameReference())))
.clickHandler(clickHandler) .clickHandler(clickHandler)
.invisible(invisible); .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()))); pib.description(user.getTranslation("protection.panel.flag-item.menu-layout", TextVariables.DESCRIPTION, user.getTranslation(getDescriptionReference())));
return pib.build(); return pib.build();
} }
switch(getType()) { return switch (getType()) {
case PROTECTION: case PROTECTION -> createProtectionFlag(plugin, user, island, pib).build();
return createProtectionFlag(plugin, user, island, pib).build(); case SETTING -> createSettingFlag(user, island, pib).build();
case SETTING: case WORLD_SETTING -> createWorldSettingFlag(user, pib).build();
return createSettingFlag(user, island, pib).build(); };
case WORLD_SETTING:
return createWorldSettingFlag(user, pib).build();
default:
return pib.build();
}
} }
private PanelItemBuilder createWorldSettingFlag(User user, PanelItemBuilder pib) { private PanelItemBuilder createWorldSettingFlag(User user, PanelItemBuilder pib) {
@ -484,8 +482,8 @@ public class Flag implements Comparable<Flag> {
*/ */
public static class Builder { public static class Builder {
// Mandatory fields // Mandatory fields
private String id; private final String id;
private Material icon; private final Material icon;
// Listener // Listener
private Listener listener; private Listener listener;
@ -514,7 +512,7 @@ public class Flag implements Comparable<Flag> {
private Mode mode = Mode.EXPERT; private Mode mode = Mode.EXPERT;
// Subflags // Subflags
private Set<Flag> subflags; private final Set<Flag> subflags;
/** /**
* Builder for making flags * 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. * 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 * @param flags all Flags that are subflags
* @return Builder - flag builder * @return Builder - flag builder
* @since 1.17.0 * @since 1.17.1
*/ */
public Builder subflags(Flag... flags) { public Builder subflags(Flag... flags) {
this.subflags.addAll(Arrays.asList(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 no clickHandler has been set, then apply default ones
if (clickHandler == null) { if (clickHandler == null) {
switch (type) { switch (type) {
case SETTING: case SETTING -> clickHandler = new IslandToggleClick(id);
clickHandler = new IslandToggleClick(id); case WORLD_SETTING -> clickHandler = new WorldToggleClick(id);
break; default -> clickHandler = new CycleClick(id);
case WORLD_SETTING:
clickHandler = new WorldToggleClick(id);
break;
case PROTECTION:
// Default option
default:
clickHandler = new CycleClick(id);
break;
} }
} }

View File

@ -4,6 +4,7 @@ import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable; import org.bukkit.event.Cancellable;
import org.bukkit.event.Event; 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.metadata.MetaDataValue;
import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.lists.Flags;
import world.bentobox.bentobox.managers.IslandWorldManager; import world.bentobox.bentobox.managers.IslandWorldManager;
import world.bentobox.bentobox.managers.IslandsManager; import world.bentobox.bentobox.managers.IslandsManager;
import world.bentobox.bentobox.util.Util; import world.bentobox.bentobox.util.Util;
@ -253,4 +255,22 @@ public abstract class FlagListener implements Listener {
protected IslandWorldManager getIWM() { protected IslandWorldManager getIWM() {
return plugin.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 { public class IslandToggleClick implements ClickHandler {
private BentoBox plugin = BentoBox.getInstance(); private final BentoBox plugin = BentoBox.getInstance();
private String id; private final String id;
/** /**
* @param id - the flag ID that this click listener is associated with * @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 static final String UNKNOWN = "unknown";
private Locale locale; private final Locale locale;
private YamlConfiguration config; private final YamlConfiguration config;
private ItemStack banner; private final ItemStack banner;
private List<String> authors; private final List<String> authors;
/** /**
* List of available prefixes in this locale. * List of available prefixes in this locale.
* @since 1.12.0 * @since 1.12.0
*/ */
private Set<String> prefixes; private final Set<String> prefixes;
public BentoBoxLocale(Locale locale, YamlConfiguration config) { public BentoBoxLocale(Locale locale, YamlConfiguration config) {
this.locale = locale; 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 { public static class Builder {
private long timestamp; private long timestamp;
private String type; private final String type;
private Map<String, String> data; private Map<String, String> data;
public Builder(@NonNull String type) { public Builder(@NonNull String type) {

View File

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

View File

@ -29,7 +29,7 @@ public class MetaDataValue {
@Expose @Expose
private Boolean booleanValue; private Boolean booleanValue;
@Expose @Expose
private @NonNull String stringValue; private String stringValue;
/** /**
* Initialize this meta data value * Initialize this meta data value
@ -85,6 +85,6 @@ public class MetaDataValue {
@NonNull @NonNull
public String asString() { 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 // Create panel
switch (type) { switch (type) {
case INVENTORY: case INVENTORY -> inventory = Bukkit.createInventory(null, fixSize(size), name);
inventory = Bukkit.createInventory(null, fixSize(size), name); case HOPPER -> inventory = Bukkit.createInventory(null, InventoryType.HOPPER, name);
break; case DROPPER -> inventory = Bukkit.createInventory(null, InventoryType.DROPPER, name);
case HOPPER:
inventory = Bukkit.createInventory(null, InventoryType.HOPPER, name);
break;
case DROPPER:
inventory = Bukkit.createInventory(null, InventoryType.DROPPER, name);
break;
} }
// Fill the inventory and return // Fill the inventory and return

View File

@ -29,7 +29,7 @@ public class PanelItem {
private String name; private String name;
private boolean glow; private boolean glow;
private ItemMeta meta; private ItemMeta meta;
private String playerHeadName; private final String playerHeadName;
private boolean invisible; private boolean invisible;
public PanelItem(PanelItemBuilder builtItem) { 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 String PROTECTION_PANEL = "protection.panel.";
private static final long ITEMS_PER_PAGE = 36; private static final long ITEMS_PER_PAGE = 36;
private final TabbedPanelBuilder tpb; private final TabbedPanelBuilder tpb;
private @NonNull BentoBox plugin = BentoBox.getInstance(); private @NonNull
final BentoBox plugin = BentoBox.getInstance();
private int activeTab; private int activeTab;
private int activePage; private int activePage;
private boolean closed; private boolean closed;
@ -146,7 +147,7 @@ public class TabbedPanel extends Panel implements PanelListener {
} }
} }
// Add any subsidiary icons // Add any subsidiary icons
tab.getTabIcons().forEach(items::put); items.putAll(tab.getTabIcons());
} }
private void setupFooter(TreeMap<Integer, PanelItem> items) { 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.SortedMap;
import java.util.TreeMap; import java.util.TreeMap;
import org.bukkit.ChatColor;
import org.bukkit.World; import org.bukkit.World;
import world.bentobox.bentobox.api.panels.Panel; import world.bentobox.bentobox.api.panels.Panel;
import world.bentobox.bentobox.api.panels.PanelItem; import world.bentobox.bentobox.api.panels.PanelItem;
import world.bentobox.bentobox.api.panels.PanelListener; import world.bentobox.bentobox.api.panels.PanelListener;
import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.util.Util;
/** /**
* Builds panels * Builds panels
@ -26,7 +27,7 @@ public class PanelBuilder {
private World world; private World world;
public PanelBuilder name(String name) { public PanelBuilder name(String name) {
this.name = ChatColor.translateAlternateColorCodes('&', name); this.name = Util.translateColorCodes(name);
return this; return this;
} }

View File

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

View File

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

View File

@ -3,7 +3,7 @@ package world.bentobox.bentobox.api.placeholders.placeholderapi;
import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.BentoBox;
public class BentoBoxPlaceholderExpansion extends BasicPlaceholderExpansion { public class BentoBoxPlaceholderExpansion extends BasicPlaceholderExpansion {
private BentoBox plugin; private final BentoBox plugin;
public BentoBoxPlaceholderExpansion(BentoBox plugin) { public BentoBoxPlaceholderExpansion(BentoBox plugin) {
super(); 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 * Utilities class that helps to avoid spamming the User with potential repeated messages
* @author Poslovitch * @author Poslovitch, tastybento
*/ */
public class Notifier { public class Notifier {
@ -18,17 +18,19 @@ public class Notifier {
*/ */
private static final int NOTIFICATION_DELAY = 4; private static final int NOTIFICATION_DELAY = 4;
private record Notification(String message, long time) {}
private final LoadingCache<User, Notification> notificationCache = CacheBuilder.newBuilder() private final LoadingCache<User, Notification> notificationCache = CacheBuilder.newBuilder()
.expireAfterAccess(NOTIFICATION_DELAY, TimeUnit.SECONDS) .expireAfterAccess(NOTIFICATION_DELAY, TimeUnit.SECONDS)
.maximumSize(500) .maximumSize(500)
.build( .build(
new CacheLoader<User, Notification>() { new CacheLoader<>() {
@Override @Override
public Notification load(User user) { public Notification load(User user) {
return new Notification(null, 0); return new Notification(null, 0);
} }
} }
); );
/** /**
* Sends message to a user only if the message hasn't been sent recently * 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); Notification lastNotification = notificationCache.get(user);
long now = System.currentTimeMillis(); 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)); notificationCache.put(user, new Notification(message, now));
user.sendRawMessage(message); user.sendRawMessage(message);
return true; 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 { 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 * Clears all users from the user list
@ -136,7 +136,7 @@ public class User implements MetaDataAble {
private static BentoBox plugin = BentoBox.getInstance(); private static BentoBox plugin = BentoBox.getInstance();
@Nullable @Nullable
private Player player; private final Player player;
private OfflinePlayer offlinePlayer; private OfflinePlayer offlinePlayer;
private final UUID playerUUID; private final UUID playerUUID;
@Nullable @Nullable
@ -410,7 +410,7 @@ public class User implements MetaDataAble {
translation = plugin.getPlaceholdersManager().replacePlaceholders(player, translation); 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) { if (obj == null) {
return false; return false;
} }
if (!(obj instanceof User)) { if (!(obj instanceof User other)) {
return false; return false;
} }
User other = (User) obj;
if (playerUUID == null) { if (playerUUID == null) {
return other.playerUUID == null; return other.playerUUID == null;
} else return playerUUID.equals(other.playerUUID); } 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 boolean copying;
private int index; private int index;
private int lastPercentage; private int lastPercentage;
private Map<Vector, List<BlueprintEntity>> bpEntities = new LinkedHashMap<>(); private final Map<Vector, List<BlueprintEntity>> bpEntities = new LinkedHashMap<>();
private Map<Vector, BlueprintBlock> bpAttachable = new LinkedHashMap<>(); private final Map<Vector, BlueprintBlock> bpAttachable = new LinkedHashMap<>();
private Map<Vector, BlueprintBlock> bpBlocks = new LinkedHashMap<>(); private final Map<Vector, BlueprintBlock> bpBlocks = new LinkedHashMap<>();
private BentoBox plugin = BentoBox.getInstance(); private final BentoBox plugin = BentoBox.getInstance();
/** /**
* Create a clipboard for blueprint * Create a clipboard for blueprint
@ -190,48 +190,7 @@ public class BlueprintClipboard {
Vector pos = new Vector(x, y, z); Vector pos = new Vector(x, y, z);
// Set entities // Set entities
List<BlueprintEntity> bpEnts = new ArrayList<>(); List<BlueprintEntity> bpEnts = setEntities(entities);
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);
}
// Store // Store
if (!bpEnts.isEmpty()) { if (!bpEnts.isEmpty()) {
bpEntities.put(pos, bpEnts); bpEntities.put(pos, bpEnts);
@ -242,22 +201,31 @@ public class BlueprintClipboard {
return true; 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 // Block state
BlockState blockState = block.getState(); BlockState blockState = block.getState();
BlueprintBlock b = new BlueprintBlock(block.getBlockData().getAsString()); BlueprintBlock b = new BlueprintBlock(block.getBlockData().getAsString());
// Biome // Biome
b.setBiome(block.getBiome()); b.setBiome(block.getBiome());
// Signs // Signs
if (blockState instanceof Sign) { if (blockState instanceof Sign sign) {
Sign sign = (Sign)blockState;
b.setSignLines(Arrays.asList(sign.getLines())); b.setSignLines(Arrays.asList(sign.getLines()));
b.setGlowingText(sign.isGlowingText());
} }
// Set block data // Set block data
if (blockState.getData() instanceof Attachable) { if (blockState.getData() instanceof Attachable) {
// Placeholder for attachment // Placeholder for attachment
bpBlocks.put(pos, new BlueprintBlock("minecraft:air")); bpBlocks.put(pos, new BlueprintBlock("minecraft:air"));
bpAttachable.put(pos, b); bpAttachable.put(pos, b);
return true; return null;
} }
if (block.getType().equals(Material.BEDROCK)) { if (block.getType().equals(Material.BEDROCK)) {
@ -272,9 +240,8 @@ public class BlueprintClipboard {
} }
// Chests // Chests
if (blockState instanceof InventoryHolder) { if (blockState instanceof InventoryHolder ih) {
b.setInventory(new HashMap<>()); b.setInventory(new HashMap<>());
InventoryHolder ih = (InventoryHolder)blockState;
for (int i = 0; i < ih.getInventory().getSize(); i++) { for (int i = 0; i < ih.getInventory().getSize(); i++) {
ItemStack item = ih.getInventory().getItem(i); ItemStack item = ih.getInventory().getItem(i);
if (item != null) { if (item != null) {
@ -283,17 +250,8 @@ public class BlueprintClipboard {
} }
} }
if (blockState instanceof CreatureSpawner) { if (blockState instanceof CreatureSpawner spawner) {
CreatureSpawner spawner = (CreatureSpawner)blockState; b.setCreatureSpawner(getSpawner(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());
b.setCreatureSpawner(cs);
} }
// Banners // Banners
@ -301,8 +259,62 @@ public class BlueprintClipboard {
b.setBannerPatterns(((Banner) blockState).getPatterns()); b.setBannerPatterns(((Banner) blockState).getPatterns());
} }
this.bpBlocks.put(pos, b); return b;
return true; }
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 java.util.concurrent.CompletableFuture;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.World; 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 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) // The minimum block position (x,y,z)
private Location pos1; private Location pos1;
// The maximum block position (x,y,z) // The maximum block position (x,y,z)
@ -86,19 +85,19 @@ public class BlueprintPaster {
* The Blueprint to paste. * The Blueprint to paste.
*/ */
@NonNull @NonNull
private Blueprint blueprint; private final Blueprint blueprint;
/** /**
* The Location to paste to. * The Location to paste to.
*/ */
@NonNull @NonNull
private Location location; private final Location location;
/** /**
* Island related to this paste, may be null. * Island related to this paste, may be null.
*/ */
@Nullable @Nullable
private Island island; private final Island island;
/** /**
* Paste a clipboard to a location and run task * Paste a clipboard to a location and run task
@ -291,37 +290,39 @@ public class BlueprintPaster {
// Get the block state // Get the block state
BlockState bs = block.getState(); BlockState bs = block.getState();
// Signs // Signs
if (bs instanceof org.bukkit.block.Sign) { if (bs instanceof org.bukkit.block.Sign sign) {
writeSign(block, bpBlock.getSignLines()); writeSign(block, bpBlock.getSignLines(), bpBlock.isGlowingText());
} }
// Chests, in general // Chests, in general
if (bs instanceof InventoryHolder) { if (bs instanceof InventoryHolder) {
Inventory ih = ((InventoryHolder)bs).getInventory(); 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); bpBlock.getInventory().forEach(ih::setItem);
} }
// Mob spawners // Mob spawners
if (bs instanceof CreatureSpawner) { if (bs instanceof CreatureSpawner spawner) {
CreatureSpawner spawner = ((CreatureSpawner) bs); setSpawner(spawner, bpBlock.getCreatureSpawner());
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);
} }
// Banners // Banners
if (bs instanceof Banner && bpBlock.getBannerPatterns() != null) { if (bs instanceof Banner banner && bpBlock.getBannerPatterns() != null) {
Banner banner = (Banner) bs;
bpBlock.getBannerPatterns().removeIf(Objects::isNull); bpBlock.getBannerPatterns().removeIf(Objects::isNull);
banner.setPatterns(bpBlock.getBannerPatterns()); banner.setPatterns(bpBlock.getBannerPatterns());
banner.update(true, false); 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 * Sets any entity that is in this location
* @param location - 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; BlockFace bf;
if (block.getType().name().contains("WALL_SIGN")) { if (block.getType().name().contains("WALL_SIGN")) {
WallSign wallSign = (WallSign)block.getBlockData(); WallSign wallSign = (WallSign)block.getBlockData();
@ -416,7 +417,7 @@ public class BlueprintPaster {
// Get the addon that is operating in this world // 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(""); String addonName = plugin.getIWM().getAddon(island.getWorld()).map(addon -> addon.getDescription().getName().toLowerCase(Locale.ENGLISH)).orElse("");
for (int i = 0; i < 4; i++) { 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))); addonName + ".sign.line" + i,"").replace(TextVariables.NAME, name)));
} }
} else { } else {
@ -425,6 +426,7 @@ public class BlueprintPaster {
s.setLine(i, lines.get(i)); s.setLine(i, lines.get(i));
} }
} }
s.setGlowingText(glow);
// Update the sign // Update the sign
s.update(); s.update();
} }

View File

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

View File

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

View File

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

View File

@ -34,6 +34,8 @@ public class BlueprintBlock {
*/ */
@Expose @Expose
private List<Pattern> bannerPatterns; private List<Pattern> bannerPatterns;
@Expose
private boolean glowingText;
public BlueprintBlock(String blockData) { public BlueprintBlock(String blockData) {
this.blockData = blockData; this.blockData = blockData;
@ -124,4 +126,20 @@ public class BlueprintBlock {
public void setBiome(Biome biome) { public void setBiome(Biome biome) {
this.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(); ((Ageable)e).setBaby();
} }
} }
if (e instanceof AbstractHorse) { if (e instanceof AbstractHorse horse) {
AbstractHorse horse = (AbstractHorse)e;
if (domestication != null) horse.setDomestication(domestication); if (domestication != null) horse.setDomestication(domestication);
if (inventory != null) { if (inventory != null) {
inventory.forEach(horse.getInventory()::setItem); 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 @Override
public ClipboardReader getReader(InputStream inputStream) throws IOException { public ClipboardReader getReader(InputStream inputStream) {
return new BlueprintClipboardReader(inputStream); return new BlueprintClipboardReader(inputStream);
} }
@Override @Override
public ClipboardWriter getWriter(OutputStream outputStream) throws IOException { public ClipboardWriter getWriter(OutputStream outputStream) {
return new BlueprintClipboardWriter(outputStream); return new BlueprintClipboardWriter(outputStream);
} }

View File

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

View File

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

View File

@ -18,7 +18,7 @@ public class BlueprintSchematicConverter {
private File blueprintFile; private File blueprintFile;
public BlueprintSchematicConverter(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 !"); BentoBox.getInstance().logError("WorldEdit must be installed to use that class !");
return; return;
} }

View File

@ -7,6 +7,7 @@ import org.bukkit.Bukkit;
import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.commands.ConfirmableCommand; import world.bentobox.bentobox.api.commands.ConfirmableCommand;
import world.bentobox.bentobox.api.events.BentoBoxReadyEvent; 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.api.user.User;
import world.bentobox.bentobox.commands.reload.BentoBoxReloadLocalesCommand; import world.bentobox.bentobox.commands.reload.BentoBoxReloadLocalesCommand;
import world.bentobox.bentobox.listeners.PanelListenerManager; import world.bentobox.bentobox.listeners.PanelListenerManager;
@ -45,6 +46,8 @@ public class BentoBoxReloadCommand extends ConfirmableCommand {
// Close all open panels // Close all open panels
PanelListenerManager.closeAllPanels(); PanelListenerManager.closeAllPanels();
// Clear all template panels.
TemplateReader.clearPanels();
// Reload settings // Reload settings
getPlugin().loadSettings(); 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> { public class Database<T> {
private AbstractDatabaseHandler<T> handler; private final AbstractDatabaseHandler<T> handler;
private Logger logger; private final Logger logger;
private static DatabaseSetup databaseSetup = DatabaseSetup.getDatabase(); 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> { public abstract class AbstractJSONDatabaseHandler<T> extends AbstractDatabaseHandler<T> {
private Gson gson; private final Gson gson;
/** /**
* Constructor * Constructor

View File

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

View File

@ -23,11 +23,11 @@ public class JSONDatabaseConnector implements DatabaseConnector {
@NonNull @NonNull
public String getUniqueId(String tableName) { public String getUniqueId(String tableName) {
UUID uuid = UUID.randomUUID(); 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; int limit = 0;
while (file.exists() && limit++ < MAX_LOOPS) { while (file.exists() && limit++ < MAX_LOOPS) {
uuid = UUID.randomUUID(); 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(); return uuid.toString();
} }

View File

@ -15,7 +15,7 @@ import world.bentobox.bentobox.api.flags.Flag;
public class FlagTypeAdapter extends TypeAdapter<Flag> { public class FlagTypeAdapter extends TypeAdapter<Flag> {
private BentoBox plugin; private final BentoBox plugin;
public FlagTypeAdapter(BentoBox plugin) { public FlagTypeAdapter(BentoBox plugin) {
this.plugin = 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 // Flags can end up null if an addon that created one is removed or if a flag name was changed
if (f == null) { if (f == null) {
// Create a temporary flag with a unique key. It will be immediately deleted after loading // 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; 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 static final String MONGO_ID = "_id";
private MongoCollection<Document> collection; 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 * 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); completableFuture.complete(false);
return completableFuture; return completableFuture;
} }
if (!(instance instanceof DataObject)) { if (!(instance instanceof DataObject dataObj)) {
plugin.logError("This class is not a DataObject: " + instance.getClass().getName()); plugin.logError("This class is not a DataObject: " + instance.getClass().getName());
completableFuture.complete(false); completableFuture.complete(false);
return completableFuture; return completableFuture;
} }
DataObject dataObj = (DataObject)instance;
try { try {
Gson gson = getGson(); Gson gson = getGson();
String toStore = gson.toJson(instance); String toStore = gson.toJson(instance);

View File

@ -1,8 +1,6 @@
package world.bentobox.bentobox.database.objects; package world.bentobox.bentobox.database.objects;
import java.io.IOException; import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@ -23,7 +21,6 @@ import org.bukkit.World;
import org.bukkit.World.Environment; import org.bukkit.World.Environment;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.util.BoundingBox; import org.bukkit.util.BoundingBox;
import org.bukkit.util.Vector;
import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.jetbrains.annotations.NotNull; 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.configuration.WorldSettings;
import world.bentobox.bentobox.api.events.island.IslandEvent; import world.bentobox.bentobox.api.events.island.IslandEvent;
import world.bentobox.bentobox.api.flags.Flag; 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.logs.LogEntry;
import world.bentobox.bentobox.api.metadata.MetaDataAble; import world.bentobox.bentobox.api.metadata.MetaDataAble;
import world.bentobox.bentobox.api.metadata.MetaDataValue; 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.lists.Flags;
import world.bentobox.bentobox.managers.IslandWorldManager; import world.bentobox.bentobox.managers.IslandWorldManager;
import world.bentobox.bentobox.managers.RanksManager; import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.bentobox.util.IslandInfo;
import world.bentobox.bentobox.util.Pair; import world.bentobox.bentobox.util.Pair;
import world.bentobox.bentobox.util.Util; import world.bentobox.bentobox.util.Util;
@ -1080,71 +1077,21 @@ public class Island implements DataObject, MetaDataAble {
* Shows info of this island to this user. * Shows info of this island to this user.
* @param user the User who is requesting it * @param user the User who is requesting it
* @return always true * @return always true
* @deprecated Use {@link IslandInfo#showInfo(User) instead}
*/ */
@Deprecated
public boolean showInfo(User user) { public boolean showInfo(User user) {
BentoBox plugin = BentoBox.getInstance(); return new IslandInfo(this).showInfo(user);
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;
} }
/** /**
* Shows the members of this island to this user. * Shows the members of this island to this user.
* @param user the User who is requesting it * @param user the User who is requesting it
* @deprecated Use {@link IslandInfo#showMembers(User) instead}
*/ */
@Deprecated
public void showMembers(User user) { public void showMembers(User user) {
BentoBox plugin = BentoBox.getInstance(); new IslandInfo(this).showMembers(user);
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)));
}
});
} }
/** /**
@ -1346,7 +1293,7 @@ public class Island implements DataObject, MetaDataAble {
* @param flag - Flag to cooldown * @param flag - Flag to cooldown
*/ */
public void setCooldown(Flag flag) { public void setCooldown(Flag flag) {
cooldowns.put(flag, flag.getCooldown() * 1000 + System.currentTimeMillis()); cooldowns.put(flag, flag.getCooldown() * 1000L + System.currentTimeMillis());
setChanged(); setChanged();
} }

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