Merge pull request #2204 from BentoBoxWorld/develop

Release 2.0.0
This commit is contained in:
tastybento 2024-01-30 22:38:04 -08:00 committed by GitHub
commit 196e90bf4e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
241 changed files with 24391 additions and 10225 deletions

97
pom.xml
View File

@ -73,10 +73,10 @@
<postgresql.version>42.2.18</postgresql.version>
<hikaricp.version>5.0.1</hikaricp.version>
<!-- More visible way to change dependency versions -->
<spigot.version>1.20.1-R0.1-SNAPSHOT</spigot.version>
<spigot.version>1.20.4-R0.1-SNAPSHOT</spigot.version>
<!-- Might differ from the last Spigot release for short periods
of time -->
<paper.version>1.20.1-R0.1-SNAPSHOT</paper.version>
<paper.version>1.20.4-R0.1-SNAPSHOT</paper.version>
<bstats.version>3.0.0</bstats.version>
<vault.version>1.7.1</vault.version>
<placeholderapi.version>2.10.9</placeholderapi.version>
@ -88,7 +88,7 @@
<!-- Do not change unless you want different name for local builds. -->
<build.number>-LOCAL</build.number>
<!-- This allows to change between versions. -->
<build.version>1.24.1</build.version>
<build.version>2.0.0</build.version>
<sonar.organization>bentobox-world</sonar.organization>
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
<server.jars>${project.basedir}/lib</server.jars>
@ -192,7 +192,32 @@
</repositories>
<dependencies>
<!-- Spigot API -->
<!-- Mockito (Unit testing) This goes at the top to ensure the dependencies are accurate. -->
<!-- This is required for PowerMockito to work and must be placed before it -->
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.30.2-GA</version>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.11.1</version>
<scope>test</scope>
</dependency>
<!-- Spigot API -->
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
@ -206,38 +231,12 @@
<version>${paper.version}</version>
<scope>provided</scope>
</dependency>
<!-- AuthLib. Used for Head Getter. -->
<dependency>
<groupId>com.mojang</groupId>
<artifactId>authlib</artifactId>
<version>3.16.29</version>
<scope>provided</scope>
</dependency>
<!-- Metrics -->
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId>
<version>${bstats.version}</version>
</dependency>
<!-- Mockito (Unit testing) -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.11.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<!-- Database -->
<dependency>
<groupId>org.mongodb</groupId>
@ -321,6 +320,20 @@
<version>${spigot.version}</version>
<scope>provided</scope>
</dependency>
<!-- Slimefun -->
<dependency>
<groupId>com.github.Slimefun</groupId>
<artifactId>Slimefun4</artifactId>
<version>RC-36</version>
<scope>provided</scope>
</dependency>
<!-- ItemsAdder -->
<dependency>
<groupId>com.github.LoneDev6</groupId>
<artifactId>api-itemsadder</artifactId>
<version>3.6.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
@ -378,29 +391,23 @@
--add-opens java.base/java.math=ALL-UNNAMED
--add-opens java.base/java.io=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens
java.base/java.util.stream=ALL-UNNAMED
--add-opens java.base/java.util.stream=ALL-UNNAMED
--add-opens java.base/java.text=ALL-UNNAMED
--add-opens
java.base/java.util.regex=ALL-UNNAMED
--add-opens
java.base/java.nio.channels.spi=ALL-UNNAMED
--add-opens java.base/java.util.regex=ALL-UNNAMED
--add-opens java.base/java.nio.channels.spi=ALL-UNNAMED
--add-opens java.base/sun.nio.ch=ALL-UNNAMED
--add-opens java.base/java.net=ALL-UNNAMED
--add-opens
java.base/java.util.concurrent=ALL-UNNAMED
--add-opens java.base/java.util.concurrent=ALL-UNNAMED
--add-opens java.base/sun.nio.fs=ALL-UNNAMED
--add-opens java.base/sun.nio.cs=ALL-UNNAMED
--add-opens java.base/java.nio.file=ALL-UNNAMED
--add-opens
java.base/java.nio.charset=ALL-UNNAMED
--add-opens
java.base/java.lang.reflect=ALL-UNNAMED
--add-opens
java.logging/java.util.logging=ALL-UNNAMED
--add-opens java.base/java.nio.charset=ALL-UNNAMED
--add-opens java.base/java.lang.reflect=ALL-UNNAMED
--add-opens java.logging/java.util.logging=ALL-UNNAMED
--add-opens java.base/java.lang.ref=ALL-UNNAMED
--add-opens java.base/java.util.jar=ALL-UNNAMED
--add-opens java.base/java.util.zip=ALL-UNNAMED
--add-opens=java.base/java.security=ALL-UNNAMED
</argLine>
</configuration>
</plugin>
@ -514,6 +521,8 @@
<!-- This is required to prevent Jacoco from adding
synthetic fields to a JavaBean class (causes errors in testing) -->
<exclude>**/*Names*</exclude>
<!-- Prevents the Material is too large to mock error -->
<exclude>org/bukkit/Material*</exclude>
</excludes>
</configuration>
<executions>

View File

@ -1,5 +1,7 @@
package world.bentobox.bentobox;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Optional;
@ -22,8 +24,10 @@ import world.bentobox.bentobox.api.user.Notifier;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.commands.BentoBoxCommand;
import world.bentobox.bentobox.database.DatabaseSetup;
import world.bentobox.bentobox.hooks.ItemsAdderHook;
import world.bentobox.bentobox.hooks.MultiverseCoreHook;
import world.bentobox.bentobox.hooks.MyWorldsHook;
import world.bentobox.bentobox.hooks.SlimefunHook;
import world.bentobox.bentobox.hooks.VaultHook;
import world.bentobox.bentobox.hooks.placeholders.PlaceholderAPIHook;
import world.bentobox.bentobox.listeners.BannedCommands;
@ -31,6 +35,7 @@ import world.bentobox.bentobox.listeners.BlockEndDragon;
import world.bentobox.bentobox.listeners.DeathListener;
import world.bentobox.bentobox.listeners.JoinLeaveListener;
import world.bentobox.bentobox.listeners.PanelListenerManager;
import world.bentobox.bentobox.listeners.PrimaryIslandListener;
import world.bentobox.bentobox.listeners.StandardSpawnProtectionListener;
import world.bentobox.bentobox.listeners.teleports.EntityTeleportListener;
import world.bentobox.bentobox.listeners.teleports.PlayerTeleportListener;
@ -68,7 +73,6 @@ public class BentoBox extends JavaPlugin implements Listener {
private AddonsManager addonsManager;
private FlagsManager flagsManager;
private IslandWorldManager islandWorldManager;
private RanksManager ranksManager;
private BlueprintsManager blueprintsManager;
private HooksManager hooksManager;
private PlaceholdersManager placeholdersManager;
@ -136,7 +140,6 @@ public class BentoBox extends JavaPlugin implements Listener {
return;
}
islandsManager = new IslandsManager(this);
ranksManager = new RanksManager();
// Start head getter
headGetter = new HeadGetter(this);
@ -230,6 +233,12 @@ public class BentoBox extends JavaPlugin implements Listener {
hooksManager.registerHook(new MyWorldsHook());
islandWorldManager.registerWorldsToMultiverse(true);
// Register Slimefun
hooksManager.registerHook(new SlimefunHook());
// Register ItemsAdder
hooksManager.registerHook(new ItemsAdderHook(this));
// TODO: re-enable after implementation
//hooksManager.registerHook(new DynmapHook());
// TODO: re-enable after rework
@ -251,6 +260,8 @@ public class BentoBox extends JavaPlugin implements Listener {
// Tell all addons that everything is loaded
isLoaded = true;
this.addonsManager.allLoaded();
// Run ready commands
settings.getReadyCommands().forEach(cmd -> Bukkit.getServer().dispatchCommand(getServer().getConsoleSender(), cmd));
// Fire plugin ready event - this should go last after everything else
Bukkit.getPluginManager().callEvent(new BentoBoxReadyEvent());
instance.log("All blueprints loaded.");
@ -306,6 +317,8 @@ public class BentoBox extends JavaPlugin implements Listener {
// Island Delete Manager
islandDeletionManager = new IslandDeletionManager(this);
manager.registerEvents(islandDeletionManager, this);
// Primary Island Listener
manager.registerEvents(new PrimaryIslandListener(this), this);
}
@Override
@ -328,10 +341,14 @@ public class BentoBox extends JavaPlugin implements Listener {
@EventHandler
public void onServerStop(ServerCommandEvent e) {
/* This is no longer needed as with https://github.com/Multiverse/Multiverse-Core/releases/tag/4.3.12 (or maybe earlier) the issue
* is fixed where the generator was not remembered across reboots.
*/
/*
if (islandWorldManager != null && (e.getCommand().equalsIgnoreCase("stop") || e.getCommand().equalsIgnoreCase("restart"))) {
// Unregister any MV worlds if () {
islandWorldManager.registerWorldsToMultiverse(false);
}
//islandWorldManager.registerWorldsToMultiverse(false);
}*/
}
/**
@ -410,9 +427,11 @@ public class BentoBox extends JavaPlugin implements Listener {
/**
* @return the ranksManager
* @deprecated Just use {@code RanksManager.getInstance()}
*/
@Deprecated(since = "2.0.0")
public RanksManager getRanksManager() {
return ranksManager;
return RanksManager.getInstance();
}
/**
@ -446,6 +465,17 @@ public class BentoBox extends JavaPlugin implements Listener {
getPluginLoader().disablePlugin(this);
return false;
}
log("Saving default panels...");
if (!Files.exists(Path.of(this.getDataFolder().getPath(), "panels", "island_creation_panel.yml"))) {
log("Saving default island_creation_panel...");
this.saveResource("panels/island_creation_panel.yml", false);
}
if (!Files.exists(Path.of(this.getDataFolder().getPath(), "panels", "language_panel.yml"))) {
log("Saving default language_panel...");
this.saveResource("panels/language_panel.yml", false);
}
return true;
}

View File

@ -1,50 +1,35 @@
package world.bentobox.bentobox;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.bukkit.Material;
import com.google.common.collect.ImmutableList;
import world.bentobox.bentobox.api.configuration.ConfigComment;
import world.bentobox.bentobox.api.configuration.ConfigEntry;
import world.bentobox.bentobox.api.configuration.ConfigObject;
import world.bentobox.bentobox.api.configuration.StoreAt;
import world.bentobox.bentobox.database.DatabaseSetup.DatabaseType;
/**
* All the plugin settings are here
*
* @author tastybento
*/
@StoreAt(filename="config.yml") // Explicitly call out what name this should have.
@StoreAt(filename = "config.yml") // Explicitly call out what name this should have.
@ConfigComment("BentoBox v[version] configuration file.")
@ConfigComment("")
@ConfigComment("This configuration file contains settings that mainly apply to or manage the following elements:")
@ConfigComment(" * Data storage")
@ConfigComment(" * Gamemodes (commands, ...)")
@ConfigComment(" * Internet connectivity (web-based content-enriched features, ...)")
@ConfigComment("")
@ConfigComment("Note that this configuration file is dynamic:")
@ConfigComment(" * It gets updated with the newest settings and comments after BentoBox loaded its settings from it.")
@ConfigComment(" * Upon updating BentoBox, new settings will be automatically added into this configuration file.")
@ConfigComment(" * Said settings are distinguishable by a dedicated comment, which looks like this:")
@ConfigComment(" Added since X.Y.Z.")
@ConfigComment(" * They are provided with default values that should not cause issues on live production servers.")
@ConfigComment(" * You can however edit this file while the server is online.")
@ConfigComment(" You will therefore need to run the following command in order to take the changes into account: /bentobox reload.")
@ConfigComment("")
@ConfigComment("Here are a few pieces of advice before you get started:")
@ConfigComment(" * You should check out our Wiki, which may provide you useful tips or insights about BentoBox's features.")
@ConfigComment(" Link: https://github.com/BentoBoxWorld/BentoBox/wiki")
@ConfigComment(" * You should edit this configuration file while the server is offline.")
@ConfigComment(" * Moreover, whenever you update BentoBox, you should do so on a test server first.")
@ConfigComment(" This will allow you to configure the new settings beforehand instead of applying them inadvertently on a live production server.")
public class Settings implements ConfigObject {
/* GENERAL */
/* GENERAL */
@ConfigComment("Default language for new players.")
@ConfigComment("This is the filename in the locale folder without .yml.")
@ConfigComment("If this does not exist, the default en-US will be used.")
@ -56,10 +41,17 @@ public class Settings implements ConfigObject {
@ConfigEntry(path = "general.use-economy")
private boolean useEconomy = true;
/* COMMANDS */
@ConfigComment("Console commands to run when BentoBox has loaded all worlds and addons.")
@ConfigComment("Commands are run as the console.")
@ConfigComment("e.g. set aliases for worlds in Multiverse here, or anything you need to")
@ConfigComment("run after the plugin is fully loaded.")
@ConfigEntry(path = "general.ready-commands", since = "1.24.2")
private List<String> readyCommands = new ArrayList<>();
// Database
@ConfigComment("JSON, MYSQL, MARIADB, MONGODB, SQLITE, POSTGRESQL and YAML(deprecated).")
@ConfigComment("JSON, MYSQL, MARIADB, MONGODB, SQLITE, and POSTGRESQL.")
@ConfigComment("Transition database options are:")
@ConfigComment(" YAML2JSON, YAML2MARIADB, YAML2MYSQL, YAML2MONGODB, YAML2SQLITE")
@ConfigComment(" JSON2MARIADB, JSON2MYSQL, JSON2MONGODB, JSON2SQLITE, JSON2POSTGRESQL")
@ConfigComment(" MYSQL2JSON, MARIADB2JSON, MONGODB2JSON, SQLITE2JSON, POSTGRESQL2JSON")
@ConfigComment("If you need others, please make a feature request.")
@ -70,7 +62,7 @@ public class Settings implements ConfigObject {
@ConfigComment(" SQLite versions 3.28 or later")
@ConfigComment(" PostgreSQL versions 9.4 or later")
@ConfigComment("Transition options enable migration from one database type to another. Use /bbox migrate.")
@ConfigComment("YAML and JSON are file-based databases.")
@ConfigComment("JSON is a file-based database.")
@ConfigComment("MYSQL might not work with all implementations: if available, use a dedicated database type (e.g. MARIADB).")
@ConfigComment("BentoBox uses HikariCP for connecting with SQL databases.")
@ConfigComment("If you use MONGODB, you must also run the BSBMongo plugin (not addon).")
@ -197,6 +189,12 @@ public class Settings implements ConfigObject {
/*
* Island
*/
// Number of islands
@ConfigComment("The default number of concurrent islands a player may have.")
@ConfigComment("This may be overridden by individual game mode config settings.")
@ConfigEntry(path = "island.concurrent-islands")
private int islandNumber = 1;
// Cooldowns
@ConfigComment("How long a player must wait until they can rejoin a team island after being kicked in minutes.")
@ConfigComment("This slows the effectiveness of players repeating challenges")
@ -287,25 +285,6 @@ public class Settings implements ConfigObject {
@ConfigEntry(path = "island.delete-speed", since = "1.7.0")
private int deleteSpeed = 1;
// Automated ownership transfer
@ConfigComment("Toggles the automated ownership transfer.")
@ConfigComment("It automatically transfers the ownership of an island to one of its members in case the current owner is inactive.")
@ConfigComment("More precisely, it transfers the ownership of the island to the player who's active, whose rank is the highest")
@ConfigComment("and who's been part of the island the longest time.")
@ConfigComment("Setting this to 'false' will disable the feature.")
@ConfigEntry(path = "island.automated-ownership-transfer.enable", hidden = true)
private boolean enableAutoOwnershipTransfer = false;
@ConfigComment("Time in days since the island owner's last disconnection before they are considered inactive.")
@ConfigEntry(path = "island.automated-ownership-transfer.inactivity-threshold", hidden = true)
private int autoOwnershipTransferInactivityThreshold = 30;
@ConfigComment("Ranks are being considered when transferring the island ownership to one of its member.")
@ConfigComment("Ignoring ranks will result in the island ownership being transferred to the player who's active and")
@ConfigComment("who's been member of the island the longest time.")
@ConfigEntry(path = "island.automated-ownership-transfer.ignore-ranks", hidden = true)
private boolean autoOwnershipTransferIgnoreRanks = false;
// Island deletion related settings
@ConfigComment("Toggles whether islands, when players are resetting them, should be kept in the world or deleted.")
@ConfigComment("* If set to 'true', whenever a player resets his island, his previous island will become unowned and won't be deleted from the world.")
@ -402,6 +381,7 @@ public class Settings implements ConfigObject {
/**
* This method returns the useSSL value.
*
* @return the value of useSSL.
* @since 1.12.0
*/
@ -411,6 +391,7 @@ public class Settings implements ConfigObject {
/**
* This method sets the useSSL value.
*
* @param useSSL the useSSL new value.
* @since 1.12.0
*/
@ -630,30 +611,6 @@ public class Settings implements ConfigObject {
this.deleteSpeed = deleteSpeed;
}
public boolean isEnableAutoOwnershipTransfer() {
return enableAutoOwnershipTransfer;
}
public void setEnableAutoOwnershipTransfer(boolean enableAutoOwnershipTransfer) {
this.enableAutoOwnershipTransfer = enableAutoOwnershipTransfer;
}
public int getAutoOwnershipTransferInactivityThreshold() {
return autoOwnershipTransferInactivityThreshold;
}
public void setAutoOwnershipTransferInactivityThreshold(int autoOwnershipTransferInactivityThreshold) {
this.autoOwnershipTransferInactivityThreshold = autoOwnershipTransferInactivityThreshold;
}
public boolean isAutoOwnershipTransferIgnoreRanks() {
return autoOwnershipTransferIgnoreRanks;
}
public void setAutoOwnershipTransferIgnoreRanks(boolean autoOwnershipTransferIgnoreRanks) {
this.autoOwnershipTransferIgnoreRanks = autoOwnershipTransferIgnoreRanks;
}
public boolean isLogCleanSuperFlatChunks() {
return logCleanSuperFlatChunks;
}
@ -725,7 +682,8 @@ public class Settings implements ConfigObject {
* @return the clearRadius
*/
public int getClearRadius() {
if (clearRadius < 0) clearRadius = 0;
if (clearRadius < 0)
clearRadius = 0;
return clearRadius;
}
@ -733,7 +691,8 @@ public class Settings implements ConfigObject {
* @param clearRadius the clearRadius to set. Cannot be negative.
*/
public void setClearRadius(int clearRadius) {
if (clearRadius < 0) clearRadius = 0;
if (clearRadius < 0)
clearRadius = 0;
this.clearRadius = clearRadius;
}
@ -757,7 +716,8 @@ public class Settings implements ConfigObject {
* @return the databasePrefix
*/
public String getDatabasePrefix() {
if (databasePrefix == null) databasePrefix = "";
if (databasePrefix == null)
databasePrefix = "";
return databasePrefix.isEmpty() ? "" : databasePrefix.replaceAll("[^a-zA-Z0-9]", "_");
}
@ -770,7 +730,9 @@ public class Settings implements ConfigObject {
/**
* Returns whether islands, when reset, should be kept or deleted.
* @return {@code true} if islands, when reset, should be kept; {@code false} otherwise.
*
* @return {@code true} if islands, when reset, should be kept; {@code false}
* otherwise.
* @since 1.13.0
*/
public boolean isKeepPreviousIslandOnReset() {
@ -779,7 +741,9 @@ public class Settings implements ConfigObject {
/**
* Sets whether islands, when reset, should be kept or deleted.
* @param keepPreviousIslandOnReset {@code true} if islands, when reset, should be kept; {@code false} otherwise.
*
* @param keepPreviousIslandOnReset {@code true} if islands, when reset, should
* be kept; {@code false} otherwise.
* @since 1.13.0
*/
public void setKeepPreviousIslandOnReset(boolean keepPreviousIslandOnReset) {
@ -787,10 +751,13 @@ public class Settings implements ConfigObject {
}
/**
* Returns a MongoDB client connection URI to override default connection options.
* Returns a MongoDB client connection URI to override default connection
* options.
*
* @return mongodb client connection.
* @see <a href="https://docs.mongodb.com/manual/reference/connection-string/">MongoDB Documentation</a>
* @see <a href=
* "https://docs.mongodb.com/manual/reference/connection-string/">MongoDB
* Documentation</a>
* @since 1.14.0
*/
public String getMongodbConnectionUri() {
@ -799,6 +766,7 @@ public class Settings implements ConfigObject {
/**
* Set the MongoDB client connection URI.
*
* @param mongodbConnectionUri connection URI.
* @since 1.14.0
*/
@ -807,8 +775,11 @@ public class Settings implements ConfigObject {
}
/**
* Returns the Material of the item to preferably use when one needs to fill gaps in Panels.
* @return the Material of the item to preferably use when one needs to fill gaps in Panels.
* Returns the Material of the item to preferably use when one needs to fill
* gaps in Panels.
*
* @return the Material of the item to preferably use when one needs to fill
* gaps in Panels.
* @since 1.14.0
*/
public Material getPanelFillerMaterial() {
@ -816,106 +787,96 @@ public class Settings implements ConfigObject {
}
/**
* Sets the Material of the item to preferably use when one needs to fill gaps in Panels.
* @param panelFillerMaterial the Material of the item to preferably use when one needs to fill gaps in Panels.
* Sets the Material of the item to preferably use when one needs to fill gaps
* in Panels.
*
* @param panelFillerMaterial the Material of the item to preferably use when
* one needs to fill gaps in Panels.
* @since 1.14.0
*/
public void setPanelFillerMaterial(Material panelFillerMaterial) {
this.panelFillerMaterial = panelFillerMaterial;
}
/**
* Method Settings#getPlayerHeadCacheTime returns the playerHeadCacheTime of this object.
* Method Settings#getPlayerHeadCacheTime returns the playerHeadCacheTime of
* this object.
*
* @return the playerHeadCacheTime (type long) of this object.
* @since 1.14.1
*/
public long getPlayerHeadCacheTime()
{
public long getPlayerHeadCacheTime() {
return playerHeadCacheTime;
}
/**
* Method Settings#setPlayerHeadCacheTime sets new value for the playerHeadCacheTime of this object.
* Method Settings#setPlayerHeadCacheTime sets new value for the
* playerHeadCacheTime of this object.
*
* @param playerHeadCacheTime new value for this object.
* @since 1.14.1
*/
public void setPlayerHeadCacheTime(long playerHeadCacheTime)
{
public void setPlayerHeadCacheTime(long playerHeadCacheTime) {
this.playerHeadCacheTime = playerHeadCacheTime;
}
/**
* Is use cache server boolean.
*
* @return the boolean
* @since 1.16.0
*/
public boolean isUseCacheServer()
{
public boolean isUseCacheServer() {
return useCacheServer;
}
/**
* Sets use cache server.
*
* @param useCacheServer the use cache server
* @since 1.16.0
*/
public void setUseCacheServer(boolean useCacheServer)
{
public void setUseCacheServer(boolean useCacheServer) {
this.useCacheServer = useCacheServer;
}
/**
* Gets heads per call.
*
* @return the heads per call
* @since 1.16.0
*/
public int getHeadsPerCall()
{
public int getHeadsPerCall() {
return headsPerCall;
}
/**
* Sets heads per call.
*
* @param headsPerCall the heads per call
* @since 1.16.0
*/
public void setHeadsPerCall(int headsPerCall)
{
public void setHeadsPerCall(int headsPerCall) {
this.headsPerCall = headsPerCall;
}
/**
* Gets ticks between calls.
*
* @return the ticks between calls
* @since 1.16.0
*/
public long getTicksBetweenCalls()
{
public long getTicksBetweenCalls() {
return ticksBetweenCalls;
}
/**
* Sets ticks between calls.
*
* @param ticksBetweenCalls the ticks between calls
* @since 1.16.0
*/
public void setTicksBetweenCalls(long ticksBetweenCalls)
{
public void setTicksBetweenCalls(long ticksBetweenCalls) {
this.ticksBetweenCalls = ticksBetweenCalls;
}
@ -933,7 +894,6 @@ public class Settings implements ConfigObject {
this.minPortalSearchRadius = minPortalSearchRadius;
}
/**
* Gets safe spot search vertical range.
*
@ -943,7 +903,6 @@ public class Settings implements ConfigObject {
return safeSpotSearchVerticalRange;
}
/**
* Sets safe spot search vertical range.
*
@ -953,7 +912,6 @@ public class Settings implements ConfigObject {
this.safeSpotSearchVerticalRange = safeSpotSearchVerticalRange;
}
/**
* Is slow deletion boolean.
*
@ -963,7 +921,6 @@ public class Settings implements ConfigObject {
return slowDeletion;
}
/**
* Sets slow deletion.
*
@ -973,69 +930,88 @@ public class Settings implements ConfigObject {
this.slowDeletion = slowDeletion;
}
/**
* Gets maximum pool size.
*
* @return the maximum pool size
*/
public int getMaximumPoolSize()
{
public int getMaximumPoolSize() {
return maximumPoolSize;
}
/**
* Gets safe spot search range.
*
* @return the safe spot search range
*/
public int getSafeSpotSearchRange()
{
public int getSafeSpotSearchRange() {
return safeSpotSearchRange;
}
/**
* Sets maximum pool size.
*
* @param maximumPoolSize the maximum pool size
*/
public void setMaximumPoolSize(int maximumPoolSize)
{
public void setMaximumPoolSize(int maximumPoolSize) {
this.maximumPoolSize = maximumPoolSize;
}
/**
* Gets custom pool properties.
*
* @return the custom pool properties
*/
public Map<String, String> getCustomPoolProperties()
{
public Map<String, String> getCustomPoolProperties() {
return customPoolProperties;
}
/**
* Sets custom pool properties.
*
* @param customPoolProperties the custom pool properties
*/
public void setCustomPoolProperties(Map<String, String> customPoolProperties)
{
public void setCustomPoolProperties(Map<String, String> customPoolProperties) {
this.customPoolProperties = customPoolProperties;
}
/**
* Sets safe spot search range.
*
* @param safeSpotSearchRange the safe spot search range
*/
public void setSafeSpotSearchRange(int safeSpotSearchRange)
{
public void setSafeSpotSearchRange(int safeSpotSearchRange) {
this.safeSpotSearchRange = safeSpotSearchRange;
}
/**
* @return an immutable list of readyCommands
*/
public List<String> getReadyCommands() {
return ImmutableList.copyOf(Objects.requireNonNullElse(readyCommands, Collections.emptyList()));
}
/**
* @param readyCommands the readyCommands to set
*/
public void setReadyCommands(List<String> readyCommands) {
this.readyCommands = readyCommands;
}
/**
* @return the islandNumber
* @since 2.0.0
*/
public int getIslandNumber() {
return islandNumber;
}
/**
* @param islandNumber the islandNumber to set
* @since 2.0.0
*/
public void setIslandNumber(int islandNumber) {
this.islandNumber = islandNumber;
}
}

View File

@ -12,6 +12,7 @@ import java.util.Optional;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import org.bukkit.Bukkit;
import org.bukkit.Server;
@ -273,7 +274,9 @@ public abstract class Addon {
"The embedded resource '" + jarResource + "' cannot be found in " + jar.getName());
}
// There are two options, use the path of the resource or not
File outFile = new File(destinationFolder, jarResource);
File outFile = new File(destinationFolder,
jarResource.replaceAll("/", Matcher.quoteReplacement(File.separator)));
if (noPath) {
outFile = new File(destinationFolder, outFile.getName());
}

View File

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

View File

@ -26,6 +26,7 @@ import world.bentobox.bentobox.api.addons.Addon;
import world.bentobox.bentobox.api.events.command.CommandEvent;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.IslandWorldManager;
import world.bentobox.bentobox.managers.IslandsManager;
import world.bentobox.bentobox.managers.PlayersManager;
@ -34,6 +35,7 @@ import world.bentobox.bentobox.util.Util;
/**
* BentoBox composite command. Provides an abstract implementation of a command.
*
* @author tastybento
* @author Poslovitch
*/
@ -50,11 +52,11 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* True if the command is only for the console
*
* @since 1.24.0
*/
private boolean onlyConsole = false;
/**
* True if command is a configurable rank
*/
@ -62,22 +64,26 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Make default command rank as owner rank.
*
* @since 1.20.0
*/
private int defaultCommandRank = RanksManager.OWNER_RANK;
/**
* True if command is hidden from help and tab complete
*
* @since 1.13.0
*/
private boolean hidden = false;
/**
* The parameters string for this command. It is the commands followed by a locale reference.
* The parameters string for this command. It is the commands followed by a
* locale reference.
*/
private String parameters = "";
/**
* The parent command to this one. If this is a top-level command it will be empty.
* The parent command to this one. If this is a top-level command it will be
* empty.
*/
protected final CompositeCommand parent;
/**
@ -109,8 +115,9 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
private final String permissionPrefix;
/**
* The world that this command operates in. This is an overworld and will cover any associated nether or end
* If the world value does not exist, then the command is general across worlds
* The world that this command operates in. This is an overworld and will cover
* any associated nether or end If the world value does not exist, then the
* command is general across worlds
*/
private World world;
@ -131,8 +138,9 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Top level command
* @param addon - addon creating the command
* @param label - string for this command
*
* @param addon - addon creating the command
* @param label - string for this command
* @param aliases - aliases
*/
protected CompositeCommand(Addon addon, String label, String... aliases) {
@ -164,17 +172,19 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* This is the top-level command constructor for commands that have no parent.
* @param label - string for this command
*
* @param label - string for this command
* @param aliases - aliases for this command
*/
protected CompositeCommand(String label, String... aliases) {
this((Addon)null, label, aliases);
this((Addon) null, label, aliases);
}
/**
* Sub-command constructor
* @param parent - the parent composite command
* @param label - string label for this subcommand
*
* @param parent - the parent composite command
* @param label - string label for this subcommand
* @param aliases - aliases for this subcommand
*/
protected CompositeCommand(CompositeCommand parent, String label, String... aliases) {
@ -182,12 +192,14 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
}
/**
* Command to register a command from an addon under a parent command (that could be from another addon)
* @param addon - this command's addon
* @param parent - parent command
* Command to register a command from an addon under a parent command (that
* could be from another addon)
*
* @param addon - this command's addon
* @param parent - parent command
* @param aliases - aliases for this command
*/
protected CompositeCommand(Addon addon, CompositeCommand parent, String label, String... aliases ) {
protected CompositeCommand(Addon addon, CompositeCommand parent, String label, String... aliases) {
super(label, "", "", Arrays.asList(aliases));
this.topLabel = parent.getTopLabel();
this.plugin = BentoBox.getInstance();
@ -220,7 +232,8 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
setDescription(COMMANDS + reference + ".description");
setParametersHelp(COMMANDS + reference + ".parameters");
setup();
// If this command does not define its own help class, then use the default help command
// If this command does not define its own help class, then use the default help
// command
if (getSubCommand("help").isEmpty() && !label.equals("help")) {
new DefaultHelpCommand(this);
}
@ -235,29 +248,25 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
// Get the User instance for this sender
User user = User.getInstance(sender);
// Fire an event to see if this command should be cancelled
CommandEvent event = CommandEvent.builder()
.setCommand(this)
.setLabel(label)
.setSender(sender)
.setArgs(args)
CommandEvent event = CommandEvent.builder().setCommand(this).setLabel(label).setSender(sender).setArgs(args)
.build();
if (event.isCancelled()) {
return false;
}
// Get command
CompositeCommand cmd = getCommandFromArgs(args);
String cmdLabel = (cmd.subCommandLevel > 0) ? args[cmd.subCommandLevel-1] : label;
String cmdLabel = (cmd.subCommandLevel > 0) ? args[cmd.subCommandLevel - 1] : label;
List<String> cmdArgs = Arrays.asList(args).subList(cmd.subCommandLevel, args.length);
return cmd.call(user, cmdLabel, cmdArgs);
}
/**
* Calls this composite command.
* Does not traverse the tree of subcommands in args.
* Event is not fired and it cannot be cancelled.
* @param user - user calling this command
* Calls this composite command. Does not traverse the tree of subcommands in
* args. Event is not fired and it cannot be cancelled.
*
* @param user - user calling this command
* @param cmdLabel - label used
* @param cmdArgs - list of args
* @param cmdArgs - list of args
* @return {@code true} if successful, {@code false} if not.
* @since 1.5.3
*/
@ -273,8 +282,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
return false;
}
if (!this.runPermissionCheck(user))
{
if (!this.runPermissionCheck(user)) {
// Error message is displayed by permission check.
return false;
}
@ -284,22 +292,18 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
return canExecute(user, cmdLabel, cmdArgs) && execute(user, cmdLabel, cmdArgs);
}
/**
* This method checks and returns if user has access to the called command.
* It also recursively checks if user has access to the all parent commands.
* This method checks and returns if user has access to the called command. It
* also recursively checks if user has access to the all parent commands.
*
* @param user User who permission must be checked.
* @return {@code true} is user can execute given command, {@code false} otherwise.
* @return {@code true} is user can execute given command, {@code false}
* otherwise.
*/
private boolean runPermissionCheck(User user)
{
private boolean runPermissionCheck(User user) {
// Check perms, but only if this isn't the console
if (user.isPlayer() &&
!user.isOp() &&
this.getPermission() != null &&
!this.getPermission().isEmpty() &&
!user.hasPermission(this.getPermission()))
{
if (user.isPlayer() && !user.isOp() && this.getPermission() != null && !this.getPermission().isEmpty()
&& !user.hasPermission(this.getPermission())) {
user.sendMessage("general.errors.no-permission", TextVariables.PERMISSION, this.getPermission());
return false;
}
@ -308,9 +312,9 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
return this.getParent() == null || this.getParent().runPermissionCheck(user);
}
/**
* Get the current composite command based on the arguments
*
* @param args - arguments
* @return the current composite command based on the arguments
*/
@ -339,6 +343,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Convenience method to get the island manager
*
* @return IslandsManager
*/
protected IslandsManager getIslands() {
@ -347,6 +352,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Convenience method to get the island manager
*
* @return IslandsManager
*/
protected IslandsManager getIslandsManager() {
@ -354,8 +360,8 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
}
/**
* @return this command's sub-level. Top level is 0.
* Every time a command registers with a parent, their level will be set.
* @return this command's sub-level. Top level is 0. Every time a command
* registers with a parent, their level will be set.
*/
protected int getLevel() {
return subCommandLevel;
@ -369,13 +375,19 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
}
/**
* Convenience method to obtain team members
* Convenience method to obtain team members of the active island for user. Note
* that the user may have more than one island in this world.
*
* @param world - world to check
* @param user - the User
* @return set of UUIDs of all team members
* @param user - the User
* @return set of UUIDs of all team members, or empty set if there is no island
*/
protected Set<UUID> getMembers(World world, User user) {
return plugin.getIslands().getMembers(world, user.getUniqueId());
Island island = plugin.getIslands().getIsland(world, user);
if (island == null) {
return Set.of();
}
return island.getMemberSet();
}
public String getParameters() {
@ -396,6 +408,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Convenience method to get the player manager
*
* @return PlayersManager
*/
protected PlayersManager getPlayers() {
@ -409,11 +422,13 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Get the island worlds manager
*
* @return island worlds manager
*/
public IslandWorldManager getIWM() {
return plugin.getIWM();
}
/**
* @return Settings object
*/
@ -423,6 +438,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Returns the CompositeCommand object referring to this command label
*
* @param label - command label or alias
* @return CompositeCommand or null if none found
*/
@ -446,9 +462,12 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
}
/**
* Returns a map of sub commands for this command.
* As it needs more calculations to handle the Help subcommand, it is preferable to use {@link #getSubCommands()} when no such distinction is needed.
* @param ignoreHelp Whether the Help subcommand should not be returned in the map or not.
* Returns a map of sub commands for this command. As it needs more calculations
* to handle the Help subcommand, it is preferable to use
* {@link #getSubCommands()} when no such distinction is needed.
*
* @param ignoreHelp Whether the Help subcommand should not be returned in the
* map or not.
* @return Map of sub commands for this command
* @see #hasSubCommands(boolean)
*/
@ -461,17 +480,6 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
return getSubCommands();
}
/**
* Convenience method to obtain the user's island owner
* @param world world to check
* @param user the User
* @return UUID of player's island owner or null if user has no island
*/
@Nullable
protected UUID getOwner(@NonNull World world, @NonNull User user) {
return plugin.getIslands().getOwner(world, user.getUniqueId());
}
@Override
public @NonNull String getUsage() {
return "/" + usage;
@ -479,6 +487,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Check if this command has a specific sub command.
*
* @param subCommand - sub command
* @return true if this command has this sub command
*/
@ -488,6 +497,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Check if this command has any sub commands.
*
* @return true if this command has subcommands
*/
protected boolean hasSubCommands() {
@ -495,9 +505,12 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
}
/**
* Check if this command has any sub commands.
* As it needs more calculations to handle the Help subcommand, it is preferable to use {@link #hasSubCommands()} when no such distinction is needed.
* @param ignoreHelp Whether the Help subcommand should not be taken into account or not.
* Check if this command has any sub commands. As it needs more calculations to
* handle the Help subcommand, it is preferable to use {@link #hasSubCommands()}
* when no such distinction is needed.
*
* @param ignoreHelp Whether the Help subcommand should not be taken into
* account or not.
* @return true if this command has subcommands
* @see #getSubCommands(boolean)
*/
@ -507,8 +520,9 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Convenience method to check if a user has a team.
*
* @param world - the world to check
* @param user - the User
* @param user - the User
* @return true if player is in a team
*/
protected boolean inTeam(World world, User user) {
@ -517,6 +531,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Check if this command is only for players.
*
* @return true or false
*/
public boolean isOnlyPlayer() {
@ -525,6 +540,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Check if this command is only for consoles.
*
* @return true or false
*/
public boolean isOnlyConsole() {
@ -532,11 +548,14 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
}
/**
* Sets whether this command should only be run by players.
* If this is set to {@code true}, this command will only be runnable by objects implementing {@link Player}.
* <br/><br/>
* The default value provided when instantiating this CompositeCommand is {@code false}.
* Therefore, this method should only be used in case you want to explicitly edit the value.
* Sets whether this command should only be run by players. If this is set to
* {@code true}, this command will only be runnable by objects implementing
* {@link Player}. <br/>
* <br/>
* The default value provided when instantiating this CompositeCommand is
* {@code false}. Therefore, this method should only be used in case you want to
* explicitly edit the value.
*
* @param onlyPlayer {@code true} if this command should only be run by players.
*/
public void setOnlyPlayer(boolean onlyPlayer) {
@ -544,11 +563,14 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
}
/**
* Sets whether this command should only be run in the console.
* This is for commands that dump a lot of data or are for debugging.
* The default value provided when instantiating this CompositeCommand is {@code false}.
* Therefore, this method should only be used in case you want to explicitly edit the value.
* @param onlyConsole {@code true} if this command should only be run in the console.
* Sets whether this command should only be run in the console. This is for
* commands that dump a lot of data or are for debugging. The default value
* provided when instantiating this CompositeCommand is {@code false}.
* Therefore, this method should only be used in case you want to explicitly
* edit the value.
*
* @param onlyConsole {@code true} if this command should only be run in the
* console.
* @since 1.24.0
*/
public void setOnlyConsole(boolean onlyConsole) {
@ -556,28 +578,33 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
}
/**
* Sets locale reference to this command's description.
* It is used to display the help of this command.
* Sets locale reference to this command's description. It is used to display
* the help of this command.
*
* <br/><br/>
* <br/>
* <br/>
*
* A default value is provided when instantiating this CompositeCommand:
*
* <ul>
* <li>{@code "commands." + getLabel() + ".description"} if this is a top-level command;</li>
* <li>{@code "commands." + getParent.getLabel() + getLabel() + ".description"} if this is a sub-command.
* <br/>
* Note that it can have up to 20 parent commands' labels being inserted before this sub-command's label.
* Here are a few examples :
* <ul>
* <li>/bentobox info : {@code "commands.bentobox.info.description"};</li>
* <li>/bsbadmin range set : {@code "commands.bsbadmin.range.set.description"};</li>
* <li>/mycommand sub1 sub2 sub3 [...] sub22 : {@code "commands.sub3.[...].sub20.sub21.sub22.description"}.</li>
* </ul>
* </li>
* <li>{@code "commands." + getLabel() + ".description"} if this is a top-level
* command;</li>
* <li>{@code "commands." + getParent.getLabel() + getLabel() + ".description"}
* if this is a sub-command. <br/>
* Note that it can have up to 20 parent commands' labels being inserted before
* this sub-command's label. Here are a few examples :
* <ul>
* <li>/bentobox info : {@code "commands.bentobox.info.description"};</li>
* <li>/bsbadmin range set :
* {@code "commands.bsbadmin.range.set.description"};</li>
* <li>/mycommand sub1 sub2 sub3 [...] sub22 :
* {@code "commands.sub3.[...].sub20.sub21.sub22.description"}.</li>
* </ul>
* </li>
* </ul>
*
* This method should therefore only be used in case you want to provide a different value than the default one.
* This method should therefore only be used in case you want to provide a
* different value than the default one.
*
* @param description The locale command's description reference to set.
* @return The instance of this {@link Command}.
@ -589,28 +616,33 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
}
/**
* Sets locale reference to this command's parameters.
* It is used to display the help of this command.
* Sets locale reference to this command's parameters. It is used to display the
* help of this command.
*
* <br/><br/>
* <br/>
* <br/>
*
* A default value is provided when instantiating this CompositeCommand:
*
* <ul>
* <li>{@code "commands." + getLabel() + ".parameters"} if this is a top-level command;</li>
* <li>{@code "commands." + getParent.getLabel() + getLabel() + ".parameters"} if this is a sub-command.
* <br/>
* Note that it can have up to 20 parent commands' labels being inserted before this sub-command's label.
* Here are a few examples :
* <ul>
* <li>/bentobox info : {@code "commands.bentobox.info.parameters"};</li>
* <li>/bsbadmin range set : {@code "commands.bsbadmin.range.set.parameters"};</li>
* <li>/mycommand sub1 sub2 sub3 [...] sub22 : {@code "commands.sub3.[...].sub20.sub21.sub22.parameters"}.</li>
* </ul>
* </li>
* <li>{@code "commands." + getLabel() + ".parameters"} if this is a top-level
* command;</li>
* <li>{@code "commands." + getParent.getLabel() + getLabel() + ".parameters"}
* if this is a sub-command. <br/>
* Note that it can have up to 20 parent commands' labels being inserted before
* this sub-command's label. Here are a few examples :
* <ul>
* <li>/bentobox info : {@code "commands.bentobox.info.parameters"};</li>
* <li>/bsbadmin range set :
* {@code "commands.bsbadmin.range.set.parameters"};</li>
* <li>/mycommand sub1 sub2 sub3 [...] sub22 :
* {@code "commands.sub3.[...].sub20.sub21.sub22.parameters"}.</li>
* </ul>
* </li>
* </ul>
*
* This method should therefore only be used in case you want to provide a different value than the default one.
* This method should therefore only be used in case you want to provide a
* different value than the default one.
*
* @param parametersHelp The locale command's paramaters reference to set.
*/
@ -618,12 +650,15 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
this.parameters = parametersHelp;
}
/* (non-Javadoc)
/*
* (non-Javadoc)
*
* @see org.bukkit.command.Command#setPermission(java.lang.String)
*/
@Override
public void setPermission(String permission) {
this.permission = ((permissionPrefix != null && !permissionPrefix.isEmpty()) ? permissionPrefix : "") + permission;
this.permission = ((permissionPrefix != null && !permissionPrefix.isEmpty()) ? permissionPrefix : "")
+ permission;
}
/**
@ -652,7 +687,8 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
@Override
@NonNull
public List<String> tabComplete(final @NonNull CommandSender sender, final @NonNull String alias, final String[] args) {
public List<String> tabComplete(final @NonNull CommandSender sender, final @NonNull String alias,
final String[] args) {
// Get command object based on args entered so far
CompositeCommand command = getCommandFromArgs(args);
// Check for console and permissions
@ -660,16 +696,22 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
|| (command.isOnlyConsole() && sender instanceof Player)) {
return List.of();
}
if (command.getPermission() != null && !command.getPermission().isEmpty() && !sender.hasPermission(command.getPermission()) && !sender.isOp()) {
if (command.getPermission() != null && !command.getPermission().isEmpty()
&& !sender.hasPermission(command.getPermission()) && !sender.isOp()) {
return List.of();
}
// Add any tab completion from the subcommand
List<String> options = new ArrayList<>(command.tabComplete(User.getInstance(sender), alias, new LinkedList<>(Arrays.asList(args))).orElseGet(ArrayList::new));
List<String> options = new ArrayList<>(
command.tabComplete(User.getInstance(sender), alias, new LinkedList<>(Arrays.asList(args)))
.orElseGet(ArrayList::new));
if (command.hasSubCommands()) {
options.addAll(getSubCommandLabels(sender, command));
}
/* /!\ The following check is likely a poor quality patch-up job. If any better solution can be applied, don't hesitate to do so. */
/*
* /!\ The following check is likely a poor quality patch-up job. If any better
* solution can be applied, don't hesitate to do so.
*/
// See https://github.com/BentoBoxWorld/BentoBox/issues/416
// "help" shouldn't appear twice, so remove it if it is already in the args.
@ -686,17 +728,19 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Returns a list containing all the labels of the subcommands for the provided
* CompositeCommand excluding any hidden commands
* @param sender the CommandSender
*
* @param sender the CommandSender
* @param command the CompositeCommand to get the subcommands from
* @return a list of subcommands labels or an empty list.
*/
@NonNull
private List<String> getSubCommandLabels(@NonNull CommandSender sender, @NonNull CompositeCommand command) {
List<String> result = new ArrayList<>();
for (CompositeCommand cc: command.getSubCommands().values()) {
for (CompositeCommand cc : command.getSubCommands().values()) {
// Player or not
if (sender instanceof Player) {
if (!cc.isHidden() && !cc.isOnlyConsole() && (cc.getPermission().isEmpty() || sender.hasPermission(cc.getPermission()))) {
if (!cc.isHidden() && !cc.isOnlyConsole()
&& (cc.getPermission().isEmpty() || sender.hasPermission(cc.getPermission()))) {
result.add(cc.getLabel());
}
} else if (!cc.isOnlyPlayer()) {
@ -708,12 +752,14 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Show help
*
* @param command - command that this help is for
* @param user - the User
* @param user - the User
* @return result of help command or false if no help defined
*/
protected boolean showHelp(CompositeCommand command, User user) {
return command.getSubCommand("help").map(helpCommand -> helpCommand.execute(user, helpCommand.getLabel(), new ArrayList<>())).orElse(false);
return command.getSubCommand("help")
.map(helpCommand -> helpCommand.execute(user, helpCommand.getLabel(), new ArrayList<>())).orElse(false);
}
/**
@ -724,7 +770,9 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
}
/**
* If the permission prefix has been set, will return the prefix plus a trailing dot.
* If the permission prefix has been set, will return the prefix plus a trailing
* dot.
*
* @return the permissionPrefix
*/
@Nullable
@ -734,6 +782,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* The the world that this command applies to.
*
* @return the world
*/
public World getWorld() {
@ -750,6 +799,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Get the parental addon
*
* @return the addon
*/
@SuppressWarnings("unchecked")
@ -766,28 +816,33 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Set a cool down - can be set by other commands on this one
* @param uniqueId - the unique ID that is having the cooldown
* @param targetUUID - the target (if any)
*
* @param uniqueId - the unique ID that is having the cooldown
* @param targetUUID - the target (if any)
* @param timeInSeconds - time in seconds to cool down
* @since 1.5.0
*/
public void setCooldown(String uniqueId, String targetUUID, int timeInSeconds) {
cooldowns.computeIfAbsent(uniqueId, k -> new HashMap<>()).put(targetUUID, System.currentTimeMillis() + timeInSeconds * 1000L);
cooldowns.computeIfAbsent(uniqueId, k -> new HashMap<>()).put(targetUUID,
System.currentTimeMillis() + timeInSeconds * 1000L);
}
/**
* Set a cool down - can be set by other commands on this one
* @param uniqueId - the UUID that is having the cooldown
* @param targetUUID - the target UUID (if any)
*
* @param uniqueId - the UUID that is having the cooldown
* @param targetUUID - the target UUID (if any)
* @param timeInSeconds - time in seconds to cool down
*/
public void setCooldown(UUID uniqueId, UUID targetUUID, int timeInSeconds) {
cooldowns.computeIfAbsent(uniqueId.toString(), k -> new HashMap<>()).put(targetUUID == null ? null : targetUUID.toString(), System.currentTimeMillis() + timeInSeconds * 1000L);
cooldowns.computeIfAbsent(uniqueId.toString(), k -> new HashMap<>()).put(
targetUUID == null ? null : targetUUID.toString(), System.currentTimeMillis() + timeInSeconds * 1000L);
}
/**
* Set a cool down for a user - can be set by other commands on this one
* @param uniqueId - the UUID that is having the cooldown
*
* @param uniqueId - the UUID that is having the cooldown
* @param timeInSeconds - time in seconds to cool down
* @since 1.5.0
*/
@ -797,7 +852,8 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Check if cool down is in progress for user
* @param user - the caller of the command
*
* @param user - the caller of the command
* @param targetUUID - the target (if any)
* @return true if cool down in place, false if not
*/
@ -807,6 +863,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Check if cool down is in progress for user
*
* @param user - the user to check
* @return true if cool down in place, false if not
* @since 1.5.0
@ -817,14 +874,16 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Check if cool down is in progress
* @param user - the caller of the command
* @param uniqueId - the id that needs to be checked
*
* @param user - the caller of the command
* @param uniqueId - the id that needs to be checked
* @param targetUUID - the target (if any)
* @return true if cool down in place, false if not
* @since 1.5.0
*/
protected boolean checkCooldown(User user, String uniqueId, String targetUUID) {
if (!cooldowns.containsKey(uniqueId) || user.isOp() || user.hasPermission(getPermissionPrefix() + "mod.bypasscooldowns")) {
if (!cooldowns.containsKey(uniqueId) || user.isOp()
|| user.hasPermission(getPermissionPrefix() + "mod.bypasscooldowns")) {
return false;
}
cooldowns.putIfAbsent(uniqueId, new HashMap<>());
@ -833,7 +892,8 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
cooldowns.get(uniqueId).remove(targetUUID);
return false;
}
int timeToGo = (int) ((cooldowns.get(uniqueId).getOrDefault(targetUUID, 0L) - System.currentTimeMillis()) / 1000);
int timeToGo = (int) ((cooldowns.get(uniqueId).getOrDefault(targetUUID, 0L) - System.currentTimeMillis())
/ 1000);
user.sendMessage("general.errors.you-must-wait", TextVariables.NUMBER, String.valueOf(timeToGo));
return true;
}
@ -874,6 +934,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Checks if a command is hidden
*
* @return the hidden
* @since 1.13.0
*/
@ -883,6 +944,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Sets a command and all its help and tab complete as hidden
*
* @param hidden whether command is hidden or not
* @since 1.13.0
*/

View File

@ -5,8 +5,6 @@ import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.bukkit.util.Vector;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
import world.bentobox.bentobox.api.events.island.IslandEvent;
@ -41,14 +39,14 @@ public class AdminDeleteCommand extends ConfirmableCommand {
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
return false;
}
UUID owner = getIslands().getOwner(getWorld(), targetUUID);
if (owner == null) {
Island island = getIslands().getIsland(getWorld(), user);
if (island == null) {
user.sendMessage("general.errors.player-has-no-island");
return false;
}
// Team members should be kicked before deleting otherwise the whole team will become weird
if (getIslands().inTeam(getWorld(), targetUUID) && owner.equals(targetUUID)) {
if (getIslands().inTeam(getWorld(), targetUUID) && user.getUniqueId().equals(island.getOwner())) {
user.sendMessage("commands.admin.delete.cannot-delete-owner");
return false;
}
@ -66,10 +64,7 @@ public class AdminDeleteCommand extends ConfirmableCommand {
private void deletePlayer(User user, UUID targetUUID) {
// Delete player and island
// Get the target's island
Island oldIsland = getIslands().getIsland(getWorld(), targetUUID);
Vector vector = null;
if (oldIsland != null) {
for (Island oldIsland : getIslands().getIslands(getWorld(), targetUUID)) {
// Fire island preclear event
IslandEvent.builder()
.involvedPlayer(user.getUniqueId())
@ -78,21 +73,17 @@ public class AdminDeleteCommand extends ConfirmableCommand {
.oldIsland(oldIsland)
.location(oldIsland.getCenter())
.build();
// Check if player is online and on the island
User target = User.getInstance(targetUUID);
// Remove them from this island (it still exists and will be deleted later)
getIslands().removePlayer(getWorld(), targetUUID);
if (target.isPlayer() && target.isOnline()) {
cleanUp(target);
}
vector = oldIsland.getCenter().toVector();
user.sendMessage("commands.admin.delete.deleted-island", TextVariables.XYZ, Util.xyz(oldIsland.getCenter().toVector()));
getIslands().deleteIsland(oldIsland, true, targetUUID);
}
if (vector == null) {
user.sendMessage("general.success");
} else {
user.sendMessage("commands.admin.delete.deleted-island", TextVariables.XYZ, Util.xyz(vector));
// Check if player is online and on the island
User target = User.getInstance(targetUUID);
// Remove them from this island (it still exists and will be deleted later)
getIslands().removePlayer(getWorld(), targetUUID);
if (target.isPlayer() && target.isOnline()) {
cleanUp(target);
}
user.sendMessage("general.success");
}
private void cleanUp(User target) {
@ -118,6 +109,10 @@ public class AdminDeleteCommand extends ConfirmableCommand {
// Reset the XP
if (getIWM().isOnLeaveResetXP(getWorld())) {
// Player collected XP (displayed)
target.getPlayer().setLevel(0);
target.getPlayer().setExp(0);
// Player total XP (not displayed)
target.getPlayer().setTotalExperience(0);
}

View File

@ -16,6 +16,7 @@ import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.bentobox.util.Util;
/**
* Tells the rank of the player
* @author tastybento
*
*/
@ -82,10 +83,10 @@ public class AdminGetrankCommand extends CompositeCommand {
@Override
public boolean execute(User user, String label, List<String> args) {
// Get rank
RanksManager rm = getPlugin().getRanksManager();
User target = User.getInstance(targetUUID);
int currentRank = island.getRank(target);
user.sendMessage("commands.admin.getrank.rank-is", TextVariables.RANK, user.getTranslation(rm.getRank(currentRank)),
user.sendMessage("commands.admin.getrank.rank-is", TextVariables.RANK,
user.getTranslation(RanksManager.getInstance().getRank(currentRank)),
TextVariables.NAME, getPlayers().getName(island.getOwner()));
return true;
}

View File

@ -79,7 +79,7 @@ public class AdminRegisterCommand extends ConfirmableCommand {
// Register island if it exists
if (!island.map(i -> {
// Island exists
getIslands().setOwner(user, targetUUID, i);
getIslands().setOwner(user, targetUUID, i, RanksManager.VISITOR_RANK);
if (i.isSpawn()) {
getIslands().clearSpawn(i.getWorld());
}
@ -111,7 +111,7 @@ public class AdminRegisterCommand extends ConfirmableCommand {
user.sendMessage("commands.admin.register.cannot-make-island");
return;
}
getIslands().setOwner(user, targetUUID, i);
getIslands().setOwner(user, targetUUID, i, RanksManager.VISITOR_RANK);
i.setReserved(true);
i.getCenter().getBlock().setType(Material.BEDROCK);
user.sendMessage("commands.admin.register.reserved-island", TextVariables.XYZ, Util.xyz(i.getCenter().toVector()),

View File

@ -1,6 +1,5 @@
package world.bentobox.bentobox.api.commands.admin;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@ -12,84 +11,70 @@ import world.bentobox.bentobox.api.commands.ConfirmableCommand;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
/**
* This command sets spawn point for island at admin location for island on which admin is located.
* This command is only for player entity.
* @author BONNe
* @since 1.13.0
*/
public class AdminSetSpawnPointCommand extends ConfirmableCommand
{
public class AdminSetSpawnPointCommand extends ConfirmableCommand {
/**
* Sub-command constructor
*
* @param parent - the parent composite command
*/
public AdminSetSpawnPointCommand(CompositeCommand parent)
{
public AdminSetSpawnPointCommand(CompositeCommand parent) {
super(parent, "setspawnpoint");
}
/**
* {@inheritDoc}
*/
@Override
public void setup()
{
public void setup() {
this.setPermission("admin.setspawnpoint");
this.setOnlyPlayer(true);
this.setDescription("commands.admin.setspawnpoint.description");
}
/**
* This method finds an island in user location and asks confirmation if spawn point
* must be changed to that location.
* This method finds an island in user location and asks confirmation if spawn point
* must be changed to that location.
* {@inheritDoc}
*/
@Override
public boolean execute(User user, String label, List<String> args)
{
public boolean execute(User user, String label, List<String> args) {
Optional<Island> optionalIsland = this.getIslands().getIslandAt(user.getLocation());
if (optionalIsland.isPresent() &&
(optionalIsland.get().hasNetherIsland() ||
!World.Environment.NETHER.equals(user.getLocation().getWorld().getEnvironment())) &&
(optionalIsland.get().hasEndIsland() ||
!World.Environment.THE_END.equals(user.getLocation().getWorld().getEnvironment())))
{
if (optionalIsland.isPresent()
&& (optionalIsland.get().hasNetherIsland()
|| !World.Environment.NETHER.equals(user.getLocation().getWorld().getEnvironment()))
&& (optionalIsland.get().hasEndIsland()
|| !World.Environment.THE_END.equals(user.getLocation().getWorld().getEnvironment()))) {
// Everything's fine, we can set the location as spawn point for island :)
this.askConfirmation(user, user.getTranslation("commands.admin.setspawnpoint.confirmation"),
() -> this.setSpawnPoint(user, optionalIsland.get()));
() -> this.setSpawnPoint(user, optionalIsland.get()));
return true;
}
else
{
} else {
user.sendMessage("commands.admin.setspawnpoint.no-island-here");
return false;
}
}
/**
* This method changes spawn point for island at given user location.
* @param user User who initiate spawn point change.
* @param island Island which is targeted by user.
*/
private void setSpawnPoint(User user, Island island)
{
private void setSpawnPoint(User user, Island island) {
island.setSpawnPoint(Objects.requireNonNull(user.getLocation().getWorld()).getEnvironment(),
user.getLocation());
user.getLocation());
user.sendMessage("commands.admin.setspawnpoint.success");
if (!island.isSpawn())
{
island.getPlayersOnIsland().forEach(player ->
User.getInstance(player).sendMessage("commands.admin.setspawnpoint.island-spawnpoint-changed",
"[user]", user.getName()));
if (!island.isSpawn()) {
island.getPlayersOnIsland().forEach(player -> User.getInstance(player)
.sendMessage("commands.admin.setspawnpoint.island-spawnpoint-changed", "[user]", user.getName()));
}
}
}

View File

@ -24,7 +24,6 @@ public class AdminSetrankCommand extends CompositeCommand {
private int rankValue;
private @Nullable UUID targetUUID;
private @Nullable UUID ownerUUID;
private RanksManager rm;
public AdminSetrankCommand(CompositeCommand adminCommand) {
super(adminCommand, "setrank");
@ -36,7 +35,6 @@ public class AdminSetrankCommand extends CompositeCommand {
setOnlyPlayer(false);
setParametersHelp("commands.admin.setrank.parameters");
setDescription("commands.admin.setrank.description");
rm = getPlugin().getRanksManager();
}
@Override
@ -53,7 +51,7 @@ public class AdminSetrankCommand extends CompositeCommand {
return false;
}
// Get rank
rankValue = rm.getRanks().entrySet().stream()
rankValue = RanksManager.getInstance().getRanks().entrySet().stream()
.filter(r -> user.getTranslation(r.getKey()).equalsIgnoreCase(args.get(1))).findFirst()
.map(Map.Entry::getValue).orElse(-999);
if (rankValue < RanksManager.BANNED_RANK) {
@ -121,8 +119,8 @@ public class AdminSetrankCommand extends CompositeCommand {
ownerName = target.getName();
}
user.sendMessage("commands.admin.setrank.rank-set",
"[from]", user.getTranslation(rm.getRank(currentRank)),
"[to]", user.getTranslation(rm.getRank(rankValue)),
"[from]", user.getTranslation(RanksManager.getInstance().getRank(currentRank)), "[to]",
user.getTranslation(RanksManager.getInstance().getRank(rankValue)),
TextVariables.NAME, ownerName);
return true;
}
@ -136,7 +134,7 @@ public class AdminSetrankCommand extends CompositeCommand {
// Return the ranks
if (args.size() == 3) {
return Optional.of(getPlugin().getRanksManager().getRanks()
return Optional.of(RanksManager.getInstance().getRanks()
.entrySet().stream()
.filter(entry -> entry.getValue() > RanksManager.VISITOR_RANK)
.map(entry -> user.getTranslation(entry.getKey())).toList());

View File

@ -175,7 +175,7 @@ public class AdminSettingsCommand extends CompositeCommand {
* @return true if rank is valid
*/
private boolean checkRank(User user, String string) {
for (Entry<String, Integer> en : getPlugin().getRanksManager().getRanks().entrySet()) {
for (Entry<String, Integer> en : RanksManager.getInstance().getRanks().entrySet()) {
if (en.getValue() > RanksManager.BANNED_RANK && en.getValue() <= RanksManager.OWNER_RANK
&& string.equalsIgnoreCase(ChatColor.stripColor(user.getTranslation(en.getKey())))) {
// We have a winner
@ -242,8 +242,8 @@ public class AdminSettingsCommand extends CompositeCommand {
new TabbedPanelBuilder()
.user(user)
.world(island.getWorld())
.tab(1, new SettingsTab(user, island, Flag.Type.PROTECTION))
.tab(2, new SettingsTab(user, island, Flag.Type.SETTING))
.island(island).tab(1, new SettingsTab(user, Flag.Type.PROTECTION))
.tab(2, new SettingsTab(user, Flag.Type.SETTING))
.startingSlot(1)
.size(54)
.build().openPanel();
@ -277,8 +277,7 @@ public class AdminSettingsCommand extends CompositeCommand {
} else if (args.size() == 4) {
// Get flag in previous argument
options = getPlugin().getFlagsManager().getFlag(args.get(2).toUpperCase(Locale.ENGLISH)).map(f -> switch (f.getType()) {
case PROTECTION -> getPlugin().getRanksManager()
.getRanks().entrySet().stream()
case PROTECTION -> RanksManager.getInstance().getRanks().entrySet().stream()
.filter(en -> en.getValue() > RanksManager.BANNED_RANK && en.getValue() <= RanksManager.OWNER_RANK)
.map(Entry::getKey)
.map(user::getTranslation).toList();

View File

@ -118,12 +118,10 @@ public class AdminTeleportCommand extends CompositeCommand {
private Location getSpot(World world) {
Island island = getIslands().getIsland(world, targetUUID);
if (island != null && island.getSpawnPoint(world.getEnvironment()) != null) {
// Return the defined spawn point
return island.getSpawnPoint(world.getEnvironment());
if (island == null) {
return null;
}
// Return the default island protection center
return island.getProtectionCenter().toVector().toLocation(world);
return island.getSpawnPoint(world.getEnvironment()) != null ? island.getSpawnPoint(world.getEnvironment()) : island.getProtectionCenter().toVector().toLocation(world);
}
@Override

View File

@ -32,6 +32,7 @@ public abstract class DefaultAdminCommand extends CompositeCommand {
*/
protected DefaultAdminCommand(GameModeAddon addon) {
// Register command with alias from config.
// The first command listed is the "label" and the others are aliases.
super(addon,
addon.getWorldSettings().getAdminCommandAliases().split(" ")[0],
addon.getWorldSettings().getAdminCommandAliases().split(" "));

View File

@ -48,12 +48,10 @@ public class AdminTeamAddCommand extends CompositeCommand {
user.sendMessage("general.errors.player-has-no-island");
return false;
}
if (getIslands().inTeam(getWorld(), ownerUUID) && !getIslands().getOwner(getWorld(), ownerUUID).equals(ownerUUID)) {
Island island = getIslands().getPrimaryIsland(getWorld(), ownerUUID);
if (getIslands().inTeam(getWorld(), ownerUUID) && island != null && !ownerUUID.equals(island.getOwner())) {
user.sendMessage("commands.admin.team.add.name-not-owner", TextVariables.NAME, args.get(0));
Island island = getIslands().getIsland(getWorld(), ownerUUID);
if (island != null) {
new IslandInfo(island).showMembers(user);
}
new IslandInfo(island).showMembers(user);
return false;
}
if (getIslands().inTeam(getWorld(), targetUUID)) {
@ -67,25 +65,19 @@ public class AdminTeamAddCommand extends CompositeCommand {
// Success
User target = User.getInstance(targetUUID);
User owner = User.getInstance(ownerUUID);
owner.sendMessage("commands.island.team.invite.accept.name-joined-your-island", TextVariables.NAME, getPlugin().getPlayers().getName(targetUUID));
owner.sendMessage("commands.island.team.invite.accept.name-joined-your-island", TextVariables.NAME,
getPlugin().getPlayers().getName(targetUUID));
target.sendMessage("commands.island.team.invite.accept.you-joined-island", TextVariables.LABEL, getTopLabel());
Island teamIsland = getIslands().getIsland(getWorld(), ownerUUID);
if (teamIsland != null) {
getIslands().setJoinTeam(teamIsland, targetUUID);
user.sendMessage("commands.admin.team.add.success", TextVariables.NAME, target.getName(), "[owner]", owner.getName());
TeamEvent.builder()
.island(teamIsland)
.reason(TeamEvent.Reason.JOINED)
.involvedPlayer(targetUUID)
.admin(true)
.build();
IslandEvent.builder()
.island(teamIsland)
.involvedPlayer(targetUUID)
.admin(true)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(teamIsland.getRank(target), RanksManager.MEMBER_RANK)
.build();
user.sendMessage("commands.admin.team.add.success", TextVariables.NAME, target.getName(), "[owner]",
owner.getName());
TeamEvent.builder().island(teamIsland).reason(TeamEvent.Reason.JOINED).involvedPlayer(targetUUID)
.admin(true).build();
IslandEvent.builder().island(teamIsland).involvedPlayer(targetUUID).admin(true)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(teamIsland.getRank(target), RanksManager.MEMBER_RANK).build();
return true;
} else {
user.sendMessage("general.errors.player-has-no-island");

View File

@ -46,31 +46,23 @@ public class AdminTeamDisbandCommand extends CompositeCommand {
user.sendMessage("general.errors.not-in-team");
return false;
}
if (!getIslands().getOwner(getWorld(), targetUUID).equals(targetUUID)) {
user.sendMessage("commands.admin.team.disband.use-disband-owner", "[owner]", getPlayers().getName(getIslands().getOwner(getWorld(), targetUUID)));
Island island = getIslands().getPrimaryIsland(getWorld(), targetUUID);
if (!targetUUID.equals(island.getOwner())) {
user.sendMessage("commands.admin.team.disband.use-disband-owner", "[owner]",
getPlayers().getName(island.getOwner()));
return false;
}
// Disband team
Island island = getIslands().getIsland(getWorld(), targetUUID);
getIslands().getMembers(getWorld(), targetUUID).forEach(m -> {
island.getMemberSet().forEach(m -> {
User mUser = User.getInstance(m);
mUser.sendMessage("commands.admin.team.disband.disbanded");
// The owner gets to keep the island
if (!m.equals(targetUUID)) {
getIslands().setLeaveTeam(getWorld(), m);
TeamEvent.builder()
.island(island)
.reason(TeamEvent.Reason.KICK)
.involvedPlayer(m)
.admin(true)
.build();
IslandEvent.builder()
.island(island)
.involvedPlayer(targetUUID)
.admin(true)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(island.getRank(mUser), RanksManager.VISITOR_RANK)
.build();
getIslands().removePlayer(island, m);
TeamEvent.builder().island(island).reason(TeamEvent.Reason.KICK).involvedPlayer(m).admin(true).build();
IslandEvent.builder().island(island).involvedPlayer(targetUUID).admin(true)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(island.getRank(mUser), RanksManager.VISITOR_RANK).build();
}
});
user.sendMessage("commands.admin.team.disband.success", TextVariables.NAME, args.get(0));

View File

@ -1,7 +1,6 @@
package world.bentobox.bentobox.api.commands.admin.team;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import world.bentobox.bentobox.api.commands.CompositeCommand;
@ -15,6 +14,7 @@ import world.bentobox.bentobox.util.Util;
/**
* Sets the owner of an island.
*
* @author tastybento
*/
public class AdminTeamSetownerCommand extends CompositeCommand {
@ -47,8 +47,8 @@ public class AdminTeamSetownerCommand extends CompositeCommand {
user.sendMessage("general.errors.not-in-team");
return false;
}
UUID previousOwnerUUID = getIslands().getOwner(getWorld(), targetUUID);
Island island = getIslands().getPrimaryIsland(getWorld(), targetUUID);
UUID previousOwnerUUID = island.getOwner();
if (targetUUID.equals(previousOwnerUUID)) {
user.sendMessage("commands.admin.team.setowner.already-owner", TextVariables.NAME, args.get(0));
return false;
@ -58,24 +58,16 @@ public class AdminTeamSetownerCommand extends CompositeCommand {
User target = User.getInstance(targetUUID);
// Fire event so add-ons know
Island island = Objects.requireNonNull(getIslands().getIsland(getWorld(), targetUUID));
// Call the setowner event
TeamEvent.builder()
.island(island)
.reason(TeamEvent.Reason.SETOWNER)
.involvedPlayer(targetUUID)
.admin(true)
.build();
TeamEvent.builder().island(island).reason(TeamEvent.Reason.SETOWNER).involvedPlayer(targetUUID).admin(true)
.build();
// Call the rank change event for the new island owner
// We need to call it BEFORE the actual change, in order to retain the player's previous rank.
IslandEvent.builder()
.island(island)
.involvedPlayer(targetUUID)
.admin(true)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(island.getRank(target), RanksManager.OWNER_RANK)
.build();
// We need to call it BEFORE the actual change, in order to retain the player's
// previous rank.
IslandEvent.builder().island(island).involvedPlayer(targetUUID).admin(true)
.reason(IslandEvent.Reason.RANK_CHANGE).rankChange(island.getRank(target), RanksManager.OWNER_RANK)
.build();
// Make new owner
getIslands().setOwner(getWorld(), user, targetUUID);
@ -84,13 +76,9 @@ public class AdminTeamSetownerCommand extends CompositeCommand {
// Call the rank change event for the old island owner
if (previousOwnerUUID != null) {
// We need to call it AFTER the actual change.
IslandEvent.builder()
.island(island)
.involvedPlayer(previousOwnerUUID)
.admin(true)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(RanksManager.OWNER_RANK, island.getRank(previousOwnerUUID))
.build();
IslandEvent.builder().island(island).involvedPlayer(previousOwnerUUID).admin(true)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(RanksManager.OWNER_RANK, island.getRank(previousOwnerUUID)).build();
}
return true;
}

View File

@ -23,6 +23,7 @@ public abstract class DefaultPlayerCommand extends CompositeCommand {
*/
protected DefaultPlayerCommand(GameModeAddon addon) {
// Register command with alias from config.
// The first command listed is the "label" and the others are aliases.
super(addon,
addon.getWorldSettings().getPlayerCommandAliases().split(" ")[0],
addon.getWorldSettings().getPlayerCommandAliases().split(" "));

View File

@ -17,6 +17,7 @@ import world.bentobox.bentobox.api.events.island.IslandEvent;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.bentobox.util.Util;
public class IslandBanCommand extends CompositeCommand {
@ -45,7 +46,8 @@ public class IslandBanCommand extends CompositeCommand {
}
UUID playerUUID = user.getUniqueId();
// Player issuing the command must have an island or be in a team
if (!getIslands().inTeam(getWorld(), user.getUniqueId()) && !getIslands().hasIsland(getWorld(), user.getUniqueId())) {
if (!getIslands().inTeam(getWorld(), user.getUniqueId())
&& !getIslands().hasIsland(getWorld(), user.getUniqueId())) {
user.sendMessage("general.errors.no-island");
return false;
}
@ -53,7 +55,8 @@ public class IslandBanCommand extends CompositeCommand {
Island island = Objects.requireNonNull(getIslands().getIsland(getWorld(), user));
int rank = island.getRank(user);
if (rank < island.getRankCommand(getUsage())) {
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK, user.getTranslation(getPlugin().getRanksManager().getRank(rank)));
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK,
user.getTranslation(RanksManager.getInstance().getRank(rank)));
return false;
}
// Get target player
@ -67,7 +70,7 @@ public class IslandBanCommand extends CompositeCommand {
user.sendMessage("commands.island.ban.cannot-ban-yourself");
return false;
}
if (getIslands().getMembers(getWorld(), user.getUniqueId()).contains(targetUUID)) {
if (getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()).getMemberSet().contains(targetUUID)) {
user.sendMessage("commands.island.ban.cannot-ban-member");
return false;
}
@ -97,25 +100,26 @@ public class IslandBanCommand extends CompositeCommand {
Island island = Objects.requireNonNull(getIslands().getIsland(getWorld(), issuer.getUniqueId()));
// Check if player can ban any more players
int banLimit = issuer.getPermissionValue(getPermissionPrefix() + "ban.maxlimit", getIWM().getBanLimit(getWorld()));
int banLimit = issuer.getPermissionValue(getPermissionPrefix() + "ban.maxlimit",
getIWM().getBanLimit(getWorld()));
if (banLimit <= -1 || island.getBanned().size() < banLimit) {
// Run the event
IslandBaseEvent banEvent = IslandEvent.builder()
.island(island)
.involvedPlayer(target.getUniqueId())
.admin(false)
.reason(IslandEvent.Reason.BAN)
.build();
if (banEvent.getNewEvent().map(IslandBaseEvent::isCancelled).orElse(banEvent.isCancelled()) ) {
IslandBaseEvent banEvent = IslandEvent.builder().island(island).involvedPlayer(target.getUniqueId())
.admin(false).reason(IslandEvent.Reason.BAN).build();
if (banEvent.getNewEvent().map(IslandBaseEvent::isCancelled).orElse(banEvent.isCancelled())) {
// Banning was blocked due to an event cancellation. Fail silently.
return false;
}
// Event is not cancelled
if (island.ban(issuer.getUniqueId(), target.getUniqueId())) {
issuer.sendMessage("commands.island.ban.player-banned", TextVariables.NAME, target.getName(), TextVariables.DISPLAY_NAME, target.getDisplayName());
target.sendMessage("commands.island.ban.owner-banned-you", TextVariables.NAME, issuer.getName(), TextVariables.DISPLAY_NAME, issuer.getDisplayName());
// If the player is online, has an island and on the banned island, move them home immediately
if (target.isOnline() && getIslands().hasIsland(getWorld(), target.getUniqueId()) && island.onIsland(target.getLocation())) {
issuer.sendMessage("commands.island.ban.player-banned", TextVariables.NAME, target.getName(),
TextVariables.DISPLAY_NAME, target.getDisplayName());
target.sendMessage("commands.island.ban.owner-banned-you", TextVariables.NAME, issuer.getName(),
TextVariables.DISPLAY_NAME, issuer.getDisplayName());
// If the player is online, has an island and on the banned island, move them
// home immediately
if (target.isOnline() && getIslands().hasIsland(getWorld(), target.getUniqueId())
&& island.onIsland(target.getLocation())) {
getIslands().homeTeleportAsync(getWorld(), target.getPlayer());
island.getWorld().playSound(target.getLocation(), Sound.ENTITY_GENERIC_EXPLODE, 1F, 1F);
}
@ -130,7 +134,7 @@ public class IslandBanCommand extends CompositeCommand {
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
String lastArg = !args.isEmpty() ? args.get(args.size() - 1) : "";
if (lastArg.isEmpty()) {
// Don't show every player on the server. Require at least the first letter
return Optional.empty();
@ -139,8 +143,7 @@ public class IslandBanCommand extends CompositeCommand {
if (island != null) {
List<String> options = Bukkit.getOnlinePlayers().stream()
.filter(p -> !p.getUniqueId().equals(user.getUniqueId()))
.filter(p -> !island.isBanned(p.getUniqueId()))
.filter(p -> user.getPlayer().canSee(p))
.filter(p -> !island.isBanned(p.getUniqueId())).filter(p -> user.getPlayer().canSee(p))
.map(Player::getName).toList();
return Optional.of(Util.tabLimit(options, lastArg));
} else {

View File

@ -8,6 +8,7 @@ import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.RanksManager;
public class IslandBanlistCommand extends CompositeCommand {
@ -40,7 +41,8 @@ public class IslandBanlistCommand extends CompositeCommand {
island = getIslands().getIsland(getWorld(), user.getUniqueId());
int rank = Objects.requireNonNull(island).getRank(user);
if (rank < island.getRankCommand(getUsage())) {
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK, user.getTranslation(getPlugin().getRanksManager().getRank(rank)));
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK,
user.getTranslation(RanksManager.getInstance().getRank(rank)));
return false;
}
return true;

View File

@ -5,16 +5,16 @@ import java.util.List;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.api.addons.GameModeAddon;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.events.island.IslandEvent.Reason;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.BlueprintsManager;
import world.bentobox.bentobox.managers.island.NewIsland;
import world.bentobox.bentobox.panels.IslandCreationPanel;
import world.bentobox.bentobox.panels.customizable.IslandCreationPanel;
import world.bentobox.bentobox.util.Util;
/**
* /island create - Create an island.
*
@ -24,6 +24,7 @@ public class IslandCreateCommand extends CompositeCommand {
/**
* Command to create an island
*
* @param islandCommand - parent command
*/
public IslandCreateCommand(CompositeCommand islandCommand) {
@ -42,14 +43,28 @@ public class IslandCreateCommand extends CompositeCommand {
public boolean canExecute(User user, String label, List<String> args) {
// Check if the island is reserved
@Nullable
Island island = getIslands().getIsland(getWorld(), user);
Island island = getIslands().getPrimaryIsland(getWorld(), user.getUniqueId());
if (island != null) {
// Reserved islands can be made
if (island.isReserved()) {
return true;
}
}
// Check if this player is on a team in this world
if (getIslands().inTeam(getWorld(), user.getUniqueId()) && island != null
&& !user.getUniqueId().equals(island.getOwner())) {
// Team members who are not owners cannot make additional islands
user.sendMessage("commands.island.create.you-cannot-make-team");
return false;
}
// Get how many islands this player has
int num = this.getIslands().getNumberOfConcurrentIslands(user.getUniqueId(), getWorld());
int max = user.getPermissionValue(
this.getIWM().getAddon(getWorld()).map(GameModeAddon::getPermissionPrefix).orElse("") + "island.number",
this.getIWM().getWorldSettings(getWorld()).getConcurrentIslands());
if (num >= max) {
// You cannot make an island
user.sendMessage("general.errors.already-have-island");
user.sendMessage("commands.island.create.you-cannot-make");
return false;
}
if (getIWM().getMaxIslands(getWorld()) > 0
@ -90,19 +105,15 @@ public class IslandCreateCommand extends CompositeCommand {
private boolean makeIsland(User user, String name) {
user.sendMessage("commands.island.create.creating-island");
try {
NewIsland.builder()
.player(user)
.addon(getAddon())
.reason(Reason.CREATE)
.name(name)
.build();
NewIsland.builder().player(user).addon(getAddon()).reason(Reason.CREATE).name(name).build();
} catch (IOException e) {
getPlugin().logError("Could not create island for player. " + e.getMessage());
user.sendMessage(e.getMessage());
return false;
}
if (getSettings().isResetCooldownOnCreate()) {
getParent().getSubCommand("reset").ifPresent(resetCommand -> resetCommand.setCooldown(user.getUniqueId(), getSettings().getResetCooldown()));
getParent().getSubCommand("reset").ifPresent(
resetCommand -> resetCommand.setCooldown(user.getUniqueId(), getSettings().getResetCooldown()));
}
return true;
}

View File

@ -1,12 +1,12 @@
package world.bentobox.bentobox.api.commands.island;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
import world.bentobox.bentobox.api.localization.TextVariables;
@ -22,8 +22,6 @@ import world.bentobox.bentobox.util.Util;
*/
public class IslandDeletehomeCommand extends ConfirmableCommand {
private @Nullable Island island;
/**
* Deletes a home
* @param islandCommand parent command
@ -48,7 +46,7 @@ public class IslandDeletehomeCommand extends ConfirmableCommand {
this.showHelp(this, user);
return false;
}
island = getIslands().getIsland(getWorld(), user);
Island island = getIslands().getIsland(getWorld(), user);
// Check island
if (island == null) {
user.sendMessage("general.errors.no-island");
@ -59,23 +57,25 @@ public class IslandDeletehomeCommand extends ConfirmableCommand {
int rank = Objects.requireNonNull(island).getRank(user);
if (rank < island.getRankCommand(getUsage())) {
user.sendMessage("general.errors.insufficient-rank",
TextVariables.RANK, user.getTranslation(getPlugin().getRanksManager().getRank(rank)));
TextVariables.RANK, user.getTranslation(RanksManager.getInstance().getRank(rank)));
return false;
}
// Check if the name is known
if (!getIslands().isHomeLocation(island, String.join(" ", args))) {
user.sendMessage("commands.island.go.unknown-home");
user.sendMessage("commands.island.sethome.homes-are");
island.getHomes().keySet().stream().filter(s -> !s.isEmpty()).forEach(s -> user.sendMessage("home-list-syntax", TextVariables.NAME, s));
return false;
}
return true;
}
@Override
public boolean execute(User user, String label, List<String> args) {
this.askConfirmation(user, () -> delete(island, user, String.join(" ", args)));
// Check if the name is known
Map<String, Island> map = getNameIslandMap(user);
String name = String.join(" ", args);
if (!map.containsKey(name)) {
user.sendMessage("commands.island.go.unknown-home");
user.sendMessage("commands.island.sethome.homes-are");
map.keySet().stream().filter(s -> !s.isEmpty()).forEach(s -> user.sendMessage("commands.island.sethome.home-list-syntax", TextVariables.NAME, s));
return false;
}
this.askConfirmation(user, () -> delete(map.get(name), user, name));
return true;
}
@ -88,11 +88,19 @@ public class IslandDeletehomeCommand extends ConfirmableCommand {
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
Island is = getIslands().getIsland(getWorld(), user.getUniqueId());
if (is != null) {
return Optional.of(Util.tabLimit(new ArrayList<>(is.getHomes().keySet()), lastArg));
} else {
return Optional.empty();
}
return Optional.of(Util.tabLimit(new ArrayList<>(getNameIslandMap(user).keySet()), lastArg));
}
private Map<String, Island> getNameIslandMap(User user) {
Map<String, Island> islandMap = new HashMap<>();
for (Island isle : getIslands().getIslands(getWorld(), user.getUniqueId())) {
// Add homes.
isle.getHomes().keySet().forEach(name -> islandMap.put(name, isle));
}
return islandMap;
}
}

View File

@ -16,6 +16,7 @@ import world.bentobox.bentobox.api.events.island.IslandEvent;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.bentobox.util.Util;
/**
@ -59,7 +60,8 @@ public class IslandExpelCommand extends CompositeCommand {
Island island = getIslands().getIsland(getWorld(), user);
int rank = Objects.requireNonNull(island).getRank(user);
if (rank < island.getRankCommand(getUsage())) {
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK, user.getTranslation(getPlugin().getRanksManager().getRank(rank)));
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK,
user.getTranslation(RanksManager.getInstance().getRank(rank)));
return false;
}
// Get target player
@ -74,7 +76,7 @@ public class IslandExpelCommand extends CompositeCommand {
return false;
}
// Or team member
if (getIslands().getMembers(getWorld(), user.getUniqueId()).contains(targetUUID)) {
if (island.getMemberSet().contains(targetUUID)) {
user.sendMessage("commands.island.expel.cannot-expel-member");
return false;
}
@ -90,53 +92,48 @@ public class IslandExpelCommand extends CompositeCommand {
return false;
}
// Cannot ban ops
if (target.isOp() ||
target.hasPermission(this.getPermissionPrefix() + "admin.noexpel") ||
target.hasPermission(this.getPermissionPrefix() + "mod.bypassexpel")) {
if (target.isOp() || target.hasPermission(this.getPermissionPrefix() + "admin.noexpel")
|| target.hasPermission(this.getPermissionPrefix() + "mod.bypassexpel")) {
user.sendMessage(CANNOT_EXPEL);
return false;
}
return true;
}
@Override
public boolean execute(User user, String label, List<String> args) {
// Finished error checking - expel player
Island island = getIslands().getIsland(getWorld(), user);
// Fire event
IslandBaseEvent expelEvent = IslandEvent.builder()
.island(island)
.involvedPlayer(target.getUniqueId())
.admin(false)
.reason(IslandEvent.Reason.EXPEL)
.build();
IslandBaseEvent expelEvent = IslandEvent.builder().island(island).involvedPlayer(target.getUniqueId())
.admin(false).reason(IslandEvent.Reason.EXPEL).build();
if (expelEvent.getNewEvent().map(IslandBaseEvent::isCancelled).orElse(expelEvent.isCancelled())) {
user.sendMessage(CANNOT_EXPEL);
return false;
}
target.sendMessage("commands.island.expel.player-expelled-you", TextVariables.NAME, user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName());
target.sendMessage("commands.island.expel.player-expelled-you", TextVariables.NAME, user.getName(),
TextVariables.DISPLAY_NAME, user.getDisplayName());
island.getWorld().playSound(target.getLocation(), Sound.ENTITY_GENERIC_EXPLODE, 1F, 1F);
if (getIslands().hasIsland(getWorld(), target) || getIslands().inTeam(getWorld(), target.getUniqueId())) {
// Success
user.sendMessage(SUCCESS, TextVariables.NAME, target.getName(), TextVariables.DISPLAY_NAME, target.getDisplayName());
user.sendMessage(SUCCESS, TextVariables.NAME, target.getName(), TextVariables.DISPLAY_NAME,
target.getDisplayName());
// Teleport home
getIslands().homeTeleportAsync(getWorld(), target.getPlayer());
return true;
} else if (getIslands().getSpawn(getWorld()).isPresent()){
} else if (getIslands().getSpawn(getWorld()).isPresent()) {
// Success
user.sendMessage(SUCCESS, TextVariables.NAME, target.getName(), TextVariables.DISPLAY_NAME, target.getDisplayName());
user.sendMessage(SUCCESS, TextVariables.NAME, target.getName(), TextVariables.DISPLAY_NAME,
target.getDisplayName());
getIslands().spawnTeleport(getWorld(), target.getPlayer());
return true;
} else if (getIWM().getAddon(getWorld())
.map(gm -> gm.getPlayerCommand()
.map(pc -> pc.getSubCommand("create").isPresent())
.orElse(false))
.orElse(false)
&& target.performCommand(this.getTopLabel() + " create")) {
.map(gm -> gm.getPlayerCommand().map(pc -> pc.getSubCommand("create").isPresent()).orElse(false))
.orElse(false) && target.performCommand(this.getTopLabel() + " create")) {
getAddon().logWarning("Expel: " + target.getName() + " had no island, so one was created");
user.sendMessage(SUCCESS, TextVariables.NAME, target.getName(), TextVariables.DISPLAY_NAME, target.getDisplayName());
user.sendMessage(SUCCESS, TextVariables.NAME, target.getName(), TextVariables.DISPLAY_NAME,
target.getDisplayName());
return true;
}
@ -149,15 +146,15 @@ public class IslandExpelCommand extends CompositeCommand {
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
Island island = getIslands().getIsland(getWorld(), user);
if (island != null) {
List<String> options = island.getPlayersOnIsland().stream()
.filter(p -> !p.equals(user.getPlayer())) // Not self
List<String> options = island.getPlayersOnIsland().stream().filter(p -> !p.equals(user.getPlayer())) // Not
// self
.filter(p -> user.getPlayer().canSee(p)) // Not invisible
.filter(p -> !p.isOp()) // Not op
.filter(p -> !p.hasPermission(this.getPermissionPrefix() + "admin.noexpel"))
.filter(p -> !p.hasPermission(this.getPermissionPrefix() + "mod.bypassexpel"))
.map(Player::getName).toList();
.filter(p -> !p.hasPermission(this.getPermissionPrefix() + "mod.bypassexpel")).map(Player::getName)
.toList();
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
String lastArg = !args.isEmpty() ? args.get(args.size() - 1) : "";
return Optional.of(Util.tabLimit(options, lastArg));
} else {
return Optional.empty();

View File

@ -2,8 +2,11 @@ package world.bentobox.bentobox.api.commands.island;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.commands.DelayedTeleportCommand;
@ -38,47 +41,92 @@ public class IslandGoCommand extends DelayedTeleportCommand {
user.sendMessage("commands.island.go.teleport");
return false;
}
// Check if the island is reserved
Island island = getIslands().getIsland(getWorld(), user.getUniqueId());
if (island == null) {
Set<Island> islands = getIslands().getIslands(getWorld(), user.getUniqueId());
if (islands.isEmpty()) {
user.sendMessage("general.errors.no-island");
return false;
}
if (island.isReserved()) {
// Send player to create an island
getParent().getSubCommand("create").ifPresent(createCmd -> createCmd.call(user, createCmd.getLabel(), Collections.emptyList()));
// Check if the island is reserved
if (checkReserved(user, islands)) {
return false;
}
// Prevent command if player is falling and its not allowed
if ((getIWM().inWorld(user.getWorld()) && Flags.PREVENT_TELEPORT_WHEN_FALLING.isSetForWorld(user.getWorld()))
&& user.getPlayer().getFallDistance() > 0) {
// We're sending the "hint" to the player to tell them they cannot teleport while falling.
user.sendMessage(Flags.PREVENT_TELEPORT_WHEN_FALLING.getHintReference());
return false;
}
if (!args.isEmpty() && !getIslands().isHomeLocation(island, String.join(" ", args))) {
user.sendMessage("commands.island.go.unknown-home");
user.sendMessage("commands.island.sethome.homes-are");
island.getHomes().keySet().stream().filter(s -> !s.isEmpty()).forEach(s -> user.sendMessage("commands.island.sethome.home-list-syntax", TextVariables.NAME, s));
return false;
}
return true;
}
@Override
public boolean execute(User user, String label, List<String> args) {
this.delayCommand(user, () -> getIslands().homeTeleportAsync(getWorld(), user.getPlayer(), String.join(" ", args)));
// Check if the home is known
if (!args.isEmpty()) {
Map<String, IslandInfo> names = getNameIslandMap(user);
final String name = String.join(" ", args);
if (!names.containsKey(name)) {
// Failed home name check
user.sendMessage("commands.island.go.unknown-home");
user.sendMessage("commands.island.sethome.homes-are");
names.keySet().forEach(n -> user.sendMessage("commands.island.sethome.home-list-syntax", TextVariables.NAME, n));
return false;
} else {
IslandInfo info = names.get(name);
getIslands().setPrimaryIsland(user.getUniqueId(), info.island);
if (!info.islandName) {
this.delayCommand(user, () -> getIslands().homeTeleportAsync(getWorld(), user.getPlayer(), name)
.thenAccept((r) -> getIslands().setPrimaryIsland(user.getUniqueId(), info.island)));
return true;
}
}
}
this.delayCommand(user, () -> getIslands().homeTeleportAsync(getWorld(), user.getPlayer()));
return true;
}
private boolean checkReserved(User user, Set<Island> islands) {
for (Island island : islands) {
if (island.isReserved()) {
// Send player to create an island
getParent().getSubCommand("create").ifPresent(createCmd -> createCmd.call(user, createCmd.getLabel(), Collections.emptyList()));
return true;
}
}
return false;
}
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
Island island = getIslands().getIsland(getWorld(), user.getUniqueId());
if (island != null) {
return Optional.of(Util.tabLimit(new ArrayList<>(island.getHomes().keySet()), lastArg));
} else {
return Optional.empty();
return Optional.of(Util.tabLimit(new ArrayList<>(getNameIslandMap(user).keySet()), lastArg));
}
private record IslandInfo(Island island, boolean islandName) {}
private Map<String, IslandInfo> getNameIslandMap(User user) {
Map<String, IslandInfo> islandMap = new HashMap<>();
int index = 0;
for (Island island : getIslands().getIslands(getWorld(), user.getUniqueId())) {
index++;
if (island.getName() != null && !island.getName().isBlank()) {
// Name has been set
islandMap.put(island.getName(), new IslandInfo(island, true));
} else {
// Name has not been set
String text = user.getTranslation("protection.flags.ENTER_EXIT_MESSAGES.island", TextVariables.NAME, user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName()) + " " + index;
islandMap.put(text, new IslandInfo(island, true));
}
// Add homes. Homes do not need an island specified
island.getHomes().keySet().forEach(n -> islandMap.put(n, new IslandInfo(island, false)));
}
return islandMap;
}
}

View File

@ -1,18 +1,20 @@
package world.bentobox.bentobox.api.commands.island;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jdt.annotation.Nullable;
import java.util.Optional;
import java.util.Set;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.Util;
public class IslandHomesCommand extends ConfirmableCommand {
private @Nullable Island island;
private Set<Island> islands;
public IslandHomesCommand(CompositeCommand islandCommand) {
super(islandCommand, "homes");
@ -27,9 +29,9 @@ public class IslandHomesCommand extends ConfirmableCommand {
@Override
public boolean canExecute(User user, String label, List<String> args) {
island = getIslands().getIsland(getWorld(), user);
islands = getIslands().getIslands(getWorld(), user);
// Check island
if (island == null || island.getOwner() == null) {
if (islands.isEmpty()) {
user.sendMessage("general.errors.no-island");
return false;
}
@ -39,9 +41,21 @@ public class IslandHomesCommand extends ConfirmableCommand {
@Override
public boolean execute(User user, String label, List<String> args) {
user.sendMessage("commands.island.sethome.homes-are");
islands.forEach(island ->
island.getHomes().keySet().stream().filter(s -> !s.isEmpty())
.forEach(s -> user.sendMessage("commands.island.sethome.home-list-syntax", TextVariables.NAME, s));
.forEach(s -> user.sendMessage("commands.island.sethome.home-list-syntax", TextVariables.NAME, s)));
return true;
}
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
List<String> result = new ArrayList<>();
for (Island island : getIslands().getIslands(getWorld(), user.getUniqueId())) {
result.addAll(island.getHomes().keySet());
}
return Optional.of(Util.tabLimit(result, lastArg));
}
}

View File

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

View File

@ -65,7 +65,8 @@ public class IslandRenamehomeCommand extends ConfirmableCommand {
// check command permission
int rank = Objects.requireNonNull(island).getRank(user);
if (rank < island.getRankCommand(getUsage())) {
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK, user.getTranslation(getPlugin().getRanksManager().getRank(rank)));
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK,
user.getTranslation(RanksManager.getInstance().getRank(rank)));
return false;
}

View File

@ -16,10 +16,9 @@ import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.BlueprintsManager;
import world.bentobox.bentobox.managers.island.NewIsland;
import world.bentobox.bentobox.managers.island.NewIsland.Builder;
import world.bentobox.bentobox.panels.IslandCreationPanel;
import world.bentobox.bentobox.panels.customizable.IslandCreationPanel;
import world.bentobox.bentobox.util.Util;
/**
* @author tastybento
*/
@ -33,8 +32,9 @@ public class IslandResetCommand extends ConfirmableCommand {
/**
* Creates the island reset command
*
* @param islandCommand - parent command
* @param noPaste - true if resetting should not paste a new island
* @param noPaste - true if resetting should not paste a new island
*/
public IslandResetCommand(CompositeCommand islandCommand, boolean noPaste) {
super(islandCommand, "reset", "restart");
@ -93,7 +93,8 @@ public class IslandResetCommand extends ConfirmableCommand {
} else {
// Show panel after confirmation
if (getPlugin().getSettings().isResetConfirmation()) {
this.askConfirmation(user, user.getTranslation("commands.island.reset.confirmation"), () -> selectBundle(user, label));
this.askConfirmation(user, user.getTranslation("commands.island.reset.confirmation"),
() -> selectBundle(user, label));
} else {
selectBundle(user, label);
}
@ -103,6 +104,7 @@ public class IslandResetCommand extends ConfirmableCommand {
/**
* Either selects the bundle to use or asks the user to choose.
*
* @since 1.5.1
*/
private void selectBundle(@NonNull User user, @NonNull String label) {
@ -117,6 +119,7 @@ public class IslandResetCommand extends ConfirmableCommand {
/**
* Reset island
*
* @param user user
* @param name name of Blueprint Bundle
* @return true if successful
@ -124,19 +127,15 @@ public class IslandResetCommand extends ConfirmableCommand {
private boolean resetIsland(User user, String name) {
// Get the player's old island
Island oldIsland = getIslands().getIsland(getWorld(), user);
if (oldIsland != null) {
deleteOldIsland(user, oldIsland);
}
deleteOldIsland(user, oldIsland);
user.sendMessage("commands.island.create.creating-island");
// Create new island and then delete the old one
try {
Builder builder = NewIsland.builder()
.player(user)
.reason(Reason.RESET)
.addon(getAddon())
.oldIsland(oldIsland)
.name(name);
if (noPaste) builder.noPaste();
Builder builder = NewIsland.builder().player(user).reason(Reason.RESET).addon(getAddon())
.oldIsland(oldIsland).name(name);
if (noPaste)
builder.noPaste();
builder.build();
} catch (IOException e) {
getPlugin().logError("Could not create island for player. " + e.getMessage());
@ -149,13 +148,8 @@ public class IslandResetCommand extends ConfirmableCommand {
private void deleteOldIsland(User user, Island oldIsland) {
// Fire island preclear event
IslandEvent.builder()
.involvedPlayer(user.getUniqueId())
.reason(Reason.PRECLEAR)
.island(oldIsland)
.oldIsland(oldIsland)
.location(oldIsland.getCenter())
.build();
IslandEvent.builder().involvedPlayer(user.getUniqueId()).reason(Reason.PRECLEAR).island(oldIsland)
.oldIsland(oldIsland).location(oldIsland.getCenter()).build();
// Reset the island
@ -168,33 +162,33 @@ public class IslandResetCommand extends ConfirmableCommand {
/**
* Kicks the members (incl. owner) of the island.
*
* @since 1.7.0
*/
private void kickMembers(Island island) {
/*
* We cannot assume the island owner can run /[cmd] team kick (it might be disabled, or there could be permission restrictions...)
* Therefore, we need to do it manually.
* Plus, a more specific team event (TeamDeleteEvent) is called by this method.
* We cannot assume the island owner can run /[cmd] team kick (it might be
* disabled, or there could be permission restrictions...) Therefore, we need to
* do it manually. Plus, a more specific team event (TeamDeleteEvent) is called
* by this method.
*/
island.getMemberSet().forEach(memberUUID -> {
User member = User.getInstance(memberUUID);
// Send a "you're kicked" message if the member is not the island owner (send before removing!)
// Send a "you're kicked" message if the member is not the island owner (send
// before removing!)
if (!memberUUID.equals(island.getOwner())) {
member.sendMessage("commands.island.reset.kicked-from-island", TextVariables.GAMEMODE, getAddon().getDescription().getName());
member.sendMessage("commands.island.reset.kicked-from-island", TextVariables.GAMEMODE,
getAddon().getDescription().getName());
}
// Remove player
getIslands().removePlayer(getWorld(), memberUUID);
getIslands().removePlayer(island, memberUUID);
// Clean player
getPlayers().cleanLeavingPlayer(getWorld(), member, false, island);
// Fire event
TeamEvent.builder()
.island(island)
.reason(TeamEvent.Reason.DELETE)
.involvedPlayer(memberUUID)
.build();
TeamEvent.builder().island(island).reason(TeamEvent.Reason.DELETE).involvedPlayer(memberUUID).build();
});
}
}

View File

@ -7,6 +7,7 @@ import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.RanksManager;
/**
@ -41,7 +42,7 @@ public class IslandResetnameCommand extends CompositeCommand {
int rank = Objects.requireNonNull(island).getRank(user);
if (rank < island.getRankCommand(getUsage())) {
user.sendMessage("general.errors.insufficient-rank",
TextVariables.RANK, user.getTranslation(getPlugin().getRanksManager().getRank(rank)));
TextVariables.RANK, user.getTranslation(RanksManager.getInstance().getRank(rank)));
return false;
}

View File

@ -46,16 +46,19 @@ public class IslandSethomeCommand extends ConfirmableCommand {
int rank = Objects.requireNonNull(island).getRank(user);
if (rank < island.getRankCommand(getUsage())) {
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK, user.getTranslation(getPlugin().getRanksManager().getRank(rank)));
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK,
user.getTranslation(RanksManager.getInstance().getRank(rank)));
return false;
}
// Check number of homes
int maxHomes = getIslands().getMaxHomes(island);
int maxHomes = getIslands().getIslands(getWorld(), user).stream().mapToInt(getIslands()::getMaxHomes).sum();
if (getIslands().getNumberOfHomesIfAdded(island, String.join(" ", args)) > maxHomes) {
user.sendMessage("commands.island.sethome.too-many-homes", TextVariables.NUMBER, String.valueOf(maxHomes));
user.sendMessage("commands.island.sethome.homes-are");
island.getHomes().keySet().stream().filter(s -> !s.isEmpty()).forEach(s -> user.sendMessage("commands.island.sethome.home-list-syntax", TextVariables.NAME, s));
getIslands().getIslands(getWorld(), user).forEach(is ->
is.getHomes().keySet().stream().filter(s -> !s.isEmpty()).forEach(s -> user.sendMessage("commands.island.sethome.home-list-syntax", TextVariables.NAME, s)));
return false;
}
return true;

View File

@ -10,6 +10,7 @@ import world.bentobox.bentobox.api.events.island.IslandEvent;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.RanksManager;
/**
@ -51,7 +52,8 @@ public class IslandSetnameCommand extends CompositeCommand {
// Check command rank.
int rank = Objects.requireNonNull(island).getRank(user);
if (rank < island.getRankCommand(getUsage())) {
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK, user.getTranslation(getPlugin().getRanksManager().getRank(rank)));
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK,
user.getTranslation(RanksManager.getInstance().getRank(rank)));
return false;
}

View File

@ -47,9 +47,9 @@ public class IslandSettingsCommand extends CompositeCommand {
public boolean execute(User user, String label, List<String> args) {
new TabbedPanelBuilder()
.user(user)
.island(island)
.world(island.getWorld())
.tab(1, new SettingsTab(user, island, Flag.Type.PROTECTION))
.tab(2, new SettingsTab(user, island, Flag.Type.SETTING))
.tab(1, new SettingsTab(user, Flag.Type.PROTECTION)).tab(2, new SettingsTab(user, Flag.Type.SETTING))
.startingSlot(1)
.size(54)
.hideIfEmpty()

View File

@ -13,6 +13,7 @@ import world.bentobox.bentobox.api.events.island.IslandEvent;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.bentobox.util.Util;
/**
@ -54,7 +55,8 @@ public class IslandUnbanCommand extends CompositeCommand {
Island island = getIslands().getIsland(getWorld(), user);
int rank = Objects.requireNonNull(island).getRank(user);
if (rank < island.getRankCommand(getUsage())) {
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK, user.getTranslation(getPlugin().getRanksManager().getRank(rank)));
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK,
user.getTranslation(RanksManager.getInstance().getRank(rank)));
return false;
}
// Get target player

View File

@ -3,6 +3,8 @@ package world.bentobox.bentobox.api.commands.island.team;
import java.util.Objects;
import java.util.UUID;
import world.bentobox.bentobox.database.objects.Island;
/**
* Represents an invite
* @author tastybento
@ -23,16 +25,19 @@ public class Invite {
private final Type type;
private final UUID inviter;
private final UUID invitee;
private final Island island;
/**
* @param type - invitation type, e.g., coop, team, trust
* @param inviter - UUID of inviter
* @param invitee - UUID of invitee
* @param island - the island this invite is for
*/
public Invite(Type type, UUID inviter, UUID invitee) {
public Invite(Type type, UUID inviter, UUID invitee, Island island) {
this.type = type;
this.inviter = inviter;
this.invitee = invitee;
this.island = island;
}
/**
@ -56,6 +61,13 @@ public class Invite {
return invitee;
}
/**
* @return the island
*/
public Island getIsland() {
return island;
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/

View File

@ -0,0 +1,53 @@
package world.bentobox.bentobox.api.commands.island.team;
import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.conversations.ConversationContext;
import org.bukkit.conversations.Prompt;
import org.bukkit.conversations.StringPrompt;
import org.eclipse.jdt.annotation.NonNull;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.user.User;
/**
* Invites a player by search
* @author tastybento
*
*/
public class InviteNamePrompt extends StringPrompt {
@NonNull
private final User user;
@NonNull
private final IslandTeamInviteCommand itic;
public InviteNamePrompt(@NonNull User user, IslandTeamInviteCommand islandTeamInviteCommand) {
this.user = user;
this.itic = islandTeamInviteCommand;
}
@Override
@NonNull
public String getPromptText(@NonNull ConversationContext context) {
return user.getTranslation("commands.island.team.invite.gui.enter-name");
}
@Override
public Prompt acceptInput(@NonNull ConversationContext context, String input) {
// TODO remove this and pass the options back to the GUI
if (itic.canExecute(user, itic.getLabel(), List.of(input))) {
if (itic.execute(user, itic.getLabel(), List.of(input))) {
return Prompt.END_OF_CONVERSATION;
}
}
// Set the search item to what was entered
itic.setSearchName(input);
// Return to the GUI but give a second for the error to show
// TODO: return the failed input and display the options in the GUI.
Bukkit.getScheduler().runTaskLater(BentoBox.getInstance(), () -> itic.build(user), 20L);
return Prompt.END_OF_CONVERSATION;
}
}

View File

@ -1,15 +1,24 @@
package world.bentobox.bentobox.api.commands.island.team;
import java.io.File;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.Sound;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.inventory.ItemStack;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
@ -17,6 +26,15 @@ import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.events.IslandBaseEvent;
import world.bentobox.bentobox.api.events.team.TeamEvent;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.panels.Panel;
import world.bentobox.bentobox.api.panels.PanelItem;
import world.bentobox.bentobox.api.panels.TemplatedPanel;
import world.bentobox.bentobox.api.panels.TemplatedPanel.ItemSlot;
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.ItemTemplateRecord.ActionRecords;
import world.bentobox.bentobox.api.panels.reader.PanelTemplateRecord.TemplateItem;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.RanksManager;
@ -24,12 +42,48 @@ import world.bentobox.bentobox.util.Util;
public class IslandTeamCommand extends CompositeCommand {
/**
* List of ranks that we will loop through in order
*/
private static final List<Integer> RANKS = List.of(RanksManager.OWNER_RANK, RanksManager.SUB_OWNER_RANK,
RanksManager.MEMBER_RANK, RanksManager.TRUSTED_RANK, RanksManager.COOP_RANK);
/**
* Invited list. Key is the invited party, value is the invite.
* @since 1.8.0
*/
private final Map<UUID, Invite> inviteMap;
private User user;
private Island island;
private int rank = RanksManager.OWNER_RANK;
private IslandTeamKickCommand kickCommand;
private IslandTeamLeaveCommand leaveCommand;
private IslandTeamSetownerCommand setOwnerCommand;
private IslandTeamUncoopCommand uncoopCommand;
private IslandTeamUntrustCommand unTrustCommand;
private @Nullable TemplateItem border;
private @Nullable TemplateItem background;
private IslandTeamInviteAcceptCommand acceptCommand;
private IslandTeamInviteRejectCommand rejectCommand;
private IslandTeamInviteCommand inviteCommand;
private IslandTeamCoopCommand coopCommand;
private IslandTeamTrustCommand trustCommand;
public IslandTeamCommand(CompositeCommand parent) {
super(parent, "team");
inviteMap = new HashMap<>();
@ -41,143 +95,575 @@ public class IslandTeamCommand extends CompositeCommand {
setOnlyPlayer(true);
setDescription("commands.island.team.description");
// Register commands
new IslandTeamInviteCommand(this);
new IslandTeamLeaveCommand(this);
new IslandTeamSetownerCommand(this);
new IslandTeamKickCommand(this);
new IslandTeamInviteAcceptCommand(this);
new IslandTeamInviteRejectCommand(this);
new IslandTeamCoopCommand(this);
new IslandTeamUncoopCommand(this);
new IslandTeamTrustCommand(this);
new IslandTeamUntrustCommand(this);
inviteCommand = new IslandTeamInviteCommand(this);
leaveCommand = new IslandTeamLeaveCommand(this);
setOwnerCommand = new IslandTeamSetownerCommand(this);
kickCommand = new IslandTeamKickCommand(this);
acceptCommand = new IslandTeamInviteAcceptCommand(this);
rejectCommand = new IslandTeamInviteRejectCommand(this);
if (RanksManager.getInstance().rankExists(RanksManager.COOP_RANK_REF)) {
coopCommand = new IslandTeamCoopCommand(this);
uncoopCommand = new IslandTeamUncoopCommand(this);
}
if (RanksManager.getInstance().rankExists(RanksManager.TRUSTED_RANK_REF)) {
trustCommand = new IslandTeamTrustCommand(this);
unTrustCommand = new IslandTeamUntrustCommand(this);
}
new IslandTeamPromoteCommand(this, "promote");
new IslandTeamPromoteCommand(this, "demote");
// Panels
if (!new File(getPlugin().getDataFolder() + File.separator + "panels", "team_panel.yml").exists()) {
getPlugin().saveResource("panels/team_panel.yml", false);
}
}
@Override
public boolean execute(User user, String label, List<String> args) {
public boolean canExecute(User user, String label, List<String> args) {
this.user = user;
// Player issuing the command must have an island
UUID ownerUUID = getOwner(getWorld(), user);
if (ownerUUID == null) {
island = getIslands().getPrimaryIsland(getWorld(), user.getUniqueId());
if (island == null) {
if (isInvited(user.getUniqueId())) {
// Player has an invite, so show the invite
build();
return true;
}
user.sendMessage("general.errors.no-island");
return false;
}
UUID playerUUID = user.getUniqueId();
// Fire event so add-ons can run commands, etc.
if (fireEvent(user)) {
if (fireEvent(user, island)) {
// Cancelled
return false;
}
Island island = getIslands().getIsland(getWorld(), playerUUID);
if (island == null) {
return false;
}
Set<UUID> teamMembers = getMembers(getWorld(), user);
if (ownerUUID.equals(playerUUID)) {
if (playerUUID.equals(island.getOwner())) {
int maxSize = getIslands().getMaxMembers(island, RanksManager.MEMBER_RANK);
if (teamMembers.size() < maxSize) {
user.sendMessage("commands.island.team.invite.you-can-invite", TextVariables.NUMBER, String.valueOf(maxSize - teamMembers.size()));
user.sendMessage("commands.island.team.invite.you-can-invite", TextVariables.NUMBER,
String.valueOf(maxSize - teamMembers.size()));
} else {
user.sendMessage("commands.island.team.invite.errors.island-is-full");
}
}
// Show members of island
showMembers(island, user);
return true;
}
private void showMembers(Island island, User user) {
@Override
public boolean execute(User user, String label, List<String> args) {
// Show the panel
build();
return true;
}
/**
* This method builds this GUI.
*/
void build() {
// Start building panel.
TemplatedPanelBuilder panelBuilder = new TemplatedPanelBuilder();
panelBuilder.user(user);
panelBuilder.world(user.getWorld());
panelBuilder.template("team_panel", new File(getPlugin().getDataFolder(), "panels"));
panelBuilder.parameters("[name]", user.getName(), "[display_name]", user.getDisplayName());
panelBuilder.registerTypeBuilder("STATUS", this::createStatusButton);
panelBuilder.registerTypeBuilder("MEMBER", this::createMemberButton);
panelBuilder.registerTypeBuilder("INVITED", this::createInvitedButton);
panelBuilder.registerTypeBuilder("RANK", this::createRankButton);
panelBuilder.registerTypeBuilder("INVITE", this::createInviteButton);
border = panelBuilder.getPanelTemplate().border();
background = panelBuilder.getPanelTemplate().background();
// Register unknown type builder.
panelBuilder.build();
}
private PanelItem createInviteButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) {
if (island == null || !user.hasPermission(this.inviteCommand.getPermission())
|| island.getRank(user) < island.getRankCommand(this.getLabel() + " invite")) {
return this.getBlankBorder();
}
PanelItemBuilder builder = new PanelItemBuilder();
builder.icon(Material.PLAYER_HEAD);
builder.name(user.getTranslation("commands.island.team.gui.buttons.invite.name"));
builder.description(user.getTranslation("commands.island.team.gui.buttons.invite.description"));
builder.clickHandler((panel, user, clickType, clickSlot) -> {
if (!template.actions().stream().anyMatch(ar -> clickType.equals(ar.clickType()))) {
// If the click type is not in the template, don't do anything
return true;
}
if (clickType.equals(ClickType.LEFT)) {
user.closeInventory();
this.inviteCommand.build(user);
}
return true;
});
return builder.build();
}
private PanelItem createRankButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) {
// If there is no island, the do not show this icon
if (island == null) {
return this.getBlankBorder();
}
PanelItemBuilder builder = new PanelItemBuilder();
builder.name(user.getTranslation("commands.island.team.gui.buttons.rank-filter.name"));
builder.icon(Material.AMETHYST_SHARD);
// Create description
RanksManager.getInstance().getRanks().forEach((reference, score) -> {
if (rank == RanksManager.OWNER_RANK && score > RanksManager.VISITOR_RANK
&& score <= RanksManager.OWNER_RANK) {
builder.description(user.getTranslation("protection.panel.flag-item.allowed-rank")
+ user.getTranslation(reference));
} else if (score > RanksManager.VISITOR_RANK && score < rank) {
builder.description(user.getTranslation("protection.panel.flag-item.blocked-rank")
+ user.getTranslation(reference));
} else if (score <= RanksManager.OWNER_RANK && score > rank) {
builder.description(user.getTranslation("protection.panel.flag-item.blocked-rank")
+ user.getTranslation(reference));
} else if (score == rank) {
builder.description(user.getTranslation("protection.panel.flag-item.allowed-rank")
+ user.getTranslation(reference));
}
});
builder.description(user.getTranslation("commands.island.team.gui.buttons.rank-filter.description"));
builder.clickHandler((panel, user, clickType, clickSlot) -> {
if (!template.actions().stream().anyMatch(ar -> clickType.equals(ar.clickType()))) {
// If the click type is not in the template, don't do anything
return true;
}
if (clickType.equals(ClickType.LEFT)) {
rank = RanksManager.getInstance().getRankDownValue(rank);
if (rank <= RanksManager.VISITOR_RANK) {
rank = RanksManager.OWNER_RANK;
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_METAL_HIT, 1F, 1F);
} else {
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1F, 1F);
}
}
if (clickType.equals(ClickType.RIGHT)) {
rank = RanksManager.getInstance().getRankUpValue(rank);
if (rank >= RanksManager.OWNER_RANK) {
rank = RanksManager.getInstance().getRankUpValue(RanksManager.VISITOR_RANK);
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_METAL_HIT, 1F, 1F);
} else {
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1F, 1F);
}
}
// Update panel after click
build();
return true;
});
return builder.build();
}
/**
* Create invited button panel item.
*
* @param template the template
* @param slot the slot
* @return the panel item
*/
private PanelItem createInvitedButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) {
PanelItemBuilder builder = new PanelItemBuilder();
if (isInvited(user.getUniqueId()) && user.hasPermission(this.acceptCommand.getPermission())) {
Invite invite = getInvite(user.getUniqueId());
User inviter = User.getInstance(invite.getInviter());
String name = inviter.getName();
builder.icon(inviter.getName());
builder.name(user.getTranslation("commands.island.team.gui.buttons.invitation"));
builder.description(switch (invite.getType()) {
case COOP ->
List.of(user.getTranslation("commands.island.team.invite.name-has-invited-you.coop", TextVariables.NAME,
name));
case TRUST ->
List.of(user.getTranslation("commands.island.team.invite.name-has-invited-you.trust",
TextVariables.NAME, name));
default ->
List.of(user.getTranslation("commands.island.team.invite.name-has-invited-you", TextVariables.NAME,
name), user.getTranslation("commands.island.team.invite.accept.confirmation"));
});
// Add all the tool tips
builder.description(template.actions().stream()
.map(ar -> user.getTranslation("commands.island.team.gui.tips." + ar.clickType().name() + ".name")
+ " "
+ user.getTranslation(ar.tooltip()))
.toList());
builder.clickHandler((panel, user, clickType, clickSlot) -> {
if (!template.actions().stream().anyMatch(ar -> clickType.equals(ar.clickType()))) {
// If the click type is not in the template, don't do anything
return true;
}
if (clickType.equals(ClickType.SHIFT_LEFT) && user.hasPermission(this.acceptCommand.getPermission())) {
getPlugin().log("Invite accepted: " + user.getName() + " accepted " + invite.getType()
+ " invite to island at " + island.getCenter());
// Accept
switch (invite.getType()) {
case COOP -> this.acceptCommand.acceptCoopInvite(user, invite);
case TRUST -> this.acceptCommand.acceptTrustInvite(user, invite);
default -> this.acceptCommand.acceptTeamInvite(user, invite);
}
user.closeInventory();
}
if (clickType.equals(ClickType.SHIFT_RIGHT) && user.hasPermission(this.rejectCommand.getPermission())) {
// Reject
getPlugin().log("Invite rejected: " + user.getName() + " rejected " + invite.getType()
+ " invite.");
this.rejectCommand.execute(user, "", List.of());
user.closeInventory();
}
return true;
});
} else {
return this.getBlankBorder();
}
return builder.build();
}
/**
* Create status button panel item.
*
* @param template the template
* @param slot the slot
* @return the panel item
*/
private PanelItem createStatusButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) {
PanelItemBuilder builder = new PanelItemBuilder();
// Player issuing the command must have an island
Island island = getIslands().getPrimaryIsland(getWorld(), user.getUniqueId());
if (island == null) {
return getBlankBorder();
}
return builder.icon(user.getName()).name(user.getTranslation("commands.island.team.gui.buttons.status.name"))
.description(showMembers()).build();
}
private PanelItem getBlankBorder() {
return new PanelItemBuilder().icon(Objects.requireNonNullElse(border.icon(), new ItemStack(Material.BARRIER)))
.name((Objects.requireNonNullElse(border.title(), ""))).build();
}
private PanelItem getBlankBackground() {
return new PanelItemBuilder()
.icon(Objects.requireNonNullElse(background.icon(), new ItemStack(Material.BARRIER)))
.name((Objects.requireNonNullElse(background.title(), ""))).build();
}
/**
* Create member button panel item.
*
* @param template the template
* @param slot the slot
* @return the panel item
*/
private PanelItem createMemberButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) {
// Player issuing the command must have an island
Island island = getIslands().getPrimaryIsland(getWorld(), user.getUniqueId());
if (island == null) {
return this.getBlankBackground();
}
return switch (rank) {
case RanksManager.OWNER_RANK -> ownerView(template, slot);
default -> getMemberButton(rank, slot.slot(), template.actions());
};
}
/**
* The owner view shows all the ranks, in order
* @param template template reference
* @param slot slot to show
* @return panel item
*/
private PanelItem ownerView(ItemTemplateRecord template, ItemSlot slot) {
if (slot.slot() == 0 && island.getOwner() != null) {
// Owner
PanelItem item = getMemberButton(RanksManager.OWNER_RANK, 1, template.actions());
if (item != null) {
return item;
}
}
long subOwnerCount = island.getMemberSet(RanksManager.SUB_OWNER_RANK, false).stream().count();
long memberCount = island.getMemberSet(RanksManager.MEMBER_RANK, false).stream().count();
long coopCount = island.getMemberSet(RanksManager.COOP_RANK, false).stream().count();
long trustedCount = island.getMemberSet(RanksManager.TRUSTED_RANK, false).stream().count();
if (slot.slot() > 0 && slot.slot() < subOwnerCount + 1) {
// Show sub owners
PanelItem item = getMemberButton(RanksManager.SUB_OWNER_RANK, slot.slot(), template.actions());
if (item != null) {
return item;
}
}
if (slot.slot() > subOwnerCount && slot.slot() < subOwnerCount + memberCount + 1) {
// Show members
PanelItem item = getMemberButton(RanksManager.MEMBER_RANK, slot.slot(), template.actions());
if (item != null) {
return item;
}
}
if (slot.slot() > subOwnerCount + memberCount && slot.slot() < subOwnerCount + memberCount + trustedCount + 1) {
// Show trusted
PanelItem item = getMemberButton(RanksManager.TRUSTED_RANK, slot.slot(), template.actions());
if (item != null) {
return item;
}
}
if (slot.slot() > subOwnerCount + memberCount + trustedCount
&& slot.slot() < subOwnerCount + memberCount + trustedCount + coopCount + 1) {
// Show coops
return getMemberButton(RanksManager.COOP_RANK, slot.slot(), template.actions());
}
return this.getBlankBackground();
}
/**
* Shows a member's head. The clicks available will depend on who is viewing.
* @param targetRank - the rank to show
* @param slot - the slot number
* @param actions - actions that need to apply to this member button as provided by the template
* @return panel item
*/
private PanelItem getMemberButton(int targetRank, int slot, List<ActionRecords> actions) {
if (slot == 0 && island.getOwner() != null) {
// Owner
return getMemberButton(RanksManager.OWNER_RANK, 1, actions);
}
String ref = RanksManager.getInstance().getRank(targetRank);
Optional<User> opMember = island.getMemberSet(targetRank, false).stream().sorted().skip(slot - 1L).limit(1L)
.map(User::getInstance).findFirst();
if (opMember.isEmpty()) {
return this.getBlankBackground();
}
User member = opMember.get();
// Make button description depending on viewer
List<String> desc = new ArrayList<>();
int userRank = Objects.requireNonNull(island).getRank(user);
// Add the tooltip for kicking
if (user.hasPermission(this.kickCommand.getPermission())
&& userRank >= island.getRankCommand(this.getLabel() + " kick") && !user.equals(member)) {
actions.stream().filter(ar -> ar.actionType().equalsIgnoreCase("kick"))
.map(ar -> user.getTranslation("commands.island.team.gui.tips." + ar.clickType().name() + ".name")
+ " " + user.getTranslation(ar.tooltip()))
.findFirst().ifPresent(desc::add);
}
// Set Owner
if (user.hasPermission(this.setOwnerCommand.getPermission()) && !user.equals(member)
&& userRank >= RanksManager.OWNER_RANK && targetRank >= RanksManager.MEMBER_RANK) {
// Add the tooltip for setowner
actions.stream().filter(ar -> ar.actionType().equalsIgnoreCase("setowner"))
.map(ar -> user.getTranslation("commands.island.team.gui.tips." + ar.clickType().name() + ".name")
+ " " + user.getTranslation(ar.tooltip()))
.findFirst().ifPresent(desc::add);
}
// Leave
if (user.hasPermission(this.leaveCommand.getPermission()) && user.equals(member)
&& userRank < RanksManager.OWNER_RANK) {
// Add the tooltip for leave
actions.stream().filter(ar -> ar.actionType().equalsIgnoreCase("leave"))
.map(ar -> user.getTranslation("commands.island.team.gui.tips." + ar.clickType().name() + ".name")
+ " " + user.getTranslation(ar.tooltip()))
.findFirst().ifPresent(desc::add);
}
if (member.isOnline()) {
desc.add(0, user.getTranslation(ref));
return new PanelItemBuilder().icon(member.getName()).name(member.getDisplayName()).description(desc)
.clickHandler(
(panel, user, clickType, i) -> clickListener(panel, user, clickType, i, member, actions))
.build();
} else {
// Offline player
desc.add(0, user.getTranslation(ref));
return new PanelItemBuilder().icon(member.getName())
.name(offlinePlayerStatus(user, Bukkit.getOfflinePlayer(member.getUniqueId()))).description(desc)
.clickHandler(
(panel, user, clickType, i) -> clickListener(panel, user, clickType, i, member, actions))
.build();
}
}
private boolean clickListener(Panel panel, User clickingUser, ClickType clickType, int i, User target,
List<ActionRecords> actions) {
if (!actions.stream().anyMatch(ar -> clickType.equals(ar.clickType()))) {
// If the click type is not in the template, don't do anything
return true;
}
int rank = Objects.requireNonNull(island).getRank(clickingUser);
for (ItemTemplateRecord.ActionRecords action : actions) {
if (clickType.equals(action.clickType())) {
switch (action.actionType().toUpperCase(Locale.ENGLISH)) {
case "KICK" -> {
// Kick the player, or uncoop, or untrust
if (clickingUser.hasPermission(this.kickCommand.getPermission()) && !target.equals(clickingUser)
&& rank >= island.getRankCommand(this.getLabel() + " kick")) {
getPlugin().log("Kick: " + clickingUser.getName() + " kicked " + target.getName()
+ " from island at " + island.getCenter());
clickingUser.closeInventory();
if (removePlayer(clickingUser, target)) {
clickingUser.getPlayer().playSound(clickingUser.getLocation(), Sound.BLOCK_GLASS_BREAK, 1F,
1F);
getPlugin().log("Kick: success");
} else {
getPlugin().log("Kick: failed");
}
}
}
case "SETOWNER" -> {
// Make the player the leader of the island
if (clickingUser.hasPermission(this.setOwnerCommand.getPermission()) && !target.equals(clickingUser)
&& clickingUser.getUniqueId().equals(island.getOwner())) {
getPlugin().log("Set Owner: " + clickingUser.getName() + " trying to make " + target.getName()
+ " owner of island at " + island.getCenter());
clickingUser.closeInventory();
if (this.setOwnerCommand.setOwner(clickingUser, target.getUniqueId())) {
getPlugin().log("Set Owner: success");
} else {
getPlugin().log("Set Owner: failed");
}
}
}
case "LEAVE" -> {
if (clickingUser.hasPermission(this.leaveCommand.getPermission()) && target.equals(clickingUser)
&& !clickingUser.getUniqueId().equals(island.getOwner())) {
getPlugin().log("Leave: " + clickingUser.getName() + " trying to leave island at "
+ island.getCenter());
clickingUser.closeInventory();
if (leaveCommand.leave(clickingUser)) {
getPlugin().log("Leave: success");
} else {
getPlugin().log("Leave: failed");
}
}
}
}
}
}
return true;
}
private boolean removePlayer(User clicker, User member) {
// If member then kick, if coop, uncoop, if trusted, then untrust
return switch (island.getRank(member)) {
case RanksManager.COOP_RANK -> this.uncoopCommand.unCoopCmd(user, member.getUniqueId());
case RanksManager.TRUSTED_RANK -> this.unTrustCommand.unTrustCmd(user, member.getUniqueId());
default -> {
if (kickCommand.canExecute(user, kickCommand.getLabel(), List.of(member.getName()))) {
yield kickCommand.execute(user, kickCommand.getLabel(), List.of(member.getName()));
} else {
yield false;
}
}
};
}
private List<String> showMembers() {
List<String> message = new ArrayList<>();
// Gather online members
long count = island
.getMemberSet(RanksManager.MEMBER_RANK)
.stream()
long onlineMemberCount = island.getMemberSet(RanksManager.MEMBER_RANK).stream()
.filter(uuid -> Util.getOnlinePlayerList(user).contains(Bukkit.getOfflinePlayer(uuid).getName()))
.count();
// List of ranks that we will loop through
Integer[] ranks = new Integer[]{RanksManager.OWNER_RANK, RanksManager.SUB_OWNER_RANK, RanksManager.MEMBER_RANK, RanksManager.TRUSTED_RANK, RanksManager.COOP_RANK};
// Show header:
user.sendMessage("commands.island.team.info.header",
"[max]", String.valueOf(getIslands().getMaxMembers(island, RanksManager.MEMBER_RANK)),
"[total]", String.valueOf(island.getMemberSet().size()),
"[online]", String.valueOf(count));
message.add(user.getTranslation("commands.island.team.info.header", "[max]",
String.valueOf(getIslands().getMaxMembers(island, RanksManager.MEMBER_RANK)), "[total]",
String.valueOf(island.getMemberSet().size()), "[online]", String.valueOf(onlineMemberCount)));
// We now need to get all online "members" of the island - incl. Trusted and coop
List<UUID> onlineMembers = island.getMemberSet(RanksManager.COOP_RANK).stream()
.filter(uuid -> Util.getOnlinePlayerList(user).contains(Bukkit.getOfflinePlayer(uuid).getName())).toList();
.filter(uuid -> Util.getOnlinePlayerList(user).contains(Bukkit.getOfflinePlayer(uuid).getName()))
.toList();
for (int rank : ranks) {
for (int rank : RANKS) {
Set<UUID> players = island.getMemberSet(rank, false);
if (!players.isEmpty()) {
if (rank == RanksManager.OWNER_RANK) {
// Slightly special handling for the owner rank
user.sendMessage("commands.island.team.info.rank-layout.owner",
TextVariables.RANK, user.getTranslation(RanksManager.OWNER_RANK_REF));
message.add(user.getTranslation("commands.island.team.info.rank-layout.owner", TextVariables.RANK,
user.getTranslation(RanksManager.OWNER_RANK_REF)));
} else {
user.sendMessage("commands.island.team.info.rank-layout.generic",
TextVariables.RANK, user.getTranslation(getPlugin().getRanksManager().getRank(rank)),
TextVariables.NUMBER, String.valueOf(island.getMemberSet(rank, false).size()));
message.add(user.getTranslation("commands.island.team.info.rank-layout.generic", TextVariables.RANK,
user.getTranslation(RanksManager.getInstance().getRank(rank)), TextVariables.NUMBER,
String.valueOf(island.getMemberSet(rank, false).size())));
}
displayOnOffline(user, rank, island, onlineMembers);
message.addAll(displayOnOffline(user, rank, island, onlineMembers));
}
}
return message;
}
private void displayOnOffline(User user, int rank, Island island, List<UUID> onlineMembers) {
private List<String> displayOnOffline(User user, int rank, Island island, List<UUID> onlineMembers) {
List<String> message = new ArrayList<>();
for (UUID member : island.getMemberSet(rank, false)) {
OfflinePlayer offlineMember = Bukkit.getOfflinePlayer(member);
if (onlineMembers.contains(member)) {
// the player is online
user.sendMessage("commands.island.team.info.member-layout.online",
TextVariables.NAME, offlineMember.getName());
} else {
// A bit of handling for the last joined date
Instant lastJoined = Instant.ofEpochMilli(offlineMember.getLastPlayed());
Instant now = Instant.now();
message.add(getMemberStatus(user, member, onlineMembers.contains(member)));
Duration duration = Duration.between(lastJoined, now);
String lastSeen;
final String reference = "commands.island.team.info.last-seen.layout";
if (duration.toMinutes() < 60L) {
lastSeen = user.getTranslation(reference,
TextVariables.NUMBER, String.valueOf(duration.toMinutes()),
TextVariables.UNIT, user.getTranslation("commands.island.team.info.last-seen.minutes"));
} else if (duration.toHours() < 24L) {
lastSeen = user.getTranslation(reference,
TextVariables.NUMBER, String.valueOf(duration.toHours()),
TextVariables.UNIT, user.getTranslation("commands.island.team.info.last-seen.hours"));
} else {
lastSeen = user.getTranslation(reference,
TextVariables.NUMBER, String.valueOf(duration.toDays()),
TextVariables.UNIT, user.getTranslation("commands.island.team.info.last-seen.days"));
}
if(island.getMemberSet(RanksManager.MEMBER_RANK, true).contains(member)) {
user.sendMessage("commands.island.team.info.member-layout.offline",
TextVariables.NAME, offlineMember.getName(),
"[last_seen]", lastSeen);
} else {
// This will prevent anyone that is trusted or below to not have a last-seen status
user.sendMessage("commands.island.team.info.member-layout.offline-not-last-seen",
TextVariables.NAME, offlineMember.getName());
}
}
}
return message;
}
private boolean fireEvent(User user) {
IslandBaseEvent e = TeamEvent.builder()
.island(getIslands()
.getIsland(getWorld(), user.getUniqueId()))
.reason(TeamEvent.Reason.INFO)
.involvedPlayer(user.getUniqueId())
.build();
return e.getNewEvent().map(IslandBaseEvent::isCancelled)
.orElse(e.isCancelled());
private String getMemberStatus(User user2, UUID member, boolean online) {
OfflinePlayer offlineMember = Bukkit.getOfflinePlayer(member);
if (online) {
return user.getTranslation("commands.island.team.info.member-layout.online", TextVariables.NAME,
offlineMember.getName());
} else {
return offlinePlayerStatus(user, offlineMember);
}
}
/**
* Creates text to describe the status of the player
* @param user2 user asking to see the status
* @param offlineMember member of the team
* @return string
*/
private String offlinePlayerStatus(User user2, OfflinePlayer offlineMember) {
String lastSeen = lastSeen(offlineMember);
if (island.getMemberSet(RanksManager.MEMBER_RANK, true).contains(offlineMember.getUniqueId())) {
return user.getTranslation("commands.island.team.info.member-layout.offline", TextVariables.NAME,
offlineMember.getName(), "[last_seen]", lastSeen);
} else {
// This will prevent anyone that is trusted or below to not have a last-seen status
return user.getTranslation("commands.island.team.info.member-layout.offline-not-last-seen",
TextVariables.NAME, offlineMember.getName());
}
}
private String lastSeen(OfflinePlayer offlineMember) {
// A bit of handling for the last joined date
Instant lastJoined = Instant.ofEpochMilli(offlineMember.getLastPlayed());
Instant now = Instant.now();
Duration duration = Duration.between(lastJoined, now);
String lastSeen;
final String reference = "commands.island.team.info.last-seen.layout";
if (duration.toMinutes() < 60L) {
lastSeen = user.getTranslation(reference, TextVariables.NUMBER, String.valueOf(duration.toMinutes()),
TextVariables.UNIT, user.getTranslation("commands.island.team.info.last-seen.minutes"));
} else if (duration.toHours() < 24L) {
lastSeen = user.getTranslation(reference, TextVariables.NUMBER, String.valueOf(duration.toHours()),
TextVariables.UNIT, user.getTranslation("commands.island.team.info.last-seen.hours"));
} else {
lastSeen = user.getTranslation(reference, TextVariables.NUMBER, String.valueOf(duration.toDays()),
TextVariables.UNIT, user.getTranslation("commands.island.team.info.last-seen.days"));
}
return lastSeen;
}
private boolean fireEvent(User user, Island island) {
IslandBaseEvent e = TeamEvent.builder().island(island).reason(TeamEvent.Reason.INFO)
.involvedPlayer(user.getUniqueId()).build();
return e.getNewEvent().map(IslandBaseEvent::isCancelled).orElse(e.isCancelled());
}
/**
@ -187,8 +673,8 @@ public class IslandTeamCommand extends CompositeCommand {
* @param invitee - uuid of invitee
* @since 1.8.0
*/
public void addInvite(Invite.Type type, @NonNull UUID inviter, @NonNull UUID invitee) {
inviteMap.put(invitee, new Invite(type, inviter, invitee));
public void addInvite(Invite.Type type, @NonNull UUID inviter, @NonNull UUID invitee, @NonNull Island island) {
inviteMap.put(invitee, new Invite(type, inviter, invitee, island));
}
/**
@ -231,4 +717,18 @@ public class IslandTeamCommand extends CompositeCommand {
public void removeInvite(@NonNull UUID invitee) {
inviteMap.remove(invitee);
}
/**
* @return the coopCommand
*/
protected IslandTeamCoopCommand getCoopCommand() {
return coopCommand;
}
/**
* @return the trustCommand
*/
protected IslandTeamTrustCommand getTrustCommand() {
return trustCommand;
}
}

View File

@ -17,6 +17,7 @@ import world.bentobox.bentobox.util.Util;
/**
* Command to coop another player
*
* @author tastybento
*
*/
@ -47,7 +48,8 @@ public class IslandTeamCoopCommand extends CompositeCommand {
return false;
}
// Player issuing the command must have an island or be in a team
if (!getIslands().inTeam(getWorld(), user.getUniqueId()) && !getIslands().hasIsland(getWorld(), user.getUniqueId())) {
if (!getIslands().inTeam(getWorld(), user.getUniqueId())
&& !getIslands().hasIsland(getWorld(), user.getUniqueId())) {
user.sendMessage("general.errors.no-island");
return false;
}
@ -55,7 +57,8 @@ public class IslandTeamCoopCommand extends CompositeCommand {
Island island = getIslands().getIsland(getWorld(), user);
int rank = Objects.requireNonNull(island).getRank(user);
if (rank < island.getRankCommand(getUsage())) {
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK, user.getTranslation(getPlugin().getRanksManager().getRank(rank)));
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK,
user.getTranslation(RanksManager.getInstance().getRank(rank)));
return false;
}
// Get target player
@ -73,11 +76,13 @@ public class IslandTeamCoopCommand extends CompositeCommand {
user.sendMessage("commands.island.team.coop.cannot-coop-yourself");
return false;
}
if (getIslands().getMembers(getWorld(), user.getUniqueId(), RanksManager.COOP_RANK).contains(targetUUID)) {
if (getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()).getMemberSet(RanksManager.COOP_RANK)
.contains(targetUUID)) {
user.sendMessage("commands.island.team.coop.already-has-rank");
return false;
}
if (itc.isInvited(targetUUID) && itc.getInviter(targetUUID).equals(user.getUniqueId()) && itc.getInvite(targetUUID).getType().equals(Type.COOP)) {
if (itc.isInvited(targetUUID) && user.getUniqueId().equals(itc.getInviter(targetUUID))
&& itc.getInvite(targetUUID) != null && itc.getInvite(targetUUID).getType().equals(Type.COOP)) {
// Prevent spam
user.sendMessage("commands.island.team.invite.errors.you-have-already-invited");
return false;
@ -92,21 +97,27 @@ public class IslandTeamCoopCommand extends CompositeCommand {
if (island != null) {
if (getPlugin().getSettings().isInviteConfirmation()) {
// Put the invited player (key) onto the list with inviter (value)
// If someone else has invited a player, then this invite will overwrite the previous invite!
itc.addInvite(Invite.Type.COOP, user.getUniqueId(), target.getUniqueId());
// If someone else has invited a player, then this invite will overwrite the
// previous invite!
itc.addInvite(Invite.Type.COOP, user.getUniqueId(), target.getUniqueId(), island);
user.sendMessage("commands.island.team.invite.invitation-sent", TextVariables.NAME, target.getName());
// Send message to online player
target.sendMessage("commands.island.team.coop.name-has-invited-you", TextVariables.NAME, user.getName());
target.sendMessage("commands.island.team.invite.to-accept-or-reject", TextVariables.LABEL, getTopLabel());
target.sendMessage("commands.island.team.coop.name-has-invited-you", TextVariables.NAME,
user.getName());
target.sendMessage("commands.island.team.invite.to-accept-or-reject", TextVariables.LABEL,
getTopLabel());
} else {
if (island.getMemberSet(RanksManager.COOP_RANK, false).size() >= getIslands().getMaxMembers(island, RanksManager.COOP_RANK)) {
if (island.getMemberSet(RanksManager.COOP_RANK, false).size() >= getIslands().getMaxMembers(island,
RanksManager.COOP_RANK)) {
user.sendMessage("commands.island.team.coop.is-full");
return false;
}
island.setRank(target, RanksManager.COOP_RANK);
user.sendMessage("commands.island.team.coop.success", TextVariables.NAME, target.getName(), TextVariables.DISPLAY_NAME, target.getDisplayName());
target.sendMessage("commands.island.team.coop.you-are-a-coop-member", TextVariables.NAME, user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName());
user.sendMessage("commands.island.team.coop.success", TextVariables.NAME, target.getName(),
TextVariables.DISPLAY_NAME, target.getDisplayName());
target.sendMessage("commands.island.team.coop.you-are-a-coop-member", TextVariables.NAME,
user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName());
}
return true;
} else {
@ -118,7 +129,7 @@ public class IslandTeamCoopCommand extends CompositeCommand {
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
String lastArg = !args.isEmpty() ? args.get(args.size() - 1) : "";
if (lastArg.isEmpty()) {
// Don't show every player on the server. Require at least the first letter
return Optional.empty();

View File

@ -1,6 +1,7 @@
package world.bentobox.bentobox.api.commands.island.team;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import world.bentobox.bentobox.api.commands.CompositeCommand;
@ -22,8 +23,6 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
private static final String INVALID_INVITE = "commands.island.team.invite.errors.invalid-invite";
private final IslandTeamCommand itc;
private UUID playerUUID;
private UUID prospectiveOwnerUUID;
public IslandTeamInviteAcceptCommand(IslandTeamCommand islandTeamCommand) {
super(islandTeamCommand, "accept");
@ -39,14 +38,14 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
@Override
public boolean canExecute(User user, String label, List<String> args) {
playerUUID = user.getUniqueId();
UUID playerUUID = user.getUniqueId();
// Check if player has been invited
if (!itc.isInvited(playerUUID)) {
user.sendMessage("commands.island.team.invite.errors.none-invited-you");
return false;
}
// Get the island owner
prospectiveOwnerUUID = itc.getInviter(playerUUID);
UUID prospectiveOwnerUUID = itc.getInviter(playerUUID);
if (prospectiveOwnerUUID == null) {
user.sendMessage(INVALID_INVITE);
return false;
@ -68,11 +67,8 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
return false;
}
// Fire event so add-ons can run commands, etc.
IslandBaseEvent e = TeamEvent.builder()
.island(getIslands().getIsland(getWorld(), prospectiveOwnerUUID))
.reason(TeamEvent.Reason.JOIN)
.involvedPlayer(playerUUID)
.build();
IslandBaseEvent e = TeamEvent.builder().island(getIslands().getIsland(getWorld(), prospectiveOwnerUUID))
.reason(TeamEvent.Reason.JOIN).involvedPlayer(playerUUID).build();
return !e.getNewEvent().map(IslandBaseEvent::isCancelled).orElse(e.isCancelled());
}
@ -82,7 +78,7 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
@Override
public boolean execute(User user, String label, List<String> args) {
// Get the invite
Invite invite = itc.getInvite(playerUUID);
Invite invite = itc.getInvite(user.getUniqueId());
switch (invite.getType()) {
case COOP -> askConfirmation(user, () -> acceptCoopInvite(user, invite));
case TRUST -> askConfirmation(user, () -> acceptTrustInvite(user, invite));
@ -92,87 +88,85 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
return true;
}
private void acceptTrustInvite(User user, Invite invite) {
void acceptTrustInvite(User user, Invite invite) {
// Remove the invite
itc.removeInvite(playerUUID);
itc.removeInvite(user.getUniqueId());
User inviter = User.getInstance(invite.getInviter());
Island island = getIslands().getIsland(getWorld(), inviter);
Island island = invite.getIsland();
if (island != null) {
if (island.getMemberSet(RanksManager.TRUSTED_RANK, false).size() > getIslands().getMaxMembers(island, RanksManager.TRUSTED_RANK)) {
if (island.getMemberSet(RanksManager.TRUSTED_RANK, false).size() > getIslands().getMaxMembers(island,
RanksManager.TRUSTED_RANK)) {
user.sendMessage("commands.island.team.trust.is-full");
return;
}
island.setRank(user, RanksManager.TRUSTED_RANK);
IslandEvent.builder()
.island(island)
.involvedPlayer(user.getUniqueId())
.admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(island.getRank(user), RanksManager.TRUSTED_RANK)
.build();
IslandEvent.builder().island(island).involvedPlayer(user.getUniqueId()).admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE).rankChange(island.getRank(user), RanksManager.TRUSTED_RANK)
.build();
if (inviter.isOnline()) {
inviter.sendMessage("commands.island.team.trust.success", TextVariables.NAME, user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName());
inviter.sendMessage("commands.island.team.trust.success", TextVariables.NAME, user.getName(),
TextVariables.DISPLAY_NAME, user.getDisplayName());
}
if (inviter.isPlayer()) {
user.sendMessage("commands.island.team.trust.you-are-trusted", TextVariables.NAME, inviter.getName(), TextVariables.DISPLAY_NAME, inviter.getDisplayName());
user.sendMessage("commands.island.team.trust.you-are-trusted", TextVariables.NAME, inviter.getName(),
TextVariables.DISPLAY_NAME, inviter.getDisplayName());
}
}
}
private void acceptCoopInvite(User user, Invite invite) {
void acceptCoopInvite(User user, Invite invite) {
// Remove the invite
itc.removeInvite(playerUUID);
itc.removeInvite(user.getUniqueId());
User inviter = User.getInstance(invite.getInviter());
Island island = getIslands().getIsland(getWorld(), inviter);
Island island = invite.getIsland();
if (island != null) {
if (island.getMemberSet(RanksManager.COOP_RANK, false).size() > getIslands().getMaxMembers(island, RanksManager.COOP_RANK)) {
if (island.getMemberSet(RanksManager.COOP_RANK, false).size() > getIslands().getMaxMembers(island,
RanksManager.COOP_RANK)) {
user.sendMessage("commands.island.team.coop.is-full");
return;
}
island.setRank(user, RanksManager.COOP_RANK);
IslandEvent.builder()
.island(island)
.involvedPlayer(user.getUniqueId())
.admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(island.getRank(user), RanksManager.COOP_RANK)
.build();
IslandEvent.builder().island(island).involvedPlayer(user.getUniqueId()).admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE).rankChange(island.getRank(user), RanksManager.COOP_RANK)
.build();
if (inviter.isOnline()) {
inviter.sendMessage("commands.island.team.coop.success", TextVariables.NAME, user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName());
inviter.sendMessage("commands.island.team.coop.success", TextVariables.NAME, user.getName(),
TextVariables.DISPLAY_NAME, user.getDisplayName());
}
if (inviter.isPlayer()) {
user.sendMessage("commands.island.team.coop.you-are-a-coop-member", TextVariables.NAME, inviter.getName(), TextVariables.DISPLAY_NAME, inviter.getDisplayName());
user.sendMessage("commands.island.team.coop.you-are-a-coop-member", TextVariables.NAME,
inviter.getName(), TextVariables.DISPLAY_NAME, inviter.getDisplayName());
}
}
}
private void acceptTeamInvite(User user, Invite invite) {
void acceptTeamInvite(User user, Invite invite) {
// Remove the invite
itc.removeInvite(playerUUID);
itc.removeInvite(user.getUniqueId());
// Get the player's island - may be null if the player has no island
Island island = getIslands().getIsland(getWorld(), playerUUID);
Set<Island> islands = getIslands().getIslands(getWorld(), user.getUniqueId());
// Get the team's island
Island teamIsland = getIslands().getIsland(getWorld(), prospectiveOwnerUUID);
Island teamIsland = invite.getIsland();
if (teamIsland == null) {
user.sendMessage(INVALID_INVITE);
return;
}
if (teamIsland.getMemberSet(RanksManager.MEMBER_RANK, true).size() >= getIslands().getMaxMembers(teamIsland, RanksManager.MEMBER_RANK)) {
if (teamIsland.getMemberSet(RanksManager.MEMBER_RANK, true).size() >= getIslands().getMaxMembers(teamIsland,
RanksManager.MEMBER_RANK)) {
user.sendMessage("commands.island.team.invite.errors.island-is-full");
return;
}
// Remove player as owner of the old island
getIslands().removePlayer(getWorld(), playerUUID);
getIslands().removePlayer(getWorld(), user.getUniqueId());
// Remove money inventory etc. for leaving
cleanPlayer(user);
// Add the player as a team member of the new island
getIslands().setJoinTeam(teamIsland, playerUUID);
getIslands().setJoinTeam(teamIsland, user.getUniqueId());
// Move player to team's island
getIslands().homeTeleportAsync(getWorld(), user.getPlayer()).thenRun(() -> {
// Delete the old island
if (island != null) {
getIslands().deleteIsland(island, true, user.getUniqueId());
}
// Delete the old islands
islands.forEach(island -> getIslands().deleteIsland(island, true, user.getUniqueId()));
// Put player back into normal mode
user.setGameMode(getIWM().getDefaultGameMode(getWorld()));
@ -183,27 +177,21 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
});
// Reset deaths
if (getIWM().isTeamJoinDeathReset(getWorld())) {
getPlayers().setDeaths(getWorld(), playerUUID, 0);
getPlayers().setDeaths(getWorld(), user.getUniqueId(), 0);
}
user.sendMessage("commands.island.team.invite.accept.you-joined-island", TextVariables.LABEL, getTopLabel());
User inviter = User.getInstance(invite.getInviter());
if (inviter.isOnline()) {
inviter.sendMessage("commands.island.team.invite.accept.name-joined-your-island", TextVariables.NAME, user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName());
inviter.sendMessage("commands.island.team.invite.accept.name-joined-your-island", TextVariables.NAME,
user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName());
}
getIslands().save(teamIsland);
// Fire event
TeamEvent.builder()
.island(getIslands().getIsland(getWorld(), prospectiveOwnerUUID))
.reason(TeamEvent.Reason.JOINED)
.involvedPlayer(playerUUID)
.build();
IslandEvent.builder()
.island(teamIsland)
.involvedPlayer(user.getUniqueId())
.admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(teamIsland.getRank(user), RanksManager.MEMBER_RANK)
.build();
TeamEvent.builder().island(teamIsland).reason(TeamEvent.Reason.JOINED).involvedPlayer(user.getUniqueId())
.build();
IslandEvent.builder().island(teamIsland).involvedPlayer(user.getUniqueId()).admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE).rankChange(teamIsland.getRank(user), RanksManager.MEMBER_RANK)
.build();
}
private void cleanPlayer(User user) {
@ -213,7 +201,8 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
if (getIWM().isOnLeaveResetInventory(getWorld()) || getIWM().isOnJoinResetInventory(getWorld())) {
user.getPlayer().getInventory().clear();
}
if (getSettings().isUseEconomy() && (getIWM().isOnLeaveResetMoney(getWorld()) || getIWM().isOnJoinResetMoney(getWorld()))) {
if (getSettings().isUseEconomy()
&& (getIWM().isOnLeaveResetMoney(getWorld()) || getIWM().isOnJoinResetMoney(getWorld()))) {
getPlugin().getVault().ifPresent(vault -> vault.withdraw(user, vault.getBalance(user)));
}
@ -229,6 +218,10 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
// Reset the XP
if (getIWM().isOnJoinResetXP(getWorld())) {
// Player collected XP (displayed)
user.getPlayer().setLevel(0);
user.getPlayer().setExp(0);
// Player total XP (not displayed)
user.getPlayer().setTotalExperience(0);
}

View File

@ -1,18 +1,35 @@
package world.bentobox.bentobox.api.commands.island.team;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.conversations.ConversationFactory;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.inventory.ItemStack;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.commands.island.team.Invite.Type;
import world.bentobox.bentobox.api.events.IslandBaseEvent;
import world.bentobox.bentobox.api.events.team.TeamEvent;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.panels.Panel;
import world.bentobox.bentobox.api.panels.PanelItem;
import world.bentobox.bentobox.api.panels.TemplatedPanel;
import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder;
import world.bentobox.bentobox.api.panels.builders.TemplatedPanelBuilder;
import world.bentobox.bentobox.api.panels.reader.ItemTemplateRecord;
import world.bentobox.bentobox.api.panels.reader.ItemTemplateRecord.ActionRecords;
import world.bentobox.bentobox.api.panels.reader.PanelTemplateRecord.TemplateItem;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.IslandsManager;
@ -24,6 +41,13 @@ public class IslandTeamInviteCommand extends CompositeCommand {
private final IslandTeamCommand itc;
private @Nullable User invitedPlayer;
private @Nullable TemplateItem border;
private @Nullable TemplateItem background;
private User user;
private long page = 0; // This number by 35
private boolean inviteCmd;
private static final long PER_PAGE = 35;
private String searchName = "";
public IslandTeamInviteCommand(IslandTeamCommand parent) {
super(parent, "invite");
@ -36,6 +60,10 @@ public class IslandTeamInviteCommand extends CompositeCommand {
setOnlyPlayer(true);
setDescription("commands.island.team.invite.description");
setConfigurableRankCommand();
// Panels
if (!new File(getPlugin().getDataFolder() + File.separator + "panels", "team_invite_panel.yml").exists()) {
getPlugin().saveResource("panels/team_invite_panel.yml", false);
}
}
@ -51,7 +79,9 @@ public class IslandTeamInviteCommand extends CompositeCommand {
}
if (args.size() != 1) {
return handleCommandWithNoArgs(user);
this.inviteCmd = true;
build(user);
return true;
}
Island island = islandsManager.getIsland(getWorld(), user);
@ -60,33 +90,15 @@ public class IslandTeamInviteCommand extends CompositeCommand {
return checkRankAndInvitePlayer(user, island, rank, args.get(0));
}
private boolean handleCommandWithNoArgs(User user) {
UUID playerUUID = user.getUniqueId();
Type inviteType = getInviteType(playerUUID);
if (inviteType != null) {
String name = getPlayers().getName(playerUUID);
switch (inviteType) {
case COOP -> user.sendMessage("commands.island.team.invite.name-has-invited-you.coop", TextVariables.NAME, name);
case TRUST -> user.sendMessage("commands.island.team.invite.name-has-invited-you.trust", TextVariables.NAME, name);
default -> user.sendMessage("commands.island.team.invite.name-has-invited-you", TextVariables.NAME, name);
}
return true;
}
showHelp(this, user);
return false;
}
private boolean checkRankAndInvitePlayer(User user, Island island, int rank, String playerName) {
RanksManager ranksManager = getPlugin().getRanksManager();
PlayersManager playersManager = getPlayers();
UUID playerUUID = user.getUniqueId();
// Check rank to use command
int requiredRank = island.getRankCommand(getUsage());
if (rank < requiredRank) {
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK, user.getTranslation(ranksManager.getRank(rank)));
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK,
user.getTranslation(RanksManager.getInstance().getRank(rank)));
return false;
}
@ -127,14 +139,6 @@ public class IslandTeamInviteCommand extends CompositeCommand {
return true;
}
private Type getInviteType(UUID playerUUID) {
if (itc.isInvited(playerUUID)) {
Invite invite = itc.getInvite(playerUUID);
return invite.getType();
}
return null;
}
private boolean canInvitePlayer(User user, User invitedPlayer) {
UUID playerUUID = user.getUniqueId();
if (!invitedPlayer.isOnline() || !user.getPlayer().canSee(invitedPlayer.getPlayer())) {
@ -166,9 +170,14 @@ public class IslandTeamInviteCommand extends CompositeCommand {
itc.removeInvite(invitedPlayer.getUniqueId());
user.sendMessage("commands.island.team.invite.removing-invite");
}
Island island = getIslands().getIsland(getWorld(), user.getUniqueId());
if (island == null) {
user.sendMessage("general.errors.no-island");
return false;
}
// Fire event so add-ons can run commands, etc.
IslandBaseEvent e = TeamEvent.builder()
.island(getIslands().getIsland(getWorld(), user.getUniqueId()))
.island(island)
.reason(TeamEvent.Reason.INVITE)
.involvedPlayer(invitedPlayer.getUniqueId())
.build();
@ -177,7 +186,7 @@ public class IslandTeamInviteCommand extends CompositeCommand {
}
// Put the invited player (key) onto the list with inviter (value)
// If someone else has invited a player, then this invite will overwrite the previous invite!
itc.addInvite(Invite.Type.TEAM, user.getUniqueId(), invitedPlayer.getUniqueId());
itc.addInvite(Invite.Type.TEAM, user.getUniqueId(), invitedPlayer.getUniqueId(), island);
user.sendMessage("commands.island.team.invite.invitation-sent", TextVariables.NAME, invitedPlayer.getName(), TextVariables.DISPLAY_NAME, invitedPlayer.getDisplayName());
// Send message to online player
invitedPlayer.sendMessage("commands.island.team.invite.name-has-invited-you", TextVariables.NAME, user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName());
@ -199,4 +208,204 @@ public class IslandTeamInviteCommand extends CompositeCommand {
return Optional.of(Util.tabLimit(options, lastArg));
}
/**
* Build the invite panel
* @param user use of the panel
*/
void build(User user) {
this.user = user;
// Start building panel.
TemplatedPanelBuilder panelBuilder = new TemplatedPanelBuilder();
panelBuilder.user(user);
panelBuilder.world(user.getWorld());
panelBuilder.template("team_invite_panel", new File(getPlugin().getDataFolder(), "panels"));
panelBuilder.parameters("[name]", user.getName(), "[display_name]", user.getDisplayName());
panelBuilder.registerTypeBuilder("PROSPECT", this::createProspectButton);
panelBuilder.registerTypeBuilder("PREVIOUS", this::createPreviousButton);
panelBuilder.registerTypeBuilder("NEXT", this::createNextButton);
panelBuilder.registerTypeBuilder("SEARCH", this::createSearchButton);
panelBuilder.registerTypeBuilder("BACK", this::createBackButton);
// Stash the backgrounds for later use
border = panelBuilder.getPanelTemplate().border();
background = panelBuilder.getPanelTemplate().background();
// Register unknown type builder.
panelBuilder.build();
}
private PanelItem createBackButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) {
checkTemplate(template);
return new PanelItemBuilder().name(user.getTranslation(template.title())).icon(template.icon())
.clickHandler((panel, user, clickType, clickSlot) -> {
user.closeInventory();
if (!inviteCmd) {
this.itc.build();
}
return true;
}).build();
}
private PanelItem createSearchButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) {
checkTemplate(template);
PanelItemBuilder pib = new PanelItemBuilder().name(user.getTranslation(template.title())).icon(template.icon())
.clickHandler((panel, user, clickType, clickSlot) -> {
user.closeInventory();
new ConversationFactory(BentoBox.getInstance()).withLocalEcho(false).withTimeout(90)
.withModality(false).withFirstPrompt(new InviteNamePrompt(user, this))
.buildConversation(user.getPlayer()).begin();
return true;
});
if (!this.searchName.isBlank()) {
pib.description(user.getTranslation(Objects
.requireNonNullElse(template.description(),
"commands.island.team.invite.gui.button.searching"),
TextVariables.NAME, searchName));
}
return pib.build();
}
private PanelItem createNextButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) {
checkTemplate(template);
long count = getWorld().getPlayers().stream().filter(player -> user.getPlayer().canSee(player))
.filter(player -> !player.equals(user.getPlayer())).count();
if (count > page * PER_PAGE) {
// We need to show a next button
return new PanelItemBuilder().name(user.getTranslation(template.title())).icon(template.icon())
.clickHandler((panel, user, clickType, clickSlot) -> {
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1F, 1F);
page++;
build(user);
return true;
}).build();
}
return getBlankBorder();
}
private void checkTemplate(ItemTemplateRecord template) {
if (template.icon() == null) {
getPlugin().logError("Icon in template is missing or unknown! " + template.toString());
}
if (template.title() == null) {
getPlugin().logError("Title in template is missing! " + template.toString());
}
}
private PanelItem createPreviousButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) {
checkTemplate(template);
if (page > 0) {
// We need to show a next button
return new PanelItemBuilder().name(user.getTranslation(template.title())).icon(template.icon())
.clickHandler((panel, user, clickType, clickSlot) -> {
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1F, 1F);
page--;
build(user);
return true;
}).build();
}
return getBlankBorder();
}
private PanelItem getBlankBorder() {
return new PanelItemBuilder().icon(Objects.requireNonNullElse(border.icon(), new ItemStack(Material.BARRIER)))
.name((Objects.requireNonNullElse(border.title(), ""))).build();
}
/**
* Create member button panel item.
*
* @param template the template
* @param slot the slot
* @return the panel item
*/
private PanelItem createProspectButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) {
// Player issuing the command must have an island
Island island = getIslands().getPrimaryIsland(getWorld(), user.getUniqueId());
if (island == null) {
return this.getBlankBackground();
}
if (page < 0) {
page = 0;
}
return getWorld().getPlayers().stream().filter(player -> user.getPlayer().canSee(player))
.filter(player -> this.searchName.isBlank() ? true
: player.getName().toLowerCase().contains(searchName.toLowerCase()))
.filter(player -> !player.equals(user.getPlayer())).skip(slot.slot() + page * PER_PAGE).findFirst()
.map(player -> getProspect(player, template)).orElse(this.getBlankBackground());
}
private PanelItem getProspect(Player player, ItemTemplateRecord template) {
// Check if the prospect has already been invited
if (this.itc.isInvited(player.getUniqueId())
&& user.getUniqueId().equals(this.itc.getInvite(player.getUniqueId()).getInviter())) {
return new PanelItemBuilder().icon(player.getName()).name(player.getDisplayName())
.description(user.getTranslation("commands.island.team.invite.gui.button.already-invited")).build();
}
List<String> desc = template.actions().stream().map(ar -> user
.getTranslation("commands.island.team.invite.gui.tips." + ar.clickType().name() + ".name")
+ " " + user.getTranslation(ar.tooltip())).toList();
return new PanelItemBuilder().icon(player.getName()).name(player.getDisplayName()).description(desc)
.clickHandler(
(panel, user, clickType, clickSlot) -> clickHandler(panel, user, clickType, clickSlot, player,
template.actions()))
.build();
}
private boolean clickHandler(Panel panel, User user, ClickType clickType, int clickSlot, Player player,
@NonNull List<ActionRecords> list) {
if (!list.stream().anyMatch(ar -> clickType.equals(ar.clickType()))) {
// If the click type is not in the template, don't do anything
return true;
}
if (clickType.equals(ClickType.LEFT)) {
user.closeInventory();
if (this.canExecute(user, this.getLabel(), List.of(player.getName()))) {
getPlugin().log("Invite sent to: " + player.getName() + " by " + user.getName() + " to join island in "
+ getWorld().getName());
this.execute(user, getLabel(), List.of(player.getName()));
} else {
getPlugin().log("Invite failed: " + player.getName() + " by " + user.getName() + " to join island in "
+ getWorld().getName());
}
} else if (clickType.equals(ClickType.RIGHT)) {
user.closeInventory();
if (this.itc.getCoopCommand().canExecute(user, this.getLabel(), List.of(player.getName()))) {
getPlugin().log("Coop: " + player.getName() + " cooped " + user.getName() + " to island in "
+ getWorld().getName());
this.itc.getCoopCommand().execute(user, getLabel(), List.of(player.getName()));
} else {
getPlugin().log(
"Coop failed: " + player.getName() + "'s coop to " + user.getName() + " failed for island in "
+ getWorld().getName());
}
} else if (clickType.equals(ClickType.SHIFT_LEFT)) {
user.closeInventory();
if (this.itc.getTrustCommand().canExecute(user, this.getLabel(), List.of(player.getName()))) {
getPlugin().log("Trust: " + player.getName() + " trusted " + user.getName() + " to island in "
+ getWorld().getName());
this.itc.getTrustCommand().execute(user, getLabel(), List.of(player.getName()));
} else {
getPlugin().log("Trust failed: " + player.getName() + "'s trust failed for " + user.getName()
+ " for island in "
+ getWorld().getName());
}
}
return true;
}
private PanelItem getBlankBackground() {
return new PanelItemBuilder()
.icon(Objects.requireNonNullElse(background.icon(), new ItemStack(Material.BARRIER)))
.name((Objects.requireNonNullElse(background.title(), ""))).build();
}
/**
* @param searchName the searchName to set
*/
void setSearchName(String searchName) {
this.searchName = searchName;
}
}

View File

@ -19,7 +19,6 @@ import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.bentobox.util.Util;
public class IslandTeamKickCommand extends ConfirmableCommand {
public IslandTeamKickCommand(CompositeCommand islandTeamCommand) {
@ -36,7 +35,7 @@ public class IslandTeamKickCommand extends ConfirmableCommand {
}
@Override
public boolean execute(User user, String label, List<String> args) {
public boolean canExecute(User user, String label, List<String> args) {
if (!getIslands().inTeam(getWorld(), user.getUniqueId())) {
user.sendMessage("general.errors.no-team");
return false;
@ -45,7 +44,8 @@ public class IslandTeamKickCommand extends ConfirmableCommand {
Island island = getIslands().getIsland(getWorld(), user);
int rank = Objects.requireNonNull(island).getRank(user);
if (rank < island.getRankCommand(getUsage())) {
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK, user.getTranslation(getPlugin().getRanksManager().getRank(rank)));
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK,
user.getTranslation(RanksManager.getInstance().getRank(rank)));
return false;
}
// If args are not right, show help
@ -63,18 +63,24 @@ public class IslandTeamKickCommand extends ConfirmableCommand {
user.sendMessage("commands.island.team.kick.cannot-kick");
return false;
}
if (!getIslands().getMembers(getWorld(), user.getUniqueId()).contains(targetUUID)) {
if (!getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()).getMemberSet().contains(targetUUID)) {
user.sendMessage("general.errors.not-in-team");
return false;
}
int targetRank = Objects.requireNonNull(island).getRank(targetUUID);
if (rank <= targetRank) {
user.sendMessage("commands.island.team.kick.cannot-kick-rank",
TextVariables.NAME, getPlayers().getName(targetUUID));
user.sendMessage("commands.island.team.kick.cannot-kick-rank", TextVariables.NAME,
getPlayers().getName(targetUUID));
return false;
}
return true;
}
@Override
public boolean execute(User user, String label, List<String> args) {
// Get target
UUID targetUUID = getPlayers().getUUID(args.get(0));
if (!getSettings().isKickConfirmation()) {
kick(user, targetUUID);
return true;
@ -84,42 +90,36 @@ public class IslandTeamKickCommand extends ConfirmableCommand {
}
}
private void kick(User user, UUID targetUUID) {
protected void kick(User user, UUID targetUUID) {
User target = User.getInstance(targetUUID);
Island oldIsland = Objects.requireNonNull(getIslands().getIsland(getWorld(), targetUUID)); // Should never be null because of checks above
Island oldIsland = Objects.requireNonNull(getIslands().getIsland(getWorld(), targetUUID)); // Should never be
// null because of
// checks above
// Fire event
IslandBaseEvent event = TeamEvent.builder()
.island(oldIsland)
.reason(TeamEvent.Reason.KICK)
.involvedPlayer(targetUUID)
.build();
IslandBaseEvent event = TeamEvent.builder().island(oldIsland).reason(TeamEvent.Reason.KICK)
.involvedPlayer(targetUUID).build();
if (event.isCancelled()) {
return;
}
target.sendMessage("commands.island.team.kick.player-kicked",
TextVariables.GAMEMODE, getAddon().getDescription().getName(),
TextVariables.NAME, user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName());
target.sendMessage("commands.island.team.kick.player-kicked", TextVariables.GAMEMODE,
getAddon().getDescription().getName(), TextVariables.NAME, user.getName(), TextVariables.DISPLAY_NAME,
user.getDisplayName());
getIslands().removePlayer(getWorld(), targetUUID);
// Clean the target player
getPlayers().cleanLeavingPlayer(getWorld(), target, true, oldIsland);
user.sendMessage("commands.island.team.kick.success", TextVariables.NAME, target.getName(), TextVariables.DISPLAY_NAME, target.getDisplayName());
IslandEvent.builder()
.island(oldIsland)
.involvedPlayer(user.getUniqueId())
.admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(oldIsland.getRank(user), RanksManager.VISITOR_RANK)
.build();
user.sendMessage("commands.island.team.kick.success", TextVariables.NAME, target.getName(),
TextVariables.DISPLAY_NAME, target.getDisplayName());
IslandEvent.builder().island(oldIsland).involvedPlayer(user.getUniqueId()).admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE).rankChange(oldIsland.getRank(user), RanksManager.VISITOR_RANK)
.build();
// Add cooldown for this player and target
if (getSettings().getInviteCooldown() > 0 && getParent() != null) {
// Get the invite class from the parent
getParent().getSubCommand("invite").ifPresent(c -> c.setCooldown(
oldIsland.getUniqueId(),
targetUUID.toString(),
getSettings().getInviteCooldown() * 60));
getParent().getSubCommand("invite").ifPresent(c -> c.setCooldown(oldIsland.getUniqueId(),
targetUUID.toString(), getSettings().getInviteCooldown() * 60));
}
}
@ -128,11 +128,10 @@ public class IslandTeamKickCommand extends ConfirmableCommand {
Island island = getIslands().getIsland(getWorld(), user.getUniqueId());
if (island != null) {
List<String> options = island.getMemberSet().stream()
.filter(uuid -> island.getRank(uuid) >= RanksManager.MEMBER_RANK)
.map(Bukkit::getOfflinePlayer)
.filter(uuid -> island.getRank(uuid) >= RanksManager.MEMBER_RANK).map(Bukkit::getOfflinePlayer)
.map(OfflinePlayer::getName).toList();
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
String lastArg = !args.isEmpty() ? args.get(args.size() - 1) : "";
return Optional.of(Util.tabLimit(options, lastArg));
} else {
return Optional.empty();

View File

@ -32,7 +32,7 @@ public class IslandTeamLeaveCommand extends ConfirmableCommand {
user.sendMessage("general.errors.no-team");
return false;
}
if (getIslands().hasIsland(getWorld(), user.getUniqueId())) {
if (user.getUniqueId().equals(getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()).getOwner())) {
user.sendMessage("commands.island.team.leave.cannot-leave");
return false;
}
@ -65,29 +65,32 @@ public class IslandTeamLeaveCommand extends ConfirmableCommand {
}
private void leave(User user) {
protected boolean leave(User user) {
Island island = getIslands().getIsland(getWorld(), user);
if (island == null) {
user.sendMessage("general.errors.no-island");
return false;
}
// Fire event
IslandBaseEvent event = TeamEvent.builder()
.island(island)
.reason(TeamEvent.Reason.LEAVE)
.involvedPlayer(user.getUniqueId())
.build();
IslandBaseEvent event = TeamEvent.builder().island(island).reason(TeamEvent.Reason.LEAVE)
.involvedPlayer(user.getUniqueId()).build();
if (event.isCancelled()) {
return;
return false;
}
UUID ownerUUID = getIslands().getOwner(getWorld(), user.getUniqueId());
UUID ownerUUID = island.getOwner();
if (ownerUUID != null) {
User.getInstance(ownerUUID).sendMessage("commands.island.team.leave.left-your-island", TextVariables.NAME, user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName());
User.getInstance(ownerUUID).sendMessage("commands.island.team.leave.left-your-island", TextVariables.NAME,
user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName());
}
getIslands().setLeaveTeam(getWorld(), user.getUniqueId());
getIslands().removePlayer(island, user.getUniqueId());
// Clean the player
getPlayers().cleanLeavingPlayer(getWorld(), user, false, island);
// Add cooldown for this player and target
if (getSettings().getInviteCooldown() > 0 && getParent() != null) {
// Get the invite class from the parent
getParent().getSubCommand("invite").ifPresent(c -> c.setCooldown(island.getUniqueId(), user.getUniqueId().toString(), getSettings().getInviteCooldown() * 60));
getParent().getSubCommand("invite").ifPresent(c -> c.setCooldown(island.getUniqueId(),
user.getUniqueId().toString(), getSettings().getInviteCooldown() * 60));
}
// Remove reset if required
if (getIWM().isLeaversLoseReset(getWorld())) {
@ -97,12 +100,9 @@ public class IslandTeamLeaveCommand extends ConfirmableCommand {
showResets(user);
}
user.sendMessage("commands.island.team.leave.success");
IslandEvent.builder()
.island(island)
.involvedPlayer(user.getUniqueId())
.admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(island.getRank(user), RanksManager.VISITOR_RANK)
.build();
IslandEvent.builder().island(island).involvedPlayer(user.getUniqueId()).admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE).rankChange(island.getRank(user), RanksManager.VISITOR_RANK)
.build();
return true;
}
}

View File

@ -17,6 +17,8 @@ import world.bentobox.bentobox.util.Util;
public class IslandTeamPromoteCommand extends CompositeCommand {
private User target;
public IslandTeamPromoteCommand(CompositeCommand islandTeamCommand, String string) {
super(islandTeamCommand, string);
}
@ -35,53 +37,72 @@ public class IslandTeamPromoteCommand extends CompositeCommand {
this.setConfigurableRankCommand();
}
@Override
public boolean execute(User user, String label, List<String> args) {
if (!getIslands().inTeam(getWorld(), user.getUniqueId())) {
user.sendMessage("general.errors.no-team");
return true;
}
// Check rank to use command
Island island = getIslands().getIsland(getWorld(), user);
int rank = Objects.requireNonNull(island).getRank(user);
if (rank < island.getRankCommand(getUsage())) {
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK, user.getTranslation(getPlugin().getRanksManager().getRank(rank)));
return false;
}
@Override
public boolean canExecute(User user, String label, List<String> args) {
// If args are not right, show help
if (args.size() != 1) {
showHelp(this, user);
return false;
}
// Get target
User target = getPlayers().getUser(args.get(0));
if (target == null) {
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
return true;
if (!getIslands().inTeam(getWorld(), user.getUniqueId())) {
user.sendMessage("general.errors.no-team");
return false;
}
// Check if the user is not trying to promote/ demote himself
if (target == user) {
user.sendMessage("commands.island.team.demote.errors.cant-demote-yourself");
return true;
}
if (!inTeam(getWorld(), target) || !Objects.requireNonNull(getOwner(getWorld(), user), "Island has no owner!").equals(getOwner(getWorld(), target))) {
user.sendMessage("general.errors.not-in-team");
return true;
// Check rank to use command
Island island = getIslands().getIsland(getWorld(), user);
int rank = Objects.requireNonNull(island).getRank(user);
if (rank < island.getRankCommand(getUsage())) {
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK,
user.getTranslation(RanksManager.getInstance().getRank(rank)));
return false;
}
// Get target
target = getPlayers().getUser(args.get(0));
if (target == null) {
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
return false;
}
// Check if the user is not trying to promote/ demote himself
if (target.equals(user)) {
if (this.getLabel().equals("promote")) {
user.sendMessage("commands.island.team.promote.errors.cant-promote-yourself");
} else {
user.sendMessage("commands.island.team.demote.errors.cant-demote-yourself");
}
return false;
}
// Check that user is not trying to promote above their own rank
// Check that user is not trying to demote ranks higher than them
if (island.getRank(target) >= island.getRank(user)) {
if (this.getLabel().equals("promote")) {
user.sendMessage("commands.island.team.promote.errors.cant-promote");
} else {
user.sendMessage("commands.island.team.demote.errors.cant-demote");
}
return false;
}
return true;
}
@Override
public boolean execute(User user, String label, List<String> args) {
return change(user, target);
}
private boolean change(User user, User target) {
Island island = getIslands().getIsland(getWorld(), user.getUniqueId());
Island island = getIslands().getIsland(getWorld(), user);
int currentRank = island.getRank(target);
if (this.getLabel().equals("promote")) {
int nextRank = getPlugin().getRanksManager().getRankUpValue(currentRank);
int nextRank = RanksManager.getInstance().getRankUpValue(currentRank);
// Stop short of owner
if (nextRank != RanksManager.OWNER_RANK && nextRank > currentRank) {
getIslands().getIsland(getWorld(), user.getUniqueId()).setRank(target, nextRank);
String rankName = user.getTranslation(getPlugin().getRanksManager().getRank(nextRank));
island.setRank(target, nextRank);
String rankName = user.getTranslation(RanksManager.getInstance().getRank(nextRank));
user.sendMessage("commands.island.team.promote.success", TextVariables.NAME, target.getName(), TextVariables.RANK, rankName, TextVariables.DISPLAY_NAME, target.getDisplayName());
IslandEvent.builder()
.island(island)
@ -97,11 +118,11 @@ public class IslandTeamPromoteCommand extends CompositeCommand {
}
} else {
// Demote
int prevRank = getPlugin().getRanksManager().getRankDownValue(currentRank);
int prevRank = RanksManager.getInstance().getRankDownValue(currentRank);
// Lowest is Member
if (prevRank >= RanksManager.MEMBER_RANK && prevRank < currentRank) {
getIslands().getIsland(getWorld(), user.getUniqueId()).setRank(target, prevRank);
String rankName = user.getTranslation(getPlugin().getRanksManager().getRank(prevRank));
island.setRank(target, prevRank);
String rankName = user.getTranslation(RanksManager.getInstance().getRank(prevRank));
user.sendMessage("commands.island.team.demote.success", TextVariables.NAME, target.getName(), TextVariables.RANK, rankName, TextVariables.DISPLAY_NAME, target.getDisplayName());
IslandEvent.builder()
.island(island)
@ -120,7 +141,7 @@ public class IslandTeamPromoteCommand extends CompositeCommand {
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
Island island = getIslands().getIsland(getWorld(), user.getUniqueId());
Island island = getIslands().getIsland(getWorld(), user);
if (island != null) {
List<String> options = island.getMemberSet().stream()
.map(Bukkit::getOfflinePlayer)

View File

@ -4,6 +4,9 @@ import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.events.IslandBaseEvent;
import world.bentobox.bentobox.api.events.island.IslandEvent;
@ -16,6 +19,8 @@ import world.bentobox.bentobox.util.Util;
public class IslandTeamSetownerCommand extends CompositeCommand {
private @Nullable UUID targetUUID;
public IslandTeamSetownerCommand(CompositeCommand islandTeamCommand) {
super(islandTeamCommand, "setowner");
}
@ -29,73 +34,77 @@ public class IslandTeamSetownerCommand extends CompositeCommand {
}
@Override
public boolean execute(User user, String label, List<String> args) {
UUID playerUUID = user.getUniqueId();
// Can use if in a team
boolean inTeam = getIslands().inTeam(getWorld(), playerUUID);
if (!inTeam) {
user.sendMessage("general.errors.no-team");
return false;
}
UUID ownerUUID = getOwner(getWorld(), user);
if (ownerUUID == null || !ownerUUID.equals(playerUUID)) {
user.sendMessage("general.errors.not-owner");
return false;
}
public boolean canExecute(User user, String label, List<String> args) {
// If args are not right, show help
if (args.size() != 1) {
showHelp(this, user);
return false;
}
UUID targetUUID = getPlayers().getUUID(args.get(0));
// Can use if in a team
Island is = getIslands().getPrimaryIsland(getWorld(), user.getUniqueId());
if (is == null || !is.getMemberSet().contains(user.getUniqueId())) {
user.sendMessage("general.errors.no-team");
return false;
}
UUID ownerUUID = is.getOwner();
if (ownerUUID == null || !ownerUUID.equals(user.getUniqueId())) {
user.sendMessage("general.errors.not-owner");
return false;
}
targetUUID = getPlayers().getUUID(args.get(0));
if (targetUUID == null) {
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
return false;
}
if (targetUUID.equals(playerUUID)) {
if (targetUUID.equals(user.getUniqueId())) {
user.sendMessage("commands.island.team.setowner.errors.cant-transfer-to-yourself");
return false;
}
if (!getIslands().getMembers(getWorld(), playerUUID).contains(targetUUID)) {
if (!is.getMemberSet().contains(targetUUID)) {
user.sendMessage("commands.island.team.setowner.errors.target-is-not-member");
return false;
}
return true;
}
@Override
public boolean execute(User user, String label, List<String> args) {
return setOwner(user, targetUUID);
}
protected boolean setOwner(User user, @NonNull UUID targetUUID2) {
// Fire event so add-ons can run commands, etc.
Island island = getIslands().getIsland(getWorld(), user);
Island island = getIslands().getPrimaryIsland(getWorld(), user.getUniqueId());
// Fire event so add-ons can run commands, etc.
IslandBaseEvent e = TeamEvent.builder()
.island(island)
.reason(TeamEvent.Reason.SETOWNER)
.involvedPlayer(targetUUID)
.build();
IslandBaseEvent e = TeamEvent.builder().island(island).reason(TeamEvent.Reason.SETOWNER)
.involvedPlayer(targetUUID2).build();
if (e.isCancelled()) {
return false;
}
getIslands().setOwner(getWorld(), user, targetUUID);
getIslands().setOwner(getWorld(), user, targetUUID2);
// Call the event for the new owner
IslandEvent.builder()
.island(island)
.involvedPlayer(targetUUID)
.admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(island.getRank(User.getInstance(targetUUID)), RanksManager.OWNER_RANK)
.build();
IslandEvent.builder().island(island).involvedPlayer(targetUUID2).admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(island.getRank(User.getInstance(targetUUID2)), RanksManager.OWNER_RANK).build();
// Call the event for the previous owner
IslandEvent.builder()
.island(island)
.involvedPlayer(playerUUID)
.admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(RanksManager.OWNER_RANK, island.getRank(user))
.build();
IslandEvent.builder().island(island).involvedPlayer(user.getUniqueId()).admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE).rankChange(RanksManager.OWNER_RANK, RanksManager.SUB_OWNER_RANK)
.build();
getIslands().save(island);
return true;
}
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
return Optional.of(Util.tabLimit(getIslands().getMembers(getWorld(), user.getUniqueId()).stream().map(getPlayers()::getName).toList(), lastArg));
String lastArg = !args.isEmpty() ? args.get(args.size() - 1) : "";
if (getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()) == null) {
return Optional.empty();
}
return Optional.of(Util.tabLimit(
getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()).getMemberSet().stream()
.filter(uuid -> !user.getUniqueId().equals(uuid)).map(getPlayers()::getName).toList(),
lastArg));
}
}
}

View File

@ -55,7 +55,8 @@ public class IslandTeamTrustCommand extends CompositeCommand {
Island island = getIslands().getIsland(getWorld(), user);
int rank = Objects.requireNonNull(island).getRank(user);
if (rank < island.getRankCommand(getUsage())) {
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK, user.getTranslation(getPlugin().getRanksManager().getRank(rank)));
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK,
user.getTranslation(RanksManager.getInstance().getRank(rank)));
return false;
}
// Get target player
@ -95,7 +96,7 @@ public class IslandTeamTrustCommand extends CompositeCommand {
if (getPlugin().getSettings().isInviteConfirmation()) {
// Put the invited player (key) onto the list with inviter (value)
// If someone else has invited a player, then this invite will overwrite the previous invite!
itc.addInvite(Type.TRUST, user.getUniqueId(), target.getUniqueId());
itc.addInvite(Type.TRUST, user.getUniqueId(), target.getUniqueId(), island);
user.sendMessage("commands.island.team.invite.invitation-sent", TextVariables.NAME, target.getName(), TextVariables.DISPLAY_NAME, target.getDisplayName());
// Send message to online player
target.sendMessage("commands.island.team.trust.name-has-invited-you", TextVariables.NAME, user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName());

View File

@ -18,6 +18,7 @@ import world.bentobox.bentobox.util.Util;
/**
* Command to uncoop a player
*
* @author tastybento
*
*/
@ -44,7 +45,8 @@ public class IslandTeamUncoopCommand extends CompositeCommand {
return false;
}
// Player issuing the command must have an island or be in a team
if (!getIslands().inTeam(getWorld(), user.getUniqueId()) && !getIslands().hasIsland(getWorld(), user.getUniqueId())) {
if (!getIslands().inTeam(getWorld(), user.getUniqueId())
&& !getIslands().hasIsland(getWorld(), user.getUniqueId())) {
user.sendMessage("general.errors.no-island");
return false;
}
@ -52,7 +54,8 @@ public class IslandTeamUncoopCommand extends CompositeCommand {
Island island = getIslands().getIsland(getWorld(), user);
int rank = Objects.requireNonNull(island).getRank(user);
if (rank < island.getRankCommand(getUsage())) {
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK, user.getTranslation(getPlugin().getRanksManager().getRank(rank)));
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK,
user.getTranslation(RanksManager.getInstance().getRank(rank)));
return false;
}
// Get target player
@ -65,13 +68,13 @@ public class IslandTeamUncoopCommand extends CompositeCommand {
return unCoopCmd(user, targetUUID);
}
private boolean unCoopCmd(User user, UUID targetUUID) {
protected boolean unCoopCmd(User user, UUID targetUUID) {
// Player cannot uncoop themselves
if (user.getUniqueId().equals(targetUUID)) {
user.sendMessage("commands.island.team.uncoop.cannot-uncoop-yourself");
return false;
}
if (getIslands().getMembers(getWorld(), user.getUniqueId()).contains(targetUUID)) {
if (getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()).getMemberSet().contains(targetUUID)) {
user.sendMessage("commands.island.team.uncoop.cannot-uncoop-member");
return false;
}
@ -83,21 +86,19 @@ public class IslandTeamUncoopCommand extends CompositeCommand {
}
Island island = getIslands().getIsland(getWorld(), user.getUniqueId());
if (island != null) {
island.removeMember(targetUUID);
user.sendMessage("commands.island.team.uncoop.success", TextVariables.NAME, target.getName(), TextVariables.DISPLAY_NAME, target.getDisplayName());
target.sendMessage("commands.island.team.uncoop.you-are-no-longer-a-coop-member", TextVariables.NAME, user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName());
getIslands().removePlayer(island, targetUUID);
user.sendMessage("commands.island.team.uncoop.success", TextVariables.NAME, target.getName(),
TextVariables.DISPLAY_NAME, target.getDisplayName());
target.sendMessage("commands.island.team.uncoop.you-are-no-longer-a-coop-member", TextVariables.NAME,
user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName());
// Set cooldown
if (getSettings().getCoopCooldown() > 0 && getParent() != null) {
getParent().getSubCommand("coop").ifPresent(subCommand ->
subCommand.setCooldown(island.getUniqueId(), targetUUID.toString(), getSettings().getCoopCooldown() * 60));
getParent().getSubCommand("coop").ifPresent(subCommand -> subCommand.setCooldown(island.getUniqueId(),
targetUUID.toString(), getSettings().getCoopCooldown() * 60));
}
IslandEvent.builder()
.island(island)
.involvedPlayer(targetUUID)
.admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(RanksManager.COOP_RANK, RanksManager.VISITOR_RANK)
.build();
IslandEvent.builder().island(island).involvedPlayer(targetUUID).admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(RanksManager.COOP_RANK, RanksManager.VISITOR_RANK).build();
return true;
} else {
// Should not happen
@ -111,10 +112,9 @@ public class IslandTeamUncoopCommand extends CompositeCommand {
Island island = getIslands().getIsland(getWorld(), user.getUniqueId());
if (island != null) {
List<String> options = island.getMembers().entrySet().stream()
.filter(e -> e.getValue() == RanksManager.COOP_RANK)
.map(e -> Bukkit.getOfflinePlayer(e.getKey()))
.filter(e -> e.getValue() == RanksManager.COOP_RANK).map(e -> Bukkit.getOfflinePlayer(e.getKey()))
.map(OfflinePlayer::getName).toList();
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
String lastArg = !args.isEmpty() ? args.get(args.size() - 1) : "";
return Optional.of(Util.tabLimit(options, lastArg));
} else {
return Optional.empty();

View File

@ -18,6 +18,7 @@ import world.bentobox.bentobox.util.Util;
/**
* Command to untrust a player
*
* @author tastybento
*
*/
@ -44,7 +45,8 @@ public class IslandTeamUntrustCommand extends CompositeCommand {
return false;
}
// Player issuing the command must have an island or be in a team
if (!getIslands().inTeam(getWorld(), user.getUniqueId()) && !getIslands().hasIsland(getWorld(), user.getUniqueId())) {
if (!getIslands().inTeam(getWorld(), user.getUniqueId())
&& !getIslands().hasIsland(getWorld(), user.getUniqueId())) {
user.sendMessage("general.errors.no-island");
return false;
}
@ -52,7 +54,8 @@ public class IslandTeamUntrustCommand extends CompositeCommand {
Island island = getIslands().getIsland(getWorld(), user);
int rank = Objects.requireNonNull(island).getRank(user);
if (rank < island.getRankCommand(getUsage())) {
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK, user.getTranslation(getPlugin().getRanksManager().getRank(rank)));
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK,
user.getTranslation(RanksManager.getInstance().getRank(rank)));
return false;
}
// Get target player
@ -65,13 +68,13 @@ public class IslandTeamUntrustCommand extends CompositeCommand {
return unTrustCmd(user, targetUUID);
}
private boolean unTrustCmd(User user, UUID targetUUID) {
protected boolean unTrustCmd(User user, UUID targetUUID) {
// Player cannot untrust themselves
if (user.getUniqueId().equals(targetUUID)) {
user.sendMessage("commands.island.team.untrust.cannot-untrust-yourself");
return false;
}
if (getIslands().getMembers(getWorld(), user.getUniqueId()).contains(targetUUID)) {
if (getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()).getMemberSet().contains(targetUUID)) {
user.sendMessage("commands.island.team.untrust.cannot-untrust-member");
return false;
}
@ -83,21 +86,19 @@ public class IslandTeamUntrustCommand extends CompositeCommand {
}
Island island = getIslands().getIsland(getWorld(), user.getUniqueId());
if (island != null) {
island.removeMember(targetUUID);
user.sendMessage("commands.island.team.untrust.success", TextVariables.NAME, target.getName(), TextVariables.DISPLAY_NAME, target.getDisplayName());
target.sendMessage("commands.island.team.untrust.you-are-no-longer-trusted", TextVariables.NAME, user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName());
getIslands().removePlayer(island, targetUUID);
user.sendMessage("commands.island.team.untrust.success", TextVariables.NAME, target.getName(),
TextVariables.DISPLAY_NAME, target.getDisplayName());
target.sendMessage("commands.island.team.untrust.you-are-no-longer-trusted", TextVariables.NAME,
user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName());
// Set cooldown
if (getSettings().getTrustCooldown() > 0 && getParent() != null) {
getParent().getSubCommand("trust").ifPresent(subCommand ->
subCommand.setCooldown(island.getUniqueId(), targetUUID.toString(), getSettings().getTrustCooldown() * 60));
getParent().getSubCommand("trust").ifPresent(subCommand -> subCommand.setCooldown(island.getUniqueId(),
targetUUID.toString(), getSettings().getTrustCooldown() * 60));
}
IslandEvent.builder()
.island(island)
.involvedPlayer(targetUUID)
.admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(RanksManager.TRUSTED_RANK, RanksManager.VISITOR_RANK)
.build();
IslandEvent.builder().island(island).involvedPlayer(targetUUID).admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(RanksManager.TRUSTED_RANK, RanksManager.VISITOR_RANK).build();
return true;
} else {
// Should not happen
@ -112,9 +113,8 @@ public class IslandTeamUntrustCommand extends CompositeCommand {
if (island != null) {
List<String> options = island.getMembers().entrySet().stream()
.filter(e -> e.getValue() == RanksManager.TRUSTED_RANK)
.map(e -> Bukkit.getOfflinePlayer(e.getKey()))
.map(OfflinePlayer::getName).toList();
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
.map(e -> Bukkit.getOfflinePlayer(e.getKey())).map(OfflinePlayer::getName).toList();
String lastArg = !args.isEmpty() ? args.get(args.size() - 1) : "";
return Optional.of(Util.tabLimit(options, lastArg));
} else {
return Optional.empty();

View File

@ -13,6 +13,7 @@ import org.bukkit.GameMode;
import org.bukkit.entity.EntityType;
import org.eclipse.jdt.annotation.NonNull;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.flags.Flag;
import world.bentobox.bentobox.lists.Flags;
@ -549,6 +550,7 @@ public interface WorldSettings extends ConfigObject {
* Returns all aliases for main admin command.
* It is assumed that all aliases are split with whitespace between them.
* String cannot be empty.
* The first command listed is the "label" in the API, and after that are the aliases
* Default value: {@code getFriendlyName() + "admin"} (to retain backward compatibility).
* @return String value
* @since 1.13.0
@ -563,6 +565,7 @@ public interface WorldSettings extends ConfigObject {
* Returns all aliases for main player command.
* It is assumed that all aliases are split with whitespace between them.
* String cannot be empty.
* The first command listed is the "label" in the API, and after that are the aliases
* Default value: {@code getFriendlyName()} (to retain backward compatibility).
* @return String value
* @since 1.13.0
@ -632,4 +635,13 @@ public interface WorldSettings extends ConfigObject {
default boolean isCheckForBlocks() {
return true;
}
/**
* Get the number of concurrent islands a player can have in the world
* @return 1 by default
* @since 2.0.0
*/
default int getConcurrentIslands() {
return BentoBox.getInstance().getSettings().getIslandNumber();
}
}

View File

@ -33,40 +33,53 @@ public abstract class IslandBaseEvent extends BentoBoxEvent implements Cancellab
}
/**
* @param island - island
* @param island - island
* @param playerUUID - the player's UUID
* @param admin - true if ths is due to an admin event
* @param location - the location
* @param admin - true if ths is due to an admin event
* @param location - the location
*/
public IslandBaseEvent(Island island, UUID playerUUID, boolean admin, Location location) {
super();
this.island = island;
this.playerUUID = playerUUID;
this.admin = admin;
this.location = location;
if (location != null) {
this.location = location;
} else if (island != null) {
this.location = island.getCenter();
} else {
this.location = null;
}
rawEvent = null;
}
/**
* @param island - island
* @param island - island
* @param playerUUID - the player's UUID
* @param admin - true if ths is due to an admin event
* @param location - the location
* @param rawEvent - the raw event
* @param admin - true if ths is due to an admin event
* @param location - the location
* @param rawEvent - the raw event
*/
public IslandBaseEvent(Island island, UUID playerUUID, boolean admin, Location location, Event rawEvent) {
super();
this.island = island;
this.playerUUID = playerUUID;
this.admin = admin;
this.location = location;
if (location != null) {
this.location = location;
} else if (island != null) {
this.location = island.getCenter();
} else {
this.location = null;
}
this.rawEvent = rawEvent;
}
/**
* @return the island involved in this event. This may be null in the case of deleted islands, so use location instead
* @return the island involved in this event. This may be null in the case of
* deleted islands, so use location instead
*/
public Island getIsland(){
public Island getIsland() {
return island;
}
@ -94,6 +107,7 @@ public abstract class IslandBaseEvent extends BentoBoxEvent implements Cancellab
/**
* @return the location
*/
@Nullable
public Location getLocation() {
return location;
}
@ -118,6 +132,7 @@ public abstract class IslandBaseEvent extends BentoBoxEvent implements Cancellab
/**
* Get new event if this event is deprecated
*
* @return optional newEvent or empty if there is none
*/
public Optional<IslandBaseEvent> getNewEvent() {
@ -126,6 +141,7 @@ public abstract class IslandBaseEvent extends BentoBoxEvent implements Cancellab
/**
* Set the newer event so it can be obtained if this event is deprecated
*
* @param newEvent the newEvent to set
*/
public void setNewEvent(IslandBaseEvent newEvent) {

View File

@ -0,0 +1,50 @@
package world.bentobox.bentobox.api.events.flags;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
import world.bentobox.bentobox.api.events.BentoBoxEvent;
/**
* This event is fired just before damage is prevented to visitors on an island, if that protection is provided.
* @author tastybento
*
*/
public class InvincibleVistorFlagDamageRemovalEvent extends BentoBoxEvent implements Cancellable {
private final Player player;
private final DamageCause cause;
private boolean cancel;
/**
* This event is fired just before damage is prevented to visitors on an island, if that protection is provided.
* @param player player being protected
* @param cause damage cause
*/
public InvincibleVistorFlagDamageRemovalEvent(Player player, DamageCause cause) {
this.player = player;
this.cause = cause;
}
@Override
public boolean isCancelled() {
return cancel;
}
@Override
public void setCancelled(boolean cancel) {
this.cancel = cancel;
}
/**
* @return the player
*/
public Player getPlayer() {
return player;
}
/**
* @return the cause
*/
public DamageCause getCause() {
return cause;
}
}

View File

@ -176,7 +176,7 @@ public class IslandEvent extends IslandBaseEvent {
* Event that will fire when an island is named or renamed
* @since 1.24.0
*/
NAME,
NAME,
/**
* Event that will fire when the info command is executed. Allows addons to add to it
* @since 1.24.0
@ -334,7 +334,7 @@ public class IslandEvent extends IslandBaseEvent {
this.previousName = previousName;
return this;
}
/**
* Addon that triggered this event, e.g. BSkyBlock
* @param addon Addon.
@ -389,4 +389,4 @@ public class IslandEvent extends IslandBaseEvent {
}
}
}
}

View File

@ -377,12 +377,13 @@ public class Flag implements Comparable<Flag> {
* Converts a flag to a panel item. The content of the flag will change depending on who the user is and where they are.
* @param plugin - plugin
* @param user - user that will see this flag
* @param world - the world this flag is being shown for. If island is present, then world is the same as the island.
* @param island - target island, if any
* @param invisible - true if this flag is not visible to players
* @return - PanelItem for this flag or null if item is invisible to user
*/
@Nullable
public PanelItem toPanelItem(BentoBox plugin, User user, @Nullable Island island, boolean invisible) {
public PanelItem toPanelItem(BentoBox plugin, User user, World world, @Nullable Island island, boolean invisible) {
// Invisibility
if (!user.isOp() && invisible) {
return null;
@ -400,12 +401,13 @@ public class Flag implements Comparable<Flag> {
return switch (getType()) {
case PROTECTION -> createProtectionFlag(plugin, user, island, pib).build();
case SETTING -> createSettingFlag(user, island, pib).build();
case WORLD_SETTING -> createWorldSettingFlag(user, pib).build();
case WORLD_SETTING -> createWorldSettingFlag(user, world, pib).build();
};
}
private PanelItemBuilder createWorldSettingFlag(User user, PanelItemBuilder pib) {
String worldSetting = this.isSetForWorld(user.getWorld()) ? user.getTranslation("protection.panel.flag-item.setting-active")
private PanelItemBuilder createWorldSettingFlag(User user, World world, PanelItemBuilder pib) {
String worldSetting = this.isSetForWorld(world)
? user.getTranslation("protection.panel.flag-item.setting-active")
: user.getTranslation("protection.panel.flag-item.setting-disabled");
pib.description(user.getTranslation("protection.panel.flag-item.setting-layout", TextVariables.DESCRIPTION, user.getTranslation(getDescriptionReference())
, "[setting]", worldSetting));
@ -430,7 +432,7 @@ public class Flag implements Comparable<Flag> {
// Protection flag
pib.description(user.getTranslation("protection.panel.flag-item.description-layout",
TextVariables.DESCRIPTION, user.getTranslation(getDescriptionReference())));
plugin.getRanksManager().getRanks().forEach((reference, score) -> {
RanksManager.getInstance().getRanks().forEach((reference, score) -> {
if (score > RanksManager.BANNED_RANK && score < island.getFlag(this)) {
pib.description(user.getTranslation("protection.panel.flag-item.blocked-rank") + user.getTranslation(reference));
} else if (score <= RanksManager.OWNER_RANK && score > island.getFlag(this)) {
@ -679,11 +681,11 @@ public class Flag implements Comparable<Flag> {
public Flag build() {
// If no clickHandler has been set, then apply default ones
if (clickHandler == null) {
switch (type) {
case SETTING -> clickHandler = new IslandToggleClick(id);
case WORLD_SETTING -> clickHandler = new WorldToggleClick(id);
default -> clickHandler = new CycleClick(id);
}
clickHandler = switch (type) {
case SETTING -> new IslandToggleClick(id);
case WORLD_SETTING -> new WorldToggleClick(id);
default -> new CycleClick(id);
};
}
return new Flag(this);

View File

@ -81,15 +81,14 @@ public class CycleClick implements PanelItem.ClickHandler {
// Shift Left Click toggles player visibility
if (island != null && (user.isOp() || island.isAllowed(user, Flags.CHANGE_SETTINGS) || user.hasPermission(prefix + "admin.settings"))) {
changeOccurred = true;
RanksManager rm = plugin.getRanksManager();
plugin.getFlagsManager().getFlag(id).ifPresent(flag -> {
// Rank
int currentRank = island.getFlag(flag);
if (click.equals(ClickType.LEFT)) {
leftClick(flag, rm, currentRank);
leftClick(flag, currentRank);
} else if (click.equals(ClickType.RIGHT)) {
rightClick(flag, rm, currentRank);
rightClick(flag, currentRank);
} else if (click.equals(ClickType.SHIFT_LEFT) && user2.isOp()) {
leftShiftClick(flag);
@ -109,16 +108,16 @@ public class CycleClick implements PanelItem.ClickHandler {
// Player is not the allowed to change settings.
user.sendMessage("general.errors.insufficient-rank",
TextVariables.RANK,
user.getTranslation(plugin.getRanksManager().getRank(Objects.requireNonNull(island).getRank(user))));
user.getTranslation(RanksManager.getInstance().getRank(Objects.requireNonNull(island).getRank(user))));
}
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_METAL_HIT, 1F, 1F);
}
private void leftClick(Flag flag, RanksManager rm, int currentRank) {
private void leftClick(Flag flag, int currentRank) {
if (currentRank >= maxRank) {
island.setFlag(flag, minRank);
} else {
island.setFlag(flag, rm.getRankUpValue(currentRank));
island.setFlag(flag, RanksManager.getInstance().getRankUpValue(currentRank));
}
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_STONE_BUTTON_CLICK_OFF, 1F, 1F);
// Fire event
@ -132,11 +131,11 @@ public class CycleClick implements PanelItem.ClickHandler {
}
private void rightClick(Flag flag, RanksManager rm, int currentRank) {
private void rightClick(Flag flag, int currentRank) {
if (currentRank <= minRank) {
island.setFlag(flag, maxRank);
} else {
island.setFlag(flag, rm.getRankDownValue(currentRank));
island.setFlag(flag, RanksManager.getInstance().getRankDownValue(currentRank));
}
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1F, 1F);
// Fire event

View File

@ -17,6 +17,7 @@ import world.bentobox.bentobox.api.panels.TabbedPanel;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.lists.Flags;
import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.bentobox.panels.settings.SettingsTab;
import world.bentobox.bentobox.util.Util;
@ -112,7 +113,7 @@ public class IslandToggleClick implements ClickHandler {
// Player is not the allowed to change settings.
user.sendMessage("general.errors.insufficient-rank",
TextVariables.RANK,
user.getTranslation(plugin.getRanksManager().getRank(Objects.requireNonNull(island).getRank(user))));
user.getTranslation(RanksManager.getInstance().getRank(Objects.requireNonNull(island).getRank(user))));
}
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_METAL_HIT, 1F, 1F);

View File

@ -2,6 +2,7 @@ package world.bentobox.bentobox.api.flags.clicklisteners;
import org.bukkit.Bukkit;
import org.bukkit.Sound;
import org.bukkit.World;
import org.bukkit.event.inventory.ClickType;
import world.bentobox.bentobox.BentoBox;
@ -32,12 +33,8 @@ public class WorldToggleClick implements ClickHandler {
@Override
public boolean onClick(Panel panel, User user, ClickType click, int slot) {
// Get the world
if (!plugin.getIWM().inWorld(user.getLocation())) {
user.sendMessage("general.errors.wrong-world");
return true;
}
String reqPerm = plugin.getIWM().getPermissionPrefix(Util.getWorld(user.getWorld())) + "admin.world.settings." + id;
World world = panel.getWorld().orElseThrow(); // The panel must have a world
String reqPerm = plugin.getIWM().getPermissionPrefix(world) + "admin.world.settings." + id;
if (!user.hasPermission(reqPerm)) {
user.sendMessage("general.errors.no-permission", TextVariables.PERMISSION, reqPerm);
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_METAL_HIT, 1F, 1F);
@ -46,31 +43,34 @@ public class WorldToggleClick implements ClickHandler {
// Get flag
plugin.getFlagsManager().getFlag(id).ifPresent(flag -> {
if (click.equals(ClickType.SHIFT_LEFT) && user.isOp()) {
if (!plugin.getIWM().getHiddenFlags(user.getWorld()).contains(flag.getID())) {
plugin.getIWM().getHiddenFlags(user.getWorld()).add(flag.getID());
if (!plugin.getIWM().getHiddenFlags(world).contains(flag.getID())) {
plugin.getIWM().getHiddenFlags(world).add(flag.getID());
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_GLASS_BREAK, 1F, 1F);
} else {
plugin.getIWM().getHiddenFlags(user.getWorld()).remove(flag.getID());
plugin.getIWM().getHiddenFlags(world).remove(flag.getID());
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_NOTE_BLOCK_CHIME, 1F, 1F);
}
// Save changes
plugin.getIWM().getAddon(user.getWorld()).ifPresent(GameModeAddon::saveWorldSettings);
plugin.getIWM().getAddon(world).ifPresent(GameModeAddon::saveWorldSettings);
} else {
// Toggle flag
flag.setSetting(user.getWorld(), !flag.isSetForWorld(user.getWorld()));
flag.setSetting(world, !flag.isSetForWorld(world));
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1F, 1F);
// Fire event
Bukkit.getPluginManager().callEvent(new FlagWorldSettingChangeEvent(user.getWorld(), user.getUniqueId(), flag, flag.isSetForWorld(user.getWorld())));
Bukkit.getPluginManager().callEvent(
new FlagWorldSettingChangeEvent(world, user.getUniqueId(), flag, flag.isSetForWorld(world)));
// Subflag support
if (flag.hasSubflags()) {
// Fire events for all subflags as well
flag.getSubflags().forEach(subflag -> Bukkit.getPluginManager().callEvent(new FlagWorldSettingChangeEvent(user.getWorld(), user.getUniqueId(), subflag, subflag.isSetForWorld(user.getWorld()))));
flag.getSubflags().forEach(
subflag -> Bukkit.getPluginManager().callEvent(new FlagWorldSettingChangeEvent(world,
user.getUniqueId(), subflag, subflag.isSetForWorld(world))));
}
}
// Save world settings
plugin.getIWM().getAddon(Util.getWorld(user.getWorld())).ifPresent(GameModeAddon::saveWorldSettings);
plugin.getIWM().getAddon(Util.getWorld(world)).ifPresent(GameModeAddon::saveWorldSettings);
});
return true;
}

View File

@ -69,5 +69,7 @@ public abstract class Hook {
* Returns an explanation that will be sent to the user to tell them why the hook process did not succeed.
* @return the probable causes why the hook process did not succeed.
*/
public abstract String getFailureCause();
public String getFailureCause() {
return "";
}
}

View File

@ -13,6 +13,7 @@ import org.eclipse.jdt.annotation.NonNull;
import world.bentobox.bentobox.api.panels.builders.PanelBuilder;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.listeners.PanelListenerManager;
import world.bentobox.bentobox.util.heads.HeadGetter;
import world.bentobox.bentobox.util.heads.HeadRequester;
@ -30,18 +31,27 @@ public class Panel implements HeadRequester, InventoryHolder {
private User user;
private String name;
private World world;
private Island island;
/**
* Various types of Panel that can be created.
* Various types of Panels that can be created that use InventoryTypes.
* <br>
* The current list of inventories that cannot be created are:<br>
* <blockquote>
* {@link Type#INVENTORY}, {@link Type#HOPPER},
* {@link Type#DROPPER}, {@link Type#ANVIL}
* </blockquote>
*
* These relate to the Bukkit inventories with INVENTORY being the standard CHEST inventory.
* See {@link org.bukkit.event.inventory.InventoryType}.
* @since 1.7.0
*/
public enum Type {
INVENTORY,
HOPPER,
DROPPER
INVENTORY, HOPPER, DROPPER, ANVIL
}
public Panel() {}
public Panel() {
}
public Panel(String name, Map<Integer, PanelItem> items, int size, User user, PanelListener listener) {
this(name, items, size, user, listener, Type.INVENTORY);
@ -65,28 +75,28 @@ public class Panel implements HeadRequester, InventoryHolder {
pb.getUser(), pb.getListener(), pb.getPanelType());
}
protected void makePanel(String name, Map<Integer, PanelItem> items, int size, User user,
PanelListener listener) {
protected void makePanel(String name, Map<Integer, PanelItem> items, int size, User user, PanelListener listener) {
this.makePanel(name, items, size, user, listener, Type.INVENTORY);
}
/**
* @since 1.7.0
*/
protected void makePanel(String name, Map<Integer, PanelItem> items, int size, User user,
PanelListener listener, Type type) {
protected void makePanel(String name, Map<Integer, PanelItem> items, int size, User user, PanelListener listener,
Type type) {
this.name = name;
this.items = items;
// Create panel
switch (type) {
case INVENTORY -> inventory = Bukkit.createInventory(null, fixSize(size), name);
case HOPPER -> inventory = Bukkit.createInventory(null, InventoryType.HOPPER, name);
case DROPPER -> inventory = Bukkit.createInventory(null, InventoryType.DROPPER, name);
case INVENTORY -> inventory = Bukkit.createInventory(null, fixSize(size), name);
case HOPPER -> inventory = Bukkit.createInventory(null, InventoryType.HOPPER, name);
case DROPPER -> inventory = Bukkit.createInventory(null, InventoryType.DROPPER, name);
case ANVIL -> inventory = Bukkit.createInventory(null, InventoryType.ANVIL, name);
}
// Fill the inventory and return
for (Map.Entry<Integer, PanelItem> en: items.entrySet()) {
for (Map.Entry<Integer, PanelItem> en : items.entrySet()) {
if (en.getKey() < 54) {
inventory.setItem(en.getKey(), en.getValue().getItem());
// Get player head async
@ -97,11 +107,13 @@ public class Panel implements HeadRequester, InventoryHolder {
}
this.listener = listener;
// If the listener is defined, then run setup
if (listener != null) listener.setup();
if (listener != null)
listener.setup();
// If the user is defined, then open panel immediately
this.user = user;
if (user != null) this.open(user);
if (user != null)
this.open(user);
}
private int fixSize(int size) {
@ -113,7 +125,8 @@ public class Panel implements HeadRequester, InventoryHolder {
// Make sure size is a multiple of 9 and is 54 max.
size = size + 8;
size -= (size % 9);
if (size > 54) size = 54;
if (size > 54)
size = 54;
} else {
return 9;
}
@ -194,12 +207,10 @@ public class Panel implements HeadRequester, InventoryHolder {
public void setHead(PanelItem item) {
// Update the panel item
// Find panel item index in items and replace it once more in inventory to update it.
this.items.entrySet().stream().
filter(entry -> entry.getValue() == item).
mapToInt(Map.Entry::getKey).findFirst()
.ifPresent(index ->
// Update item inside inventory to change icon only if item is inside panel.
this.inventory.setItem(index, item.getItem()));
this.items.entrySet().stream().filter(entry -> entry.getValue() == item).mapToInt(Map.Entry::getKey).findFirst()
.ifPresent(index ->
// Update item inside inventory to change icon only if item is inside panel.
this.inventory.setItem(index, item.getItem()));
}
/**
@ -226,5 +237,18 @@ public class Panel implements HeadRequester, InventoryHolder {
this.world = world;
}
/**
* @return the island
*/
public Island getIsland() {
return island;
}
/**
* @param island the island to set
*/
protected void setIsland(Island island) {
this.island = island;
}
}

View File

@ -1,5 +1,6 @@
package world.bentobox.bentobox.api.panels;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
@ -99,7 +100,7 @@ public class PanelItem {
public void setInvisible(boolean invisible) {
this.invisible = invisible;
if (meta != null) {
if (meta != null && !inTest()) {
if (invisible) {
meta.addEnchant(Enchantment.VANISHING_CURSE, 1, true);
meta.removeItemFlags(ItemFlag.HIDE_ENCHANTS);
@ -129,6 +130,9 @@ public class PanelItem {
public void setGlow(boolean glow) {
this.glow = glow;
if (inTest()) {
return;
}
if (meta != null) {
if (glow) {
meta.addEnchant(Enchantment.ARROW_DAMAGE, 0, glow);
@ -140,6 +144,15 @@ public class PanelItem {
}
}
/**
* This checks the stack trace for @Test to determine if a test is calling the code and skips.
* TODO: when we find a way to mock Enchantment, remove this.
* @return true if it's a test.
*/
private boolean inTest() {
return Arrays.stream(Thread.currentThread().getStackTrace()).anyMatch(e -> e.getClassName().endsWith("Test"));
}
/**
* @return the playerHead
*/

View File

@ -15,6 +15,19 @@ import org.eclipse.jdt.annotation.Nullable;
*/
public interface Tab {
/**
* @return the tabbed panel that owns this tab
*/
default TabbedPanel getParentPanel() {
return null;
}
/**
* @param parent set the tabbed panel that owns this tab
*/
default void setParentPanel(TabbedPanel parent) {
}
// The icon that should be shown at the top of the tabbed panel
PanelItem getIcon();

View File

@ -43,7 +43,10 @@ public class TabbedPanel extends Panel implements PanelListener {
*/
public TabbedPanel(TabbedPanelBuilder tpb) {
this.tpb = tpb;
// Set world
this.setWorld(tpb.getWorld());
// Set island context in Panel
this.setIsland(tpb.getIsland());
}
/* (non-Javadoc)
@ -208,4 +211,5 @@ public class TabbedPanel extends Panel implements PanelListener {
public void setActiveTab(int activeTab) {
this.activeTab = activeTab;
}
}

View File

@ -3,10 +3,8 @@
// Copyright - 2021
//
package world.bentobox.bentobox.api.panels;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
@ -22,20 +20,20 @@ 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
{
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.
*
* @param builder Builder that contains all information about the panel that
* must be generated.
*/
public TemplatedPanel(@NonNull TemplatedPanelBuilder builder)
{
public TemplatedPanel(@NonNull TemplatedPanelBuilder builder) {
this.user = builder.getUser();
this.setWorld(builder.getWorld());
this.setListener(builder.getListener());
@ -48,218 +46,63 @@ public class TemplatedPanel extends Panel
this.parameters = builder.getParameters().toArray(new String[0]);
if (this.panelTemplate == null)
{
if (this.panelTemplate == null) {
BentoBox.getInstance().logError("Cannot generate panel because template is not loaded.");
}
else
{
} 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();
};
private void generatePanel() {
Map<Integer, PanelItem> items = switch (this.panelTemplate.type()) {
case INVENTORY -> this.populateInventoryPanel(new PanelItem[6][9]);
case HOPPER -> this.populateInventoryPanel(new PanelItem[1][5]);
case DROPPER -> this.populateInventoryPanel(new PanelItem[3][3]);
case ANVIL -> this.populateInventoryPanel(new PanelItem[4][9]);
};
super.makePanel(this.user.getTranslation(this.panelTemplate.title(), this.parameters),
items,
items.keySet().stream().max(Comparator.naturalOrder()).orElse(9),
this.user,
this.getListener().orElse(null),
this.panelTemplate.type());
super.makePanel(this.user.getTranslation(this.panelTemplate.title(), this.parameters), 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.
* 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() {
PanelItem[][] itemArray = new PanelItem[6][9];
processItemData(itemArray);
removeEmptyLines(itemArray);
fillBorder(itemArray);
fillBackground(itemArray);
return createItemMap(itemArray);
private Map<Integer, PanelItem> populateInventoryPanel(PanelItem[][] itemArray) {
this.preProcessPanelTemplate(itemArray);
this.processItemData(itemArray);
this.removeEmptyLines(itemArray);
this.fillBorder(itemArray);
this.fillBackground(itemArray);
return this.createItemMap(itemArray);
}
private void processItemData(PanelItem[][] itemArray) {
/**
* This method processes what items should be added into the panel. It counts
* how many same type buttons should be generated. This cannot be done in the
* same step as creating button.
*
* @param itemArray The double array with items into panel
*/
private void preProcessPanelTemplate(PanelItem[][] itemArray) {
final int numRows = itemArray.length;
final int numCols = itemArray[0].length;
// Analyze the GUI button layout a bit.
for (int i = 0; i < panelTemplate.content().length; i++) {
for (int k = 0; k < panelTemplate.content()[i].length; k++) {
ItemTemplateRecord rec = panelTemplate.content()[i][k];
if (rec != null && rec.dataMap().containsKey("type")) {
String type = String.valueOf(rec.dataMap().get("type"));
int counter = typeSlotMap.computeIfAbsent(type, key -> 0);
typeSlotMap.put(type, counter + 1);
}
// Make buttons for the GUI
itemArray[i][k] = makeButton(panelTemplate.content()[i][k]);
}
}
}
private void removeEmptyLines(PanelItem[][] itemArray) {
boolean[] showLine = panelTemplate.forcedRows();
for (int i = 0; i < panelTemplate.content().length; i++) {
boolean emptyLine = true;
for (int k = 0; emptyLine && k < panelTemplate.content()[i].length; k++) {
emptyLine = itemArray[i][k] == null;
}
showLine[i] = showLine[i] || !emptyLine;
}
}
private void fillBorder(PanelItem[][] itemArray) {
if (panelTemplate.border() == null) {
return;
}
PanelItem template = makeTemplate(panelTemplate.border());
int numRows = itemArray.length;
int numCols = itemArray[0].length;
for (int i = 0; i < numRows; i++) {
if (i == 0 || i == numRows - 1) {
// Fill first and last row completely with border.
for (int k = 0; k < numCols; 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][numCols - 1] == null) {
itemArray[i][numCols - 1] = template;
}
}
}
panelTemplate.forcedRows()[0] = true;
panelTemplate.forcedRows()[numRows - 1] = true;
}
private void fillBackground(PanelItem[][] itemArray) {
if (panelTemplate.background() != null) {
PanelItem template = makeTemplate(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;
}
}
}
}
}
private Map<Integer, PanelItem> createItemMap(PanelItem[][] itemArray) {
Map<Integer, PanelItem> itemMap = new HashMap<>(6 * 9);
int correctIndex = 0;
for (int i = 0; i < itemArray.length; i++) {
final boolean iterate = panelTemplate.forcedRows()[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 rec = this.panelTemplate.content()[0][i];
if (rec != null && rec.dataMap().containsKey("type"))
{
String type = String.valueOf(rec.dataMap().get("type"));
int counter = this.typeSlotMap.computeIfAbsent(type, key -> 0);
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++)
{
for (int k = 0; k < numCols; k++) {
ItemTemplateRecord rec = this.panelTemplate.content()[i][k];
if (rec != null && rec.dataMap().containsKey("type"))
{
if (rec != null && rec.dataMap().containsKey("type")) {
String type = String.valueOf(rec.dataMap().get("type"));
int counter = this.typeSlotMap.computeIfAbsent(type, key -> 0);
@ -267,47 +110,114 @@ public class TemplatedPanel extends Panel
}
}
}
}
// Init item array with the max available size.
PanelItem[][] itemArray = new PanelItem[3][3];
/**
* This method populates item array with all buttons.
*
* @param itemArray The double array with items into panel
*/
private void processItemData(PanelItem[][] itemArray) {
final int numRows = itemArray.length;
final int numCols = itemArray[0].length;
// Make buttons
for (int i = 0; i < 3; i++)
{
for (int k = 0; k < 3; k++)
{
for (int i = 0; i < numRows; i++) {
for (int k = 0; k < numCols; 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());
/**
* This method removes all empty lines if they are not forced to be showed.
*
* @param itemArray The double array with items into panel
*/
private void removeEmptyLines(PanelItem[][] itemArray) {
// After items are created, remove empty lines.
boolean[] showLine = this.panelTemplate.forcedRows();
for (int i = 0; i < 3; i++)
{
for (int k = 0; k < 3; k++)
{
if (itemArray[i][k] == null)
{
itemArray[i][k] = template;
final int numRows = itemArray.length;
final int numCols = itemArray[0].length;
for (int i = 0; i < numRows; i++) {
boolean emptyLine = true;
for (int k = 0; emptyLine && k < numCols; k++) {
emptyLine = itemArray[i][k] == null;
}
// Do not generate fallback for "empty" lines.
showLine[i] = showLine[i] || !emptyLine;
}
}
/**
* Fills the border of a panel with a template item.
*
* @param itemArray 2D array of panel items
*/
private void fillBorder(PanelItem[][] itemArray) {
if (this.panelTemplate.border() == null)
return;
PanelItem template = makeTemplate(this.panelTemplate.border());
int numRows = itemArray.length;
int numCols = itemArray[0].length;
for (int row = 0; row < numRows; row++) {
for (int col = 0; col < numCols; col++) {
// Fill border rows completely, and first/last columns of other rows
if (row == 0 || row == numRows - 1 || col == 0 || col == numCols - 1) {
if (itemArray[row][col] == null) {
itemArray[row][col] = template;
}
}
}
}
// Init item map with the max available size.
Map<Integer, PanelItem> itemMap = new HashMap<>(9);
this.panelTemplate.forcedRows()[0] = true;
this.panelTemplate.forcedRows()[numRows - 1] = true;
}
/**
* This method fills background elements with item from template.
*
* @param itemArray The double array with items into panel
*/
private void fillBackground(PanelItem[][] itemArray) {
if (this.panelTemplate.background() == null) {
return;
}
PanelItem template = this.makeTemplate(this.panelTemplate.background());
final int numRows = itemArray.length;
final int numCols = itemArray[0].length;
for (int i = 0; i < numRows; i++) {
for (int k = 0; k < numCols; k++) {
if (itemArray[i][k] == null) {
itemArray[i][k] = template;
}
}
}
}
/**
* This method converts to PanelItem array to the correct item map.
*
* @param itemArray The double array with items into panel
* @return The map that links index of button to panel item.
*/
private Map<Integer, PanelItem> createItemMap(PanelItem[][] itemArray) {
Map<Integer, PanelItem> itemMap = new HashMap<>(itemArray.length * itemArray[0].length);
int correctIndex = 0;
for (int i = 0; i < 3; i++)
{
for (int k = 0; k < 3; k++)
{
if (itemArray[i][k] != null)
{
for (int i = 0; i < itemArray.length; i++) {
final boolean iterate = this.panelTemplate.forcedRows()[i];
for (int k = 0; iterate && k < itemArray[i].length; k++) {
if (itemArray[i][k] != null) {
itemMap.put(correctIndex, itemArray[i][k]);
}
@ -318,43 +228,36 @@ public class TemplatedPanel extends Panel
return itemMap;
}
/**
* This method passes button creation from given record template.
*
* @param rec Template of the button that must be created.
* @return PanelItem of the template, otherwise null.
*/
@Nullable
private PanelItem makeButton(@Nullable ItemTemplateRecord rec)
{
if (rec == null)
{
private PanelItem makeButton(@Nullable ItemTemplateRecord rec) {
if (rec == null) {
// Immediate exit if record is null.
return null;
}
if (rec.dataMap().containsKey("type"))
{
// If dataMap is not null, and it is not empty, then pass button to the object creator function.
if (rec.dataMap().containsKey("type")) {
// If dataMap is not null, and it is not empty, then pass button to the object
// creator function.
return this.makeAddonButton(rec);
}
else
{
} else {
PanelItemBuilder itemBuilder = new PanelItemBuilder();
if (rec.icon() != null)
{
if (rec.icon() != null) {
itemBuilder.icon(rec.icon().clone());
}
if (rec.title() != null)
{
if (rec.title() != null) {
itemBuilder.name(this.user.getTranslation(rec.title()));
}
if (rec.description() != null)
{
if (rec.description() != null) {
itemBuilder.description(this.user.getTranslation(rec.description()));
}
@ -366,20 +269,18 @@ public class TemplatedPanel extends Panel
}
}
/**
* This method passes button to the type creator, if that exists.
*
* @param rec Template of the button that must be created.
* @return PanelItem of the button created by typeCreator, otherwise null.
*/
@Nullable
private PanelItem makeAddonButton(@NonNull ItemTemplateRecord rec)
{
private PanelItem makeAddonButton(@NonNull ItemTemplateRecord rec) {
// Get object type.
String type = String.valueOf(rec.dataMap().getOrDefault("type", ""));
if (!this.typeCreators.containsKey(type))
{
if (!this.typeCreators.containsKey(type)) {
// There are no object with a given type.
return this.makeFallBack(rec.fallback());
}
@ -387,9 +288,7 @@ public class TemplatedPanel extends Panel
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);
ItemSlot itemSlot = this.typeIndex.containsKey(type) ? this.typeIndex.get(type) : new ItemSlot(0, this);
this.typeIndex.put(type, itemSlot.nextItemSlot());
// Try to get next object.
@ -397,43 +296,38 @@ public class TemplatedPanel extends Panel
return item == null ? this.makeFallBack(rec.fallback()) : item;
}
/**
* This method creates a fall back button for given record.
*
* @param rec Record which fallback must be created.
* @return PanelItem if fallback was creates successfully, otherwise null.
*/
@Nullable
private PanelItem makeFallBack(@Nullable ItemTemplateRecord rec)
{
private PanelItem makeFallBack(@Nullable ItemTemplateRecord rec) {
return rec == null ? null : this.makeButton(rec.fallback());
}
/**
* This method translates template record into a panel item.
*
* @param rec Record that must be translated.
* @return PanelItem that contains all information from the record.
*/
private PanelItem makeTemplate(PanelTemplateRecord.TemplateItem rec)
{
private PanelItem makeTemplate(PanelTemplateRecord.TemplateItem rec) {
PanelItemBuilder itemBuilder = new PanelItemBuilder();
// Read icon only if it is not null.
if (rec.icon() != null)
{
if (rec.icon() != null) {
itemBuilder.icon(rec.icon().clone());
}
// Read title only if it is not null.
if (rec.title() != null)
{
if (rec.title() != null) {
itemBuilder.name(this.user.getTranslation(rec.title()));
}
// Read description only if it is not null.
if (rec.description() != null)
{
if (rec.description() != null) {
itemBuilder.description(this.user.getTranslation(rec.description()));
}
@ -441,37 +335,67 @@ public class TemplatedPanel extends Panel
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.
* 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 parentPanel The parent panel for current Item.
*/
public record ItemSlot(int slot, Map<String, Integer> amountMap)
{
public record ItemSlot(int slot, TemplatedPanel parentPanel) {
/**
* 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());
ItemSlot nextItemSlot() {
return new ItemSlot(this.slot() + 1, this.parentPanel());
}
}
/**
* This method returns map that links button types with a number of slots that
* this button is present.
*
* @return Map that links button type to amount in the gui.
* @deprecated Use {@link #amount(String)} instead.
*/
@Deprecated
public Map<String, Integer> amountMap() {
return this.parentPanel.typeSlotMap;
}
/**
* This returns amount of slots for given button type.
*
* @param type Type of the button.
* @return Number of slots in panel.
*/
public int amount(String type) {
return this.amount(type, 0);
}
/**
* This returns amount of slots for given button type.
*
* @param type Type of the button.
* @param defaultValue The default value if the type is not found
* @return Number of slots in panel.
*/
public int amount(String type, int defaultValue) {
return this.parentPanel.typeSlotMap.getOrDefault(type, defaultValue);
}
}
// ---------------------------------------------------------------------
// Section: Variables
// ---------------------------------------------------------------------
/**
* The GUI template record.
*/
@ -499,6 +423,7 @@ public class TemplatedPanel extends Panel
/**
* Stores the parameters for panel title object.
*
* @since 1.20.0
*/
private final String[] parameters;

View File

@ -9,6 +9,7 @@ import org.bukkit.World;
import world.bentobox.bentobox.api.panels.Tab;
import world.bentobox.bentobox.api.panels.TabbedPanel;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
/**
* Builds {@link TabbedPanel}'s
@ -23,6 +24,17 @@ public class TabbedPanelBuilder {
private World world;
private User user;
private boolean hideIfEmpty;
private Island island;
/**
* Set the island related to this panel
* @param island island
* @return PanelBuilder - PanelBuilder
*/
public TabbedPanelBuilder island(Island island) {
this.island = island;
return this;
}
/**
* Forces panel to be a specific number of slots.
@ -97,7 +109,10 @@ public class TabbedPanelBuilder {
if (!tabs.isEmpty() && !tabs.containsKey(startingSlot)) {
startingSlot = ((TreeMap<Integer, Tab>)tabs).firstKey();
}
return new TabbedPanel(this);
TabbedPanel tp = new TabbedPanel(this);
// Set tab parents
tabs.values().forEach(tab -> tab.setParentPanel(tp));
return tp;
}
/**
@ -142,6 +157,12 @@ public class TabbedPanelBuilder {
return hideIfEmpty;
}
/**
* @return the island
*/
public Island getIsland() {
return island;
}
}

View File

@ -3,10 +3,8 @@
// Copyright - 2021
//
package world.bentobox.bentobox.api.panels.reader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -29,13 +27,32 @@ import org.eclipse.jdt.annotation.Nullable;
*
* @since 1.17.3
*/
public record ItemTemplateRecord(@Nullable ItemStack icon,
public record ItemTemplateRecord(
/**
* ItemStack of the Item
*/
@Nullable ItemStack icon,
/**
* Title of the item
*/
@Nullable String title,
/**
* Lore message of the item
*/
@Nullable String description,
/**
* List of Actions for a button
*/
@NonNull List<ActionRecords> actions,
/**
* DataMap that links additional objects for a button.
*/
@NonNull Map<String, Object> dataMap,
@Nullable ItemTemplateRecord fallback)
{
/**
* FallBack item if current one is not possible to generate.
*/
@Nullable ItemTemplateRecord fallback) {
/**
* Instantiates a new Item template record without actions and data map.
*
@ -44,39 +61,32 @@ public record ItemTemplateRecord(@Nullable ItemStack icon,
* @param description the description
* @param fallback the fallback
*/
public ItemTemplateRecord(ItemStack icon, String title, String description, ItemTemplateRecord 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)
{
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)
{
public void addAction(ActionRecords actionData) {
this.actions.add(actionData);
}
// ---------------------------------------------------------------------
// Section: Classes
// ---------------------------------------------------------------------
/**
* The Action Records holds data about each action.
*
@ -85,5 +95,22 @@ public record ItemTemplateRecord(@Nullable ItemStack icon,
* @param content the content of the action
* @param tooltip the tooltip of action
*/
public record ActionRecords(ClickType clickType, String actionType, String content, String tooltip) {}
public record ActionRecords(
/**
* the click type
*/
ClickType clickType,
/**
* the string that represents action type
*/
String actionType,
/**
* the content of the action
*/
String content,
/**
* the tooltip of action
*/
String tooltip) {
}
}

View File

@ -22,6 +22,7 @@ import org.eclipse.jdt.annotation.Nullable;
import com.google.common.base.Enums;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.panels.Panel;
import world.bentobox.bentobox.util.ItemParser;
@ -83,6 +84,7 @@ public class TemplateReader
{
if (!panelLocation.exists())
{
BentoBox.getInstance().logError("Panel Template reader: Folder does not exist");
// Return null because folder does not exist.
return null;
}
@ -91,6 +93,7 @@ public class TemplateReader
if (!file.exists())
{
BentoBox.getInstance().logError(file.getAbsolutePath() + " does not exist for panel template");
// Return as file does not exist.
return null;
}
@ -117,6 +120,8 @@ public class TemplateReader
}
catch (IOException | InvalidConfigurationException e)
{
BentoBox.getInstance().logError("Error loading template");
BentoBox.getInstance().logStacktrace(e);
rec = null;
}
@ -133,6 +138,7 @@ public class TemplateReader
{
if (configurationSection == null)
{
BentoBox.getInstance().logError("No configuration section!");
// No data to return.
return null;
}

View File

@ -42,13 +42,15 @@ import world.bentobox.bentobox.database.objects.Players;
import world.bentobox.bentobox.util.Util;
/**
* Combines {@link Player}, {@link OfflinePlayer} and {@link CommandSender} to provide convenience methods related to
* localization and generic interactions.
* Combines {@link Player}, {@link OfflinePlayer} and {@link CommandSender} to
* provide convenience methods related to localization and generic interactions.
* <br/>
* Therefore, a User could usually be a Player, an OfflinePlayer or the server's console.
* Preliminary checks should be performed before trying to run methods that relies on a specific implementation.
* <br/><br/>
* It is good practice to use the User instance whenever possible instead of Player or CommandSender.
* Therefore, a User could usually be a Player, an OfflinePlayer or the server's
* console. Preliminary checks should be performed before trying to run methods
* that relies on a specific implementation. <br/>
* <br/>
* It is good practice to use the User instance whenever possible instead of
* Player or CommandSender.
*
* @author tastybento
*/
@ -85,6 +87,7 @@ public class User implements MetaDataAble {
/**
* Gets an instance of User from a CommandSender
*
* @param sender - command sender, e.g. console
* @return user - user
*/
@ -99,6 +102,7 @@ public class User implements MetaDataAble {
/**
* Gets an instance of User from a Player object.
*
* @param player - the player
* @return user - user
*/
@ -113,6 +117,7 @@ public class User implements MetaDataAble {
/**
* Gets an instance of User from a UUID. This will always return a user object.
* If the player is offline then the getPlayer value will be null.
*
* @param uuid - UUID
* @return user - user
*/
@ -127,6 +132,7 @@ public class User implements MetaDataAble {
/**
* Gets an instance of User from an OfflinePlayer
*
* @param offlinePlayer offline Player
* @return user
* @since 1.3.0
@ -141,6 +147,7 @@ public class User implements MetaDataAble {
/**
* Removes this player from the User cache and player manager cache
*
* @param player the player
*/
public static void removePlayer(Player player) {
@ -193,6 +200,7 @@ public class User implements MetaDataAble {
/**
* Used for testing
*
* @param p - plugin
*/
public static void setPlugin(BentoBox p) {
@ -205,6 +213,7 @@ public class User implements MetaDataAble {
/**
* Get the user's inventory
*
* @return player's inventory
*/
@NonNull
@ -214,6 +223,7 @@ public class User implements MetaDataAble {
/**
* Get the user's location
*
* @return location
*/
@NonNull
@ -223,6 +233,7 @@ public class User implements MetaDataAble {
/**
* Get the user's name
*
* @return player's name
*/
@NonNull
@ -232,7 +243,9 @@ public class User implements MetaDataAble {
/**
* Get the user's display name
* @return player's display name if the player is online otherwise just their name
*
* @return player's display name if the player is online otherwise just their
* name
* @since 1.22.1
*/
@NonNull
@ -242,6 +255,7 @@ public class User implements MetaDataAble {
/**
* Check if the User is a player before calling this method. {@link #isPlayer()}
*
* @return the player
*/
@NonNull
@ -258,6 +272,7 @@ public class User implements MetaDataAble {
/**
* Use {@link #isOfflinePlayer()} before calling this method
*
* @return the offline player
* @since 1.3.0
*/
@ -285,7 +300,8 @@ public class User implements MetaDataAble {
/**
* @param permission permission string
* @return true if permission is empty or null or if the player has that permission or if the player is op.
* @return true if permission is empty or null or if the player has that
* permission or if the player is op.
*/
public boolean hasPermission(@Nullable String permission) {
return permission == null || permission.isEmpty() || isOp() || sender.hasPermission(permission);
@ -293,6 +309,7 @@ public class User implements MetaDataAble {
/**
* Removes permission from user
*
* @param name - Name of the permission to remove
* @return true if successful
* @since 1.5.0
@ -310,6 +327,7 @@ public class User implements MetaDataAble {
/**
* Add a permission to user
*
* @param name - Name of the permission to attach
* @return The PermissionAttachment that was just created
* @since 1.5.0
@ -324,6 +342,7 @@ public class User implements MetaDataAble {
/**
* Checks if user is Op
*
* @return true if user is Op
*/
public boolean isOp() {
@ -337,30 +356,42 @@ public class User implements MetaDataAble {
}
/**
* Get the maximum value of a numerical permission setting.
* If a player is given an explicit negative number then this is treated as "unlimited" and returned immediately.
* @param permissionPrefix the start of the perm, e.g., {@code plugin.mypermission}
* @param defaultValue the default value; the result may be higher or lower than this
* Get the maximum value of a numerical permission setting. If a player is given
* an explicit negative number then this is treated as "unlimited" and returned
* immediately.
*
* @param permissionPrefix the start of the perm, e.g.,
* {@code plugin.mypermission}
* @param defaultValue the default value; the result may be higher or lower
* than this
* @return max value
*/
public int getPermissionValue(String permissionPrefix, int defaultValue) {
// If requester is console, then return the default value
if (!isPlayer()) return defaultValue;
if (!isPlayer())
return defaultValue;
// If there is a dot at the end of the permissionPrefix, remove it
if (permissionPrefix.endsWith(".")) {
permissionPrefix = permissionPrefix.substring(0, permissionPrefix.length()-1);
permissionPrefix = permissionPrefix.substring(0, permissionPrefix.length() - 1);
}
final String permPrefix = permissionPrefix + ".";
List<String> permissions = player.getEffectivePermissions().stream()
.filter(PermissionAttachmentInfo::getValue) // Must be a positive permission, not a negative one
.map(PermissionAttachmentInfo::getPermission)
.filter(permission -> permission.startsWith(permPrefix))
List<String> permissions = player.getEffectivePermissions().stream().filter(PermissionAttachmentInfo::getValue) // Must
// be
// a
// positive
// permission,
// not
// a
// negative
// one
.map(PermissionAttachmentInfo::getPermission).filter(permission -> permission.startsWith(permPrefix))
.toList();
if (permissions.isEmpty()) return defaultValue;
if (permissions.isEmpty())
return defaultValue;
return iteratePerms(permissions, permPrefix, defaultValue);
@ -376,7 +407,8 @@ public class User implements MetaDataAble {
String[] spl = permission.split(permPrefix);
if (spl.length > 1) {
if (!NumberUtils.isNumber(spl[1])) {
plugin.logError("Player " + player.getName() + " has permission: '" + permission + "' <-- the last part MUST be a number! Ignoring...");
plugin.logError("Player " + player.getName() + " has permission: '" + permission
+ "' <-- the last part MUST be a number! Ignoring...");
} else {
int v = Integer.parseInt(spl[1]);
if (v < 0) {
@ -393,27 +425,32 @@ public class User implements MetaDataAble {
/**
* Gets a translation for a specific world
* @param world - world of translation
*
* @param world - world of translation
* @param reference - reference found in a locale file
* @param variables - variables to insert into translated string. Variables go in pairs, for example
* "[name]", "tastybento"
* @return Translated string with colors converted, or the reference if nothing has been found
* @param variables - variables to insert into translated string. Variables go
* in pairs, for example "[name]", "tastybento"
* @return Translated string with colors converted, or the reference if nothing
* has been found
* @since 1.3.0
*/
public String getTranslation(World world, String reference, String... variables) {
// Get translation.
String addonPrefix = plugin.getIWM()
.getAddon(world).map(a -> a.getDescription().getName().toLowerCase(Locale.ENGLISH) + ".").orElse("");
String addonPrefix = plugin.getIWM().getAddon(world)
.map(a -> a.getDescription().getName().toLowerCase(Locale.ENGLISH) + ".").orElse("");
return Util.translateColorCodes(translate(addonPrefix, reference, variables));
}
/**
* Gets a translation of this reference for this user with colors converted. Translations may be overridden by Addons
* by using the same reference prefixed by the addon name (from the Addon Description) in lower case.
* Gets a translation of this reference for this user with colors converted.
* Translations may be overridden by Addons by using the same reference prefixed
* by the addon name (from the Addon Description) in lower case.
*
* @param reference - reference found in a locale file
* @param variables - variables to insert into translated string. Variables go in pairs, for example
* "[name]", "tastybento"
* @return Translated string with colors converted, or the reference if nothing has been found
* @param variables - variables to insert into translated string. Variables go
* in pairs, for example "[name]", "tastybento"
* @return Translated string with colors converted, or the reference if nothing
* has been found
*/
public String getTranslation(String reference, String... variables) {
// Get addonPrefix
@ -422,11 +459,13 @@ public class User implements MetaDataAble {
}
/**
* Gets a translation of this reference for this user without colors translated. Translations may be overridden by Addons
* by using the same reference prefixed by the addon name (from the Addon Description) in lower case.
* Gets a translation of this reference for this user without colors translated.
* Translations may be overridden by Addons by using the same reference prefixed
* by the addon name (from the Addon Description) in lower case.
*
* @param reference - reference found in a locale file
* @param variables - variables to insert into translated string. Variables go in pairs, for example
* "[name]", "tastybento"
* @param variables - variables to insert into translated string. Variables go
* in pairs, for example "[name]", "tastybento"
* @return Translated string or the reference if nothing has been found
* @since 1.17.4
*/
@ -461,9 +500,11 @@ public class User implements MetaDataAble {
for (String prefix : plugin.getLocalesManager().getAvailablePrefixes(this)) {
String prefixTranslation = getTranslation("prefixes." + prefix);
// Replace the [gamemode] text variable
prefixTranslation = prefixTranslation.replace("[gamemode]", addon != null ? addon.getDescription().getName() : "[gamemode]");
prefixTranslation = prefixTranslation.replace("[gamemode]",
addon != null ? addon.getDescription().getName() : "[gamemode]");
// Replace the [friendly_name] text variable
prefixTranslation = prefixTranslation.replace("[friendly_name]", isPlayer() ? plugin.getIWM().getFriendlyName(getWorld()) : "[friendly_name]");
prefixTranslation = prefixTranslation.replace("[friendly_name]",
isPlayer() ? plugin.getIWM().getFriendlyName(getWorld()) : "[friendly_name]");
// Replace the prefix in the actual message
translation = translation.replace("[prefix_" + prefix + "]", prefixTranslation);
@ -506,10 +547,12 @@ public class User implements MetaDataAble {
/**
* Gets a translation of this reference for this user.
*
* @param reference - reference found in a locale file
* @param variables - variables to insert into translated string. Variables go in pairs, for example
* "[name]", "tastybento"
* @return Translated string with colors converted, or a blank String if nothing has been found
* @param variables - variables to insert into translated string. Variables go
* in pairs, for example "[name]", "tastybento"
* @return Translated string with colors converted, or a blank String if nothing
* has been found
*/
public String getTranslationOrNothing(String reference, String... variables) {
String translation = getTranslation(reference, variables);
@ -518,6 +561,7 @@ public class User implements MetaDataAble {
/**
* Send a message to sender if message is not empty.
*
* @param reference - language file reference
* @param variables - CharSequence target, replacement pairs
*/
@ -529,7 +573,9 @@ public class User implements MetaDataAble {
}
/**
* Sends a message to sender without any modification (colors, multi-lines, placeholders).
* Sends a message to sender without any modification (colors, multi-lines,
* placeholders).
*
* @param message - the message to send
*/
public void sendRawMessage(String message) {
@ -542,7 +588,9 @@ public class User implements MetaDataAble {
}
/**
* Sends a message to sender if message is not empty and if the same wasn't sent within the previous Notifier.NOTIFICATION_DELAY seconds.
* Sends a message to sender if message is not empty and if the same wasn't sent
* within the previous Notifier.NOTIFICATION_DELAY seconds.
*
* @param reference - language file reference
* @param variables - CharSequence target, replacement pairs
*
@ -556,8 +604,10 @@ public class User implements MetaDataAble {
}
/**
* Sends a message to sender if message is not empty and if the same wasn't sent within the previous Notifier.NOTIFICATION_DELAY seconds.
* @param world - the world the translation should come from
* Sends a message to sender if message is not empty and if the same wasn't sent
* within the previous Notifier.NOTIFICATION_DELAY seconds.
*
* @param world - the world the translation should come from
* @param reference - language file reference
* @param variables - CharSequence target, replacement pairs
*
@ -573,6 +623,7 @@ public class User implements MetaDataAble {
/**
* Sets the user's game mode
*
* @param mode - GameMode
*/
public void setGameMode(GameMode mode) {
@ -580,7 +631,9 @@ public class User implements MetaDataAble {
}
/**
* Teleports user to this location. If the user is in a vehicle, they will exit first.
* Teleports user to this location. If the user is in a vehicle, they will exit
* first.
*
* @param location - the location
*/
public void teleport(Location location) {
@ -589,6 +642,7 @@ public class User implements MetaDataAble {
/**
* Gets the current world this entity resides in
*
* @return World - world
*/
@NonNull
@ -606,6 +660,7 @@ public class User implements MetaDataAble {
/**
* Get the user's locale
*
* @return Locale
*/
public Locale getLocale() {
@ -616,8 +671,8 @@ public class User implements MetaDataAble {
}
/**
* Forces an update of the user's complete inventory.
* Deprecated, but there is no current alternative.
* Forces an update of the user's complete inventory. Deprecated, but there is
* no current alternative.
*/
public void updateInventory() {
player.updateInventory();
@ -625,6 +680,7 @@ public class User implements MetaDataAble {
/**
* Performs a command as the player
*
* @param command - command to execute
* @return true if the command was successful, otherwise false
*/
@ -634,7 +690,8 @@ public class User implements MetaDataAble {
// only perform the command, if the event wasn't cancelled by an other plugin:
if (!event.isCancelled()) {
return getPlayer().performCommand(event.getMessage());
return getPlayer().performCommand(
event.getMessage().startsWith("/") ? event.getMessage().substring(1) : event.getMessage());
}
// Cancelled, but it was recognized, so return true
return true;
@ -642,6 +699,7 @@ public class User implements MetaDataAble {
/**
* Checks if a user is in one of the game worlds
*
* @return true if user is, false if not
*/
public boolean inWorld() {
@ -649,80 +707,74 @@ public class User implements MetaDataAble {
}
/**
* Spawn particles to the player.
* They are only displayed if they are within the server's view distance.
* @param particle Particle to display.
* @param dustOptions Particle.DustOptions for the particle to display.
* Cannot be null when particle is {@link Particle#REDSTONE}.
* @param x X coordinate of the particle to display.
* @param y Y coordinate of the particle to display.
* @param z Z coordinate of the particle to display.
* Spawn particles to the player. They are only displayed if they are within the
* server's view distance.
*
* @param particle Particle to display.
* @param dustOptions Particle.DustOptions for the particle to display. Cannot
* be null when particle is {@link Particle#REDSTONE}.
* @param x X coordinate of the particle to display.
* @param y Y coordinate of the particle to display.
* @param z Z coordinate of the particle to display.
*/
public void spawnParticle(Particle particle, @Nullable Object dustOptions, double x, double y, double z)
{
public void spawnParticle(Particle particle, @Nullable Object dustOptions, double x, double y, double z) {
Class<?> expectedClass = VALIDATION_CHECK.get(particle);
if (expectedClass == null) throw new IllegalArgumentException("Unexpected value: " + particle);
if (expectedClass == null)
throw new IllegalArgumentException("Unexpected value: " + particle);
if (!(expectedClass.isInstance(dustOptions))) {
throw new IllegalArgumentException("A non-null " + expectedClass.getSimpleName() + " must be provided when using Particle." + particle + " as particle.");
throw new IllegalArgumentException("A non-null " + expectedClass.getSimpleName()
+ " must be provided when using Particle." + particle + " as particle.");
}
// Check if this particle is beyond the viewing distance of the server
if (this.player != null
&& this.player.getLocation().toVector().distanceSquared(new Vector(x, y, z)) <
(Bukkit.getServer().getViewDistance() * 256 * Bukkit.getServer().getViewDistance()))
{
if (particle.equals(Particle.REDSTONE))
{
if (this.player != null && this.player.getLocation().toVector().distanceSquared(new Vector(x, y,
z)) < (Bukkit.getServer().getViewDistance() * 256 * Bukkit.getServer().getViewDistance())) {
if (particle.equals(Particle.REDSTONE)) {
player.spawnParticle(particle, x, y, z, 1, 0, 0, 0, 1, dustOptions);
}
else if (dustOptions != null)
{
} else if (dustOptions != null) {
player.spawnParticle(particle, x, y, z, 1, dustOptions);
}
else
{
// This will never be called unless the value in VALIDATION_CHECK is null in the future
} else {
// This will never be called unless the value in VALIDATION_CHECK is null in the
// future
player.spawnParticle(particle, x, y, z, 1);
}
}
}
/**
* Spawn particles to the player.
* They are only displayed if they are within the server's view distance.
* Compatibility method for older usages.
* @param particle Particle to display.
* @param dustOptions Particle.DustOptions for the particle to display.
* Cannot be null when particle is {@link Particle#REDSTONE}.
* @param x X coordinate of the particle to display.
* @param y Y coordinate of the particle to display.
* @param z Z coordinate of the particle to display.
* Spawn particles to the player. They are only displayed if they are within the
* server's view distance. Compatibility method for older usages.
*
* @param particle Particle to display.
* @param dustOptions Particle.DustOptions for the particle to display. Cannot
* be null when particle is {@link Particle#REDSTONE}.
* @param x X coordinate of the particle to display.
* @param y Y coordinate of the particle to display.
* @param z Z coordinate of the particle to display.
*/
public void spawnParticle(Particle particle, Particle.DustOptions dustOptions, double x, double y, double z)
{
public void spawnParticle(Particle particle, Particle.DustOptions dustOptions, double x, double y, double z) {
this.spawnParticle(particle, (Object) dustOptions, x, y, z);
}
/**
* Spawn particles to the player.
* They are only displayed if they are within the server's view distance.
* @param particle Particle to display.
* @param dustOptions Particle.DustOptions for the particle to display.
* Cannot be null when particle is {@link Particle#REDSTONE}.
* @param x X coordinate of the particle to display.
* @param y Y coordinate of the particle to display.
* @param z Z coordinate of the particle to display.
* Spawn particles to the player. They are only displayed if they are within the
* server's view distance.
*
* @param particle Particle to display.
* @param dustOptions Particle.DustOptions for the particle to display. Cannot
* be null when particle is {@link Particle#REDSTONE}.
* @param x X coordinate of the particle to display.
* @param y Y coordinate of the particle to display.
* @param z Z coordinate of the particle to display.
*/
public void spawnParticle(Particle particle, Particle.DustOptions dustOptions, int x, int y, int z)
{
public void spawnParticle(Particle particle, Particle.DustOptions dustOptions, int x, int y, int z) {
this.spawnParticle(particle, dustOptions, (double) x, (double) y, (double) z);
}
/* (non-Javadoc)
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
@ -733,7 +785,9 @@ public class User implements MetaDataAble {
return result;
}
/* (non-Javadoc)
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
@ -749,11 +803,13 @@ public class User implements MetaDataAble {
}
if (playerUUID == null) {
return other.playerUUID == null;
} else return playerUUID.equals(other.playerUUID);
} else
return playerUUID.equals(other.playerUUID);
}
/**
* Set the addon context when a command is executed
*
* @param addon - the addon executing the command
*/
public void setAddon(Addon addon) {
@ -762,14 +818,13 @@ public class User implements MetaDataAble {
/**
* Get all the meta data for this user
*
* @return the metaData
* @since 1.15.4
*/
@Override
public Optional<Map<String, MetaDataValue>> getMetaData() {
Players p = plugin
.getPlayers()
.getPlayer(playerUUID);
Players p = plugin.getPlayers().getPlayer(playerUUID);
return Objects.requireNonNull(p, "Unknown player for " + playerUUID).getMetaData();
}
@ -779,9 +834,7 @@ public class User implements MetaDataAble {
*/
@Override
public void setMetaData(Map<String, MetaDataValue> metaData) {
Players p = plugin
.getPlayers()
.getPlayer(playerUUID);
Players p = plugin.getPlayers().getPlayer(playerUUID);
Objects.requireNonNull(p, "Unknown player for " + playerUUID).setMetaData(metaData);
}

View File

@ -167,11 +167,11 @@ public class BlueprintClipboard {
* @param b - bounding box
* @return - list of vectors
*/
private List<Vector> getVectors(BoundingBox b) {
protected List<Vector> getVectors(BoundingBox b) {
List<Vector> r = new ArrayList<>();
for (int y = (int)b.getMinY(); y <= b.getMaxY(); y++) {
for (int x = (int)b.getMinX(); x <= b.getMaxX(); x++) {
for (int z = (int)b.getMinZ(); z <= b.getMaxZ(); z++) {
for (int y = (int) Math.floor(b.getMinY()); y <= b.getMaxY(); y++) {
for (int x = (int) Math.floor(b.getMinX()); x <= b.getMaxX(); x++) {
for (int z = (int) Math.floor(b.getMinZ()); z <= b.getMaxZ(); z++) {
r.add(new Vector(x,y,z));
}
}

View File

@ -78,7 +78,8 @@ public class BlueprintEntity {
if (e instanceof AbstractHorse horse) {
if (domestication != null) horse.setDomestication(domestication);
if (inventory != null) {
inventory.forEach(horse.getInventory()::setItem);
inventory.forEach((index, item) -> horse.getInventory().setItem(index.intValue(), item));
}
}
if (style != null && e instanceof Horse horse) {

View File

@ -25,6 +25,7 @@ public class BentoBoxCommand extends CompositeCommand {
new BentoBoxLocaleCommand(this);
new BentoBoxHelpCommand(this);
new BentoBoxPermsCommand(this);
new BentoBoxRankCommand(this);
// Database names with a 2 in them are migration databases
if (getPlugin().getSettings().getDatabaseType().name().contains("2")) {
new BentoBoxMigrateCommand(this);

View File

@ -1,6 +1,7 @@
package world.bentobox.bentobox.commands;
import java.util.List;
import java.util.Set;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.commands.CompositeCommand;
@ -8,8 +9,7 @@ import world.bentobox.bentobox.api.commands.ConfirmableCommand;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.Database;
import world.bentobox.bentobox.database.objects.Names;
import world.bentobox.bentobox.database.objects.Players;
import world.bentobox.bentobox.database.objects.DataObject;
/**
* Forces migration from one database to another
@ -38,16 +38,10 @@ public class BentoBoxMigrateCommand extends ConfirmableCommand {
@Override
public boolean execute(User user, String label, List<String> args) {
this.askConfirmation(user, () -> {
// Migrate BentoBox data
user.sendMessage("commands.bentobox.migrate.players");
new Database<>(getPlugin(), Players.class).loadObjects();
user.sendMessage(MIGRATED);
user.sendMessage("commands.bentobox.migrate.names");
new Database<>(getPlugin(), Names.class).loadObjects();
user.sendMessage(MIGRATED);
// Migrate addons data
user.sendMessage("commands.bentobox.migrate.addons");
getPlugin().getAddonsManager().getDataObjects().forEach(t -> {
Set<Class<? extends DataObject>> classSet = getPlugin().getAddonsManager().getDataObjects();
classSet.addAll(Database.getDataobjects());
classSet.forEach(t -> {
user.sendMessage("commands.bentobox.migrate.class", TextVariables.DESCRIPTION, BentoBox.getInstance().getSettings().getDatabasePrefix() + t.getCanonicalName());
new Database<>(getPlugin(), t).loadObjects();
user.sendMessage(MIGRATED);

View File

@ -0,0 +1,143 @@
package world.bentobox.bentobox.commands;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.managers.RanksManager;
/**
* Manages ranks
*
* @author tastybento
* @since 2.0.0
*/
public class BentoBoxRankCommand extends CompositeCommand {
private static final String REMOVE = "remove";
private int rankValue;
private String firstElement;
/**
* Rank management. Add and remove
*
* @param parent command parent
*/
public BentoBoxRankCommand(CompositeCommand parent) {
super(parent, "rank");
}
@Override
public void setup() {
setPermission("bentobox.admin.rank");
setDescription("commands.bentobox.rank.description");
this.setParametersHelp("commands.bentobox.rank.parameters");
}
@Override
public boolean canExecute(User user, String label, List<String> args) {
if (args.isEmpty()) {
// Show help
showHelp(this, user);
return false;
}
// Check if the first element is "add" or REMOVE or "list"
firstElement = args.get(0);
if (!("list".equals(firstElement) || "add".equals(firstElement) || REMOVE.equals(firstElement))) {
// Show help
showHelp(this, user);
return false;
}
if (REMOVE.equals(firstElement) && args.size() != 2) {
// Show help
showHelp(this, user);
return false;
}
// If the first element is "add", then check if the third element is an integer
if ("add".equals(firstElement)) {
// Check if there is a third element
if (args.size() < 3) {
// Show help
showHelp(this, user);
return false;
}
// Check if the third element is an integer
String thirdElement = args.get(2);
try {
rankValue = Integer.parseInt(thirdElement);
} catch (NumberFormatException e) {
// Show help
showHelp(this, user);
return false;
}
}
// If all checks passed, return true
return true;
}
@Override
public boolean execute(User user, String label, List<String> args) {
if ("list".equals(firstElement)) {
showRanks(user);
return true;
}
if ("add".equals(firstElement)) {
if (RanksManager.getInstance().addRank(args.get(1), rankValue)) {
user.sendMessage("commands.bentobox.rank.add.success", TextVariables.RANK, args.get(1),
TextVariables.NUMBER, String.valueOf(rankValue));
showRanks(user);
} else {
user.sendMessage("commands.bentobox.rank.add.failure", TextVariables.RANK, args.get(1),
TextVariables.NUMBER, String.valueOf(rankValue));
return false;
}
} else {
if (RanksManager.getInstance().removeRank(args.get(1))) {
user.sendMessage("commands.bentobox.rank.remove.success", TextVariables.RANK, args.get(1));
showRanks(user);
} else {
user.sendMessage("commands.bentobox.rank.remove.failure", TextVariables.RANK, args.get(1));
return false;
}
}
return true;
}
private void showRanks(User user) {
user.sendMessage("commands.bentobox.rank.list");
RanksManager.getInstance().getRanks().forEach((ref, rank) -> {
user.sendRawMessage(user.getTranslation(ref) + ": " + ref + " " + String.valueOf(rank));
});
}
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
if (args.size() <= 1) {
return Optional.empty();
}
firstElement = args.get(1);
if (args.size() <= 2) {
return Optional.of(List.of("add", REMOVE, "list"));
}
if (args.size() > 1 && "add".equals(firstElement)) {
List<String> options = new ArrayList<>(RanksManager.DEFAULT_RANKS.keySet());
options.removeIf(RanksManager.getInstance().getRanks().keySet()::contains);
return Optional.of(options);
}
if (args.size() > 1 && REMOVE.equals(firstElement)) {
return Optional.of(new ArrayList<>(RanksManager.getInstance().getRanks().keySet()));
}
return Optional.empty();
}
}

View File

@ -2,11 +2,8 @@ package world.bentobox.bentobox.commands;
import java.util.List;
import org.bukkit.Bukkit;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
import world.bentobox.bentobox.api.events.BentoBoxReadyEvent;
import world.bentobox.bentobox.api.panels.reader.TemplateReader;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.commands.reload.BentoBoxReloadLocalesCommand;
@ -40,7 +37,7 @@ public class BentoBoxReloadCommand extends ConfirmableCommand {
public boolean execute(User user, String label, List<String> args) {
if (args.isEmpty()) {
this.askConfirmation(user, user.getTranslation("commands.bentobox.reload.warning"), () -> {
// Unregister all placeholders
getPlugin().getPlaceholdersManager().unregisterAll();
@ -53,22 +50,13 @@ public class BentoBoxReloadCommand extends ConfirmableCommand {
getPlugin().loadSettings();
user.sendMessage("commands.bentobox.reload.settings-reloaded");
// Reload addons
getPlugin().getAddonsManager().reloadAddons();
user.sendMessage("commands.bentobox.reload.addons-reloaded");
// Reload locales
getPlugin().getLocalesManager().reloadLanguages();
user.sendMessage("commands.bentobox.reload.locales-reloaded");
// Register new default gamemode placeholders
getPlugin().getAddonsManager().getGameModeAddons().forEach(getPlugin().getPlaceholdersManager()::registerDefaultPlaceholders);
// Call the all Loaded method for addons
getPlugin().getAddonsManager().allLoaded();
// Fire ready event
Bukkit.getPluginManager().callEvent(new BentoBoxReadyEvent());
});
} else {
showHelp(this, user);

View File

@ -3,7 +3,9 @@ package world.bentobox.bentobox.database;
import java.beans.IntrospectionException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Logger;
@ -12,6 +14,7 @@ import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.addons.Addon;
import world.bentobox.bentobox.database.objects.DataObject;
/**
* Handy class to store and load Java POJOs in the Database
@ -24,15 +27,19 @@ public class Database<T> {
private final AbstractDatabaseHandler<T> handler;
private final Logger logger;
private static DatabaseSetup databaseSetup = DatabaseSetup.getDatabase();
private static final Set<Class<? extends DataObject>> dataObjects = new HashSet<>();
/**
* Construct a database
* @param plugin - plugin
* @param type - to store this type
*/
@SuppressWarnings("unchecked")
public Database(BentoBox plugin, Class<T> type) {
this.logger = plugin.getLogger();
handler = databaseSetup.getHandler(type);
// Log any database classes
dataObjects.add((Class<? extends DataObject>) type);
}
/**
@ -40,9 +47,12 @@ public class Database<T> {
* @param addon - addon requesting
* @param type - to store this type
*/
@SuppressWarnings("unchecked")
public Database(Addon addon, Class<T> type) {
this.logger = addon.getLogger();
handler = databaseSetup.getHandler(type);
// Log any database classes
dataObjects.add((Class<? extends DataObject>) type);
}
/**
@ -149,6 +159,13 @@ public class Database<T> {
handler.close();
}
/**
* @return the dataobjects
*/
public static Set<Class<? extends DataObject>> getDataobjects() {
return dataObjects;
}
}

View File

@ -3,6 +3,7 @@ package world.bentobox.bentobox.database.json;
import java.util.Map;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Biome;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
@ -23,6 +24,7 @@ import world.bentobox.bentobox.database.json.adapters.EnumTypeAdapter;
import world.bentobox.bentobox.database.json.adapters.FlagTypeAdapter;
import world.bentobox.bentobox.database.json.adapters.ItemStackTypeAdapter;
import world.bentobox.bentobox.database.json.adapters.LocationTypeAdapter;
import world.bentobox.bentobox.database.json.adapters.MaterialTypeAdapter;
import world.bentobox.bentobox.database.json.adapters.PotionEffectTypeAdapter;
import world.bentobox.bentobox.database.json.adapters.VectorTypeAdapter;
import world.bentobox.bentobox.database.json.adapters.WorldTypeAdapter;
@ -55,6 +57,8 @@ public class BentoboxTypeAdapterFactory implements TypeAdapterFactory {
if (Location.class.isAssignableFrom(rawType)) {
// Use our current location adapter for backward compatibility
return (TypeAdapter<T>) new LocationTypeAdapter();
} else if (Material.class.isAssignableFrom(rawType)) {
return (TypeAdapter<T>) new MaterialTypeAdapter();
} else if (Biome.class.isAssignableFrom(rawType)) {
return (TypeAdapter<T>) new BiomeTypeAdapter();
} else if (Enum.class.isAssignableFrom(rawType)) {

View File

@ -0,0 +1,59 @@
package world.bentobox.bentobox.database.json.adapters;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.bukkit.Material;
import com.google.common.base.Enums;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
/**
* Minecraft 1.20 changed GRASS to SHORT_GRASS. This class provides and backwards compatibility when loading
* databased files stored with previous versions. It can be extended in the future if further enum changes are made.
* @author tastybento
* @since 2.0.0
*/
public final class MaterialTypeAdapter extends TypeAdapter<Material>
{
/**
* Map that contains string value to the actual Material enum object.
*/
final Map<String, Material> materialMap;
public MaterialTypeAdapter() {
this.materialMap = new HashMap<>();
// Put in current values.
Arrays.stream(Material.values()).forEach(mat -> this.materialMap.put(mat.name(), mat));
// Put in renamed material values.
if (Enums.getIfPresent(Material.class, "SHORT_GRASS").isPresent()) {
this.materialMap.put("GRASS", Material.SHORT_GRASS);
}
}
@Override
public Material read(JsonReader input) throws IOException
{
if (JsonToken.NULL.equals(input.peek())) {
input.nextNull();
return null;
}
return this.materialMap.get(input.nextString().toUpperCase());
}
@Override
public void write(JsonWriter output, Material enumValue) throws IOException {
output.value(enumValue != null ? enumValue.name() : null);
}
}

View File

@ -298,7 +298,7 @@ public class Players implements DataObject, MetaDataAble {
* @param world - world
*/
public void addReset(World world) {
resets.merge(world.getName(), 1, Integer::sum);
resets.merge(world.getName(), 1, (oldValue, newValue) -> Integer.valueOf(oldValue + newValue));
}
/**

View File

@ -0,0 +1,43 @@
package world.bentobox.bentobox.database.objects;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import com.google.gson.annotations.Expose;
/**
* Stores data on ranks
*/
@Table(name = "Ranks")
public class Ranks implements DataObject {
public static final String ID = "BentoBox-Ranks";
public Ranks(Map<String, Integer> rankReference) {
super();
this.rankReference = rankReference;
}
@Expose
private Map<String, Integer> rankReference;
@Override
public String getUniqueId() {
return ID;
}
@Override
public void setUniqueId(String uniqueId) {
// Nothing to do
}
public Map<String, Integer> getRankReference() {
return Objects.requireNonNullElse(rankReference, new LinkedHashMap<>());
}
public void setRankReference(Map<String, Integer> rankReference) {
this.rankReference = rankReference;
}
}

View File

@ -636,6 +636,10 @@ public class YamlDatabaseHandler<T> extends AbstractDatabaseHandler<T> {
return Enums.getIfPresent(EntityType.class, "ZOMBIFIED_PIGLIN")
.or(Enums.getIfPresent(EntityType.class, "PIG_ZOMBIE").or(EntityType.PIG));
}
// Backwards compatibility for upgrade to 1.20.4
if (name.equals("GRASS")) {
return Enums.getIfPresent(EntityType.class, "SHORT_GRASS");
}
value = Enum.valueOf(enumClass, name);
} catch (Exception e) {
// This value does not exist - probably admin typed it wrongly

View File

@ -0,0 +1,124 @@
package world.bentobox.bentobox.hooks;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.entity.EntityExplodeEvent;
import dev.lone.itemsadder.api.CustomBlock;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.flags.Flag;
import world.bentobox.bentobox.api.flags.FlagListener;
import world.bentobox.bentobox.api.flags.clicklisteners.CycleClick;
import world.bentobox.bentobox.api.hooks.Hook;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.managers.RanksManager;
/**
* Hook to enable itemsadder blocks to be deleted when islands are deleted.
* It also includes a flag to track explosion access
*/
public class ItemsAdderHook extends Hook {
/**
* This flag allows to switch which island member group can use explosive items from Items Adder.
*/
public static final Flag ITEMS_ADDER_EXPLOSIONS =
new Flag.Builder("ITEMS_ADDER_EXPLOSIONS", Material.TNT).
type(Flag.Type.PROTECTION).
defaultRank(RanksManager.MEMBER_RANK).
clickHandler(new CycleClick("ITEMS_ADDER_EXPLOSIONS",
RanksManager.VISITOR_RANK, RanksManager.OWNER_RANK))
.
build();
private BentoBox plugin;
private BlockInteractListener listener;
/**
* Register the hook
* @param plugin BentoBox
*/
public ItemsAdderHook(BentoBox plugin) {
super("ItemsAdder", Material.NETHER_STAR);
this.plugin = plugin;
}
@Override
public boolean hook() {
// See if ItemsAdder is around
if (Bukkit.getPluginManager().getPlugin("ItemsAdder") == null) {
return false;
}
// Register listener
listener = new BlockInteractListener();
Bukkit.getPluginManager().registerEvents(listener, plugin);
plugin.getFlagsManager().registerFlag(ITEMS_ADDER_EXPLOSIONS);
return true;
}
/**
* @return the listener
*/
protected BlockInteractListener getListener() {
return listener;
}
/**
* Remove the CustomBlock at location
* @param location
*/
public void clearBlockInfo(Location location) {
CustomBlock.remove(location);
}
class BlockInteractListener extends FlagListener {
/**
* Handles explosions of ItemAdder items
* @param event explosion event
*/
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onExplosion(EntityExplodeEvent event)
{
if (!EntityType.PLAYER.equals(event.getEntityType())) {
// Ignore non-player explosions.
return;
}
Player player = (Player) event.getEntity();
if (!player.hasPermission("XXXXXX")) {
// Ignore players that does not have magic XXXXXX permission.
return;
}
// Use BentoBox flag processing system to validate usage.
// Technically not necessary as internally it should be cancelled by BentoBox.
if (!this.checkIsland(event, player, event.getLocation(), ITEMS_ADDER_EXPLOSIONS)) {
// Remove any blocks from the explosion list if required
event.blockList().removeIf(block -> this.protect(player, block.getLocation()));
event.setCancelled(this.protect(player, event.getLocation()));
}
}
/**
* This method returns if the protection in given location is enabled or not.
* @param player Player who triggers explosion.
* @param location Location where explosion happens.
* @return {@code true} if location is protected, {@code false} otherwise.
*/
private boolean protect(Player player, Location location)
{
return plugin.getIslands().getProtectedIslandAt(location)
.map(island -> !island.isAllowed(User.getInstance(player), ITEMS_ADDER_EXPLOSIONS)).orElse(false);
}
}
}

View File

@ -127,7 +127,7 @@ public class LangUtilsHook extends Hook {
public static String getItemName(ItemStack itemStack, User user) {
return hooked
? LanguageHelper.getItemName(itemStack, getUserLocale(user))
: Util.prettifyText(itemStack.getType().name());
: Util.prettifyText(itemStack.getType().name());
}
/**
@ -143,7 +143,7 @@ public class LangUtilsHook extends Hook {
public static String getMaterialName(Material material, User user) {
return hooked
? LanguageHelper.getMaterialName(material, getUserLocale(user))
: Util.prettifyText(material.name());
: Util.prettifyText(material.name());
}
/**
@ -156,7 +156,7 @@ public class LangUtilsHook extends Hook {
public static String getEntityDisplayName(Entity entity, User user) {
return entity.getCustomName() != null
? entity.getCustomName()
: getEntityName(entity, user);
: getEntityName(entity, user);
}
/**
@ -169,7 +169,7 @@ public class LangUtilsHook extends Hook {
public static String getEntityName(EntityType entityType, User user) {
return hooked
? LanguageHelper.getEntityName(entityType, getUserLocale(user))
: Util.prettifyText(entityType.toString());
: Util.prettifyText(entityType.toString());
}
/**
@ -182,7 +182,7 @@ public class LangUtilsHook extends Hook {
public static String getEntityName(Entity entity, User user) {
return hooked
? LanguageHelper.getEntityName(entity, getUserLocale(user))
: Util.prettifyText(entity.getType().toString());
: Util.prettifyText(entity.getType().toString());
}
/**
@ -195,7 +195,7 @@ public class LangUtilsHook extends Hook {
public static String getBiomeName(Biome biome, User user) {
return hooked
? LanguageHelper.getBiomeName(biome, getUserLocale(user))
: Util.prettifyText(biome.name());
: Util.prettifyText(biome.name());
}
/**
@ -209,7 +209,7 @@ public class LangUtilsHook extends Hook {
public static String getEnchantDisplayName(Enchantment ench, int level, User user) {
return hooked
? LanguageHelper.getEnchantmentDisplayName(ench, level, getUserLocale(user))
: Util.prettifyText(ench.getKey().getKey()) + " " + level;
: Util.prettifyText(ench.getKey().getKey()) + " " + level;
}
/**
@ -223,7 +223,7 @@ public class LangUtilsHook extends Hook {
public static String getEnchantDisplayName(Entry<Enchantment, Integer> entry, User user) {
return hooked
? LanguageHelper.getEnchantmentDisplayName(entry, getUserLocale(user))
: Util.prettifyText(entry.getKey().getKey().getKey()) + " " + entry.getValue();
: Util.prettifyText(entry.getKey().getKey().getKey()) + " " + entry.getValue();
}
/**
@ -236,7 +236,7 @@ public class LangUtilsHook extends Hook {
public static String getEnchantName(Enchantment enchant, User user) {
return hooked
? LanguageHelper.getEnchantmentName(enchant, getUserLocale(user))
: Util.prettifyText(enchant.getKey().getKey());
: Util.prettifyText(enchant.getKey().getKey());
}
/**
@ -250,7 +250,7 @@ public class LangUtilsHook extends Hook {
public static String getEnchantLevelName(int level, User user) {
return hooked
? LanguageHelper.getEnchantmentLevelName(level, getUserLocale(user))
: String.valueOf(level);
: String.valueOf(level);
}
/**
@ -265,27 +265,28 @@ public class LangUtilsHook extends Hook {
return LanguageHelper.getPotionName(potionType, getUserLocale(user));
}
return switch (potionType) {
case UNCRAFTABLE -> "Uncraftable Potion";
case WATER -> "Water Bottle";
case MUNDANE -> "Mundane Potion";
case THICK -> "Thick Potion";
case AWKWARD -> "Awkward Potion";
case NIGHT_VISION -> "Potion of Night Vision";
case INVISIBILITY -> "Potion of Invisibility";
case JUMP -> "Potion of Leaping";
case FIRE_RESISTANCE -> "Potion of Fire Resistance";
case SPEED -> "Potion of Swiftness";
case SLOWNESS -> "Potion of Slowness";
case WATER_BREATHING -> "Potion of Water Breathing";
case INSTANT_HEAL -> "Potion of Healing";
case INSTANT_DAMAGE -> "Potion of Harming";
case POISON -> "Potion of Poison";
case REGEN -> "Potion of Regeneration";
case STRENGTH -> "Potion of Strength";
case WEAKNESS -> "Potion of Weakness";
case LUCK -> "Potion of Luck";
case TURTLE_MASTER -> "Potion of the Turtle Master";
case SLOW_FALLING -> "Potion of Slow Falling";
case UNCRAFTABLE -> "Uncraftable Potion";
case WATER -> "Water Bottle";
case MUNDANE -> "Mundane Potion";
case THICK -> "Thick Potion";
case AWKWARD -> "Awkward Potion";
case NIGHT_VISION -> "Potion of Night Vision";
case INVISIBILITY -> "Potion of Invisibility";
case JUMP -> "Potion of Leaping";
case FIRE_RESISTANCE -> "Potion of Fire Resistance";
case SPEED -> "Potion of Swiftness";
case SLOWNESS -> "Potion of Slowness";
case WATER_BREATHING -> "Potion of Water Breathing";
case INSTANT_HEAL -> "Potion of Healing";
case INSTANT_DAMAGE -> "Potion of Harming";
case POISON -> "Potion of Poison";
case REGEN -> "Potion of Regeneration";
case STRENGTH -> "Potion of Strength";
case WEAKNESS -> "Potion of Weakness";
case LUCK -> "Potion of Luck";
case TURTLE_MASTER -> "Potion of the Turtle Master";
case SLOW_FALLING -> "Potion of Slow Falling";
default -> "Unknown Potion";
};
}
@ -302,27 +303,28 @@ public class LangUtilsHook extends Hook {
return LanguageHelper.getSplashPotionName(potionType, getUserLocale(user));
}
return switch (potionType) {
case UNCRAFTABLE -> "Splash Uncraftable Potion";
case WATER -> "Splash Water Bottle";
case MUNDANE -> "Mundane Splash Potion";
case THICK -> "Thick Splash Potion";
case AWKWARD -> "Awkward Splash Potion";
case NIGHT_VISION -> "Splash Potion of Night Vision";
case INVISIBILITY -> "Splash Potion of Invisibility";
case JUMP -> "Splash Potion of Leaping";
case FIRE_RESISTANCE -> "Splash Potion of Fire Resistance";
case SPEED -> "Splash Potion of Swiftness";
case SLOWNESS -> "Splash Potion of Slowness";
case WATER_BREATHING -> "Splash Potion of Water Breathing";
case INSTANT_HEAL -> "Splash Potion of Healing";
case INSTANT_DAMAGE -> "Splash Potion of Harming";
case POISON -> "Splash Potion of Poison";
case REGEN -> "Splash Potion of Regeneration";
case STRENGTH -> "Splash Potion of Strength";
case WEAKNESS -> "Splash Potion of Weakness";
case LUCK -> "Splash Potion of Luck";
case TURTLE_MASTER -> "Splash Potion of the Turtle Master";
case SLOW_FALLING -> "Splash Potion of Slow Falling";
case UNCRAFTABLE -> "Splash Uncraftable Potion";
case WATER -> "Splash Water Bottle";
case MUNDANE -> "Mundane Splash Potion";
case THICK -> "Thick Splash Potion";
case AWKWARD -> "Awkward Splash Potion";
case NIGHT_VISION -> "Splash Potion of Night Vision";
case INVISIBILITY -> "Splash Potion of Invisibility";
case JUMP -> "Splash Potion of Leaping";
case FIRE_RESISTANCE -> "Splash Potion of Fire Resistance";
case SPEED -> "Splash Potion of Swiftness";
case SLOWNESS -> "Splash Potion of Slowness";
case WATER_BREATHING -> "Splash Potion of Water Breathing";
case INSTANT_HEAL -> "Splash Potion of Healing";
case INSTANT_DAMAGE -> "Splash Potion of Harming";
case POISON -> "Splash Potion of Poison";
case REGEN -> "Splash Potion of Regeneration";
case STRENGTH -> "Splash Potion of Strength";
case WEAKNESS -> "Splash Potion of Weakness";
case LUCK -> "Splash Potion of Luck";
case TURTLE_MASTER -> "Splash Potion of the Turtle Master";
case SLOW_FALLING -> "Splash Potion of Slow Falling";
default -> "Unknown Splash Potion";
};
}
@ -338,27 +340,28 @@ public class LangUtilsHook extends Hook {
return LanguageHelper.getLingeringPotionName(potionType, getUserLocale(user));
}
return switch (potionType) {
case UNCRAFTABLE -> "Lingering Uncraftable Potion";
case WATER -> "Lingering Water Bottle";
case MUNDANE -> "Mundane Lingering Potion";
case THICK -> "Thick Lingering Potion";
case AWKWARD -> "Awkward Lingering Potion";
case NIGHT_VISION -> "Lingering Potion of Night Vision";
case INVISIBILITY -> "Lingering Potion of Invisibility";
case JUMP -> "Lingering Potion of Leaping";
case FIRE_RESISTANCE -> "Lingering Potion of Fire Resistance";
case SPEED -> "Lingering Potion of Swiftness";
case SLOWNESS -> "Lingering Potion of Slowness";
case WATER_BREATHING -> "Lingering Potion of Water Breathing";
case INSTANT_HEAL -> "Lingering Potion of Healing";
case INSTANT_DAMAGE -> "Lingering Potion of Harming";
case POISON -> "Lingering Potion of Poison";
case REGEN -> "Lingering Potion of Regeneration";
case STRENGTH -> "Lingering Potion of Strength";
case WEAKNESS -> "Lingering Potion of Weakness";
case LUCK -> "Lingering Potion of Luck";
case TURTLE_MASTER -> "Lingering Potion of the Turtle Master";
case SLOW_FALLING -> "Lingering Potion of Slow Falling";
case UNCRAFTABLE -> "Lingering Uncraftable Potion";
case WATER -> "Lingering Water Bottle";
case MUNDANE -> "Mundane Lingering Potion";
case THICK -> "Thick Lingering Potion";
case AWKWARD -> "Awkward Lingering Potion";
case NIGHT_VISION -> "Lingering Potion of Night Vision";
case INVISIBILITY -> "Lingering Potion of Invisibility";
case JUMP -> "Lingering Potion of Leaping";
case FIRE_RESISTANCE -> "Lingering Potion of Fire Resistance";
case SPEED -> "Lingering Potion of Swiftness";
case SLOWNESS -> "Lingering Potion of Slowness";
case WATER_BREATHING -> "Lingering Potion of Water Breathing";
case INSTANT_HEAL -> "Lingering Potion of Healing";
case INSTANT_DAMAGE -> "Lingering Potion of Harming";
case POISON -> "Lingering Potion of Poison";
case REGEN -> "Lingering Potion of Regeneration";
case STRENGTH -> "Lingering Potion of Strength";
case WEAKNESS -> "Lingering Potion of Weakness";
case LUCK -> "Lingering Potion of Luck";
case TURTLE_MASTER -> "Lingering Potion of the Turtle Master";
case SLOW_FALLING -> "Lingering Potion of Slow Falling";
default -> "Unknown Lingering Potion";
};
}
@ -374,25 +377,26 @@ public class LangUtilsHook extends Hook {
return LanguageHelper.getTippedArrowName(potionType, getUserLocale(user));
}
return switch (potionType) {
case UNCRAFTABLE -> "Uncraftable Tipped Arrow";
case WATER -> "Arrow of Splashing";
case MUNDANE, THICK, AWKWARD -> "Tipped Arrow";
case NIGHT_VISION -> "Arrow of Night Vision";
case INVISIBILITY -> "Arrow of Invisibility";
case JUMP -> "Arrow of Leaping";
case FIRE_RESISTANCE -> "Arrow of Fire Resistance";
case SPEED -> "Arrow of Swiftness";
case SLOWNESS -> "Arrow of Slowness";
case WATER_BREATHING -> "Arrow of Water Breathing";
case INSTANT_HEAL -> "Arrow of Healing";
case INSTANT_DAMAGE -> "Arrow of Harming";
case POISON -> "Arrow of Poison";
case REGEN -> "Arrow of Regeneration";
case STRENGTH -> "Arrow of Strength";
case WEAKNESS -> "Arrow of Weakness";
case LUCK -> "Arrow of Luck";
case TURTLE_MASTER -> "Arrow of the Turtle Master";
case SLOW_FALLING -> "Arrow of Slow Falling";
case UNCRAFTABLE -> "Uncraftable Tipped Arrow";
case WATER -> "Arrow of Splashing";
case MUNDANE, THICK, AWKWARD -> "Tipped Arrow";
case NIGHT_VISION -> "Arrow of Night Vision";
case INVISIBILITY -> "Arrow of Invisibility";
case JUMP -> "Arrow of Leaping";
case FIRE_RESISTANCE -> "Arrow of Fire Resistance";
case SPEED -> "Arrow of Swiftness";
case SLOWNESS -> "Arrow of Slowness";
case WATER_BREATHING -> "Arrow of Water Breathing";
case INSTANT_HEAL -> "Arrow of Healing";
case INSTANT_DAMAGE -> "Arrow of Harming";
case POISON -> "Arrow of Poison";
case REGEN -> "Arrow of Regeneration";
case STRENGTH -> "Arrow of Strength";
case WEAKNESS -> "Arrow of Weakness";
case LUCK -> "Arrow of Luck";
case TURTLE_MASTER -> "Arrow of the Turtle Master";
case SLOW_FALLING -> "Arrow of Slow Falling";
default -> "Unknown Arrow";
};
}
@ -426,7 +430,7 @@ public class LangUtilsHook extends Hook {
public static String getPotionEffectName(PotionEffectType effectType, User user) {
return hooked
? LanguageHelper.getPotionEffectName(effectType, getUserLocale(user))
: Util.prettifyText(effectType.getName());
: Util.prettifyText(effectType.getName());
}
/**
@ -483,7 +487,7 @@ public class LangUtilsHook extends Hook {
public static String getTropicalFishTypeName(TropicalFish.Pattern fishPattern, User user) {
return hooked
? LanguageHelper.getTropicalFishTypeName(fishPattern, getUserLocale(user))
: Util.prettifyText(fishPattern.name());
: Util.prettifyText(fishPattern.name());
}
/**
@ -515,74 +519,74 @@ public class LangUtilsHook extends Hook {
int variant = (pcol & 255) << 24 | (bcol & 255) << 16 | (patt & 255) << 8 | type;
switch (variant) {
case 117506305 -> {
return "Anemone";
}
case 117899265 -> {
return "Black Tang";
}
case 185008129 -> {
return "Blue Tang";
}
case 117441793 -> {
return "Butterflyfish";
}
case 118161664 -> {
return "Cichlid";
}
case 65536 -> {
return "Clownfish";
}
case 50726144 -> {
return "Cotton Candy Betta";
}
case 67764993 -> {
return "Dottyback";
}
case 234882305 -> {
return "Emperor Red Snapper";
}
case 67110144 -> {
return "Goatfish";
}
case 117441025 -> {
return "Moorish Idol";
}
case 16778497 -> {
return "Ornate Butterflyfish";
}
case 101253888 -> {
return "Parrotfish";
}
case 50660352 -> {
return "Queen Angelfish";
}
case 918529 -> {
return "Red Cichlid";
}
case 235340288 -> {
return "Red Lipped Blenny";
}
case 918273 -> {
return "Red Snapper";
}
case 67108865 -> {
return "Threadfin";
}
case 917504 -> {
return "Tomato Clownfish";
}
case 459008 -> {
return "Triggerfish";
}
case 67699456 -> {
return "Yellowtail Parrotfish";
}
case 67371009 -> {
return "Yellow Tang";
}
default -> {
}
case 117506305 -> {
return "Anemone";
}
case 117899265 -> {
return "Black Tang";
}
case 185008129 -> {
return "Blue Tang";
}
case 117441793 -> {
return "Butterflyfish";
}
case 118161664 -> {
return "Cichlid";
}
case 65536 -> {
return "Clownfish";
}
case 50726144 -> {
return "Cotton Candy Betta";
}
case 67764993 -> {
return "Dottyback";
}
case 234882305 -> {
return "Emperor Red Snapper";
}
case 67110144 -> {
return "Goatfish";
}
case 117441025 -> {
return "Moorish Idol";
}
case 16778497 -> {
return "Ornate Butterflyfish";
}
case 101253888 -> {
return "Parrotfish";
}
case 50660352 -> {
return "Queen Angelfish";
}
case 918529 -> {
return "Red Cichlid";
}
case 235340288 -> {
return "Red Lipped Blenny";
}
case 918273 -> {
return "Red Snapper";
}
case 67108865 -> {
return "Threadfin";
}
case 917504 -> {
return "Tomato Clownfish";
}
case 459008 -> {
return "Triggerfish";
}
case 67699456 -> {
return "Yellowtail Parrotfish";
}
case 67371009 -> {
return "Yellow Tang";
}
default -> {
}
}
}
return null;
@ -598,7 +602,7 @@ public class LangUtilsHook extends Hook {
public static String getDyeColorName(DyeColor color, User user) {
return hooked
? LanguageHelper.getDyeColorName(color, getUserLocale(user))
: Util.prettifyText(color.name());
: Util.prettifyText(color.name());
}
/**
@ -611,7 +615,7 @@ public class LangUtilsHook extends Hook {
public static String getVillagerLevelName(int level, User user) {
return hooked
? LanguageHelper.getVillagerLevelName(level, getUserLocale(user))
: Integer.toString(level);
: Integer.toString(level);
}
/**
@ -624,7 +628,7 @@ public class LangUtilsHook extends Hook {
public static String getVillagerProfessionName(Villager.Profession profession, User user) {
return hooked
? LanguageHelper.getVillagerProfessionName(profession, getUserLocale(user))
: Util.prettifyText(profession.name());
: Util.prettifyText(profession.name());
}
/**
@ -637,9 +641,9 @@ public class LangUtilsHook extends Hook {
public static String getBannerPatternName(Pattern pattern, User user) {
return hooked
? LanguageHelper.getBannerPatternName(pattern, getUserLocale(user))
: pattern.getColor().name().toLowerCase(Locale.ROOT)
+ "_"
+ pattern.getPattern().name().toLowerCase(Locale.ROOT);
: pattern.getColor().name().toLowerCase(Locale.ROOT)
+ "_"
+ pattern.getPattern().name().toLowerCase(Locale.ROOT);
}
/**
@ -658,22 +662,22 @@ public class LangUtilsHook extends Hook {
// so directly output it here.
return switch (material) {
case MUSIC_DISC_13 -> "C418 - 13";
case MUSIC_DISC_CAT -> "C418 - cat";
case MUSIC_DISC_BLOCKS -> "C418 - blocks";
case MUSIC_DISC_CHIRP -> "C418 - chirp";
case MUSIC_DISC_FAR -> "C418 - far";
case MUSIC_DISC_MALL -> "C418 - mall";
case MUSIC_DISC_MELLOHI -> "C418 - mellohi";
case MUSIC_DISC_STAL -> "C418 - stal";
case MUSIC_DISC_STRAD -> "C418 - strad";
case MUSIC_DISC_WARD -> "C418 - ward";
case MUSIC_DISC_11 -> "C418 - 11";
case MUSIC_DISC_WAIT -> "C418 - wait";
case MUSIC_DISC_PIGSTEP -> "Lena Raine - Pigstep";
case MUSIC_DISC_5 -> "Samuel Åberg - 5";
case MUSIC_DISC_OTHERSIDE -> "Lena Raine - otherside";
default -> null;
case MUSIC_DISC_13 -> "C418 - 13";
case MUSIC_DISC_CAT -> "C418 - cat";
case MUSIC_DISC_BLOCKS -> "C418 - blocks";
case MUSIC_DISC_CHIRP -> "C418 - chirp";
case MUSIC_DISC_FAR -> "C418 - far";
case MUSIC_DISC_MALL -> "C418 - mall";
case MUSIC_DISC_MELLOHI -> "C418 - mellohi";
case MUSIC_DISC_STAL -> "C418 - stal";
case MUSIC_DISC_STRAD -> "C418 - strad";
case MUSIC_DISC_WARD -> "C418 - ward";
case MUSIC_DISC_11 -> "C418 - 11";
case MUSIC_DISC_WAIT -> "C418 - wait";
case MUSIC_DISC_PIGSTEP -> "Lena Raine - Pigstep";
case MUSIC_DISC_5 -> "Samuel Åberg - 5";
case MUSIC_DISC_OTHERSIDE -> "Lena Raine - otherside";
default -> null;
};
}

View File

@ -32,8 +32,12 @@ public class MultiverseCoreHook extends Hook implements WorldManagementHook {
public void registerWorld(World world, boolean islandWorld) {
if (islandWorld) {
// Only register generator if one is defined in the addon (is not null)
String generator = BentoBox.getInstance().getIWM().getAddon(world).map(gm -> gm.getDefaultWorldGenerator(world.getName(), "") != null).orElse(false) ? " -g " + BentoBox.getInstance().getName() : "";
String cmd1 = MULTIVERSE_IMPORT + world.getName() + " " + world.getEnvironment().name().toLowerCase(Locale.ENGLISH) + generator;
String generator = BentoBox.getInstance().getIWM().getAddon(world)
.map(gm -> gm.getDefaultWorldGenerator(world.getName(), "") != null).orElse(false)
? " -g " + BentoBox.getInstance().getName()
: "";
String cmd1 = MULTIVERSE_IMPORT + world.getName() + " "
+ world.getEnvironment().name().toLowerCase(Locale.ENGLISH) + generator;
String cmd2 = MULTIVERSE_SET_GENERATOR + BentoBox.getInstance().getName() + " " + world.getName();
Bukkit.getServer().dispatchCommand(Bukkit.getServer().getConsoleSender(), cmd1);
if (!generator.isEmpty()) {
@ -42,7 +46,8 @@ public class MultiverseCoreHook extends Hook implements WorldManagementHook {
}
} else {
// Set the generator to null - this will remove any previous registration
String cmd1 = MULTIVERSE_IMPORT + world.getName() + " " + world.getEnvironment().name().toLowerCase(Locale.ENGLISH);
String cmd1 = MULTIVERSE_IMPORT + world.getName() + " "
+ world.getEnvironment().name().toLowerCase(Locale.ENGLISH);
String cmd2 = MULTIVERSE_SET_GENERATOR + "null " + world.getName();
Bukkit.getServer().dispatchCommand(Bukkit.getServer().getConsoleSender(), cmd1);
Bukkit.getServer().dispatchCommand(Bukkit.getServer().getConsoleSender(), cmd2);

View File

@ -0,0 +1,37 @@
package world.bentobox.bentobox.hooks;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import me.mrCookieSlime.Slimefun.api.BlockStorage;
import world.bentobox.bentobox.api.hooks.Hook;
/**
* Hook to enable slimefun blocks to be deleted when islands are deleted.
*/
public class SlimefunHook extends Hook {
public SlimefunHook() {
super("Slimefun", Material.SLIME_BLOCK);
}
@Override
public boolean hook() {
// See if Slimefun is around
return Bukkit.getPluginManager().getPlugin("SlimeFun") != null;
}
@Override
public String getFailureCause() {
return ""; // No errors
}
public void clearBlockInfo(Location location, boolean destroy) {
if (BlockStorage.hasBlockInfo(location)) {
BlockStorage.clearBlockInfo(location, destroy);
}
}
}

View File

@ -17,7 +17,7 @@ import world.bentobox.bentobox.api.user.User;
public class VaultHook extends Hook {
private static final String AMOUNT_MUST_BE_POSITIVE = "Amount must be positive.";
private static final String PLAYER_OR_OFFLINEPLAYER_REQUIRED = "User must be a Player or an OfflinePlayer";
private static final String PLAYER_OR_OFFLINEPLAYER_REQUIRED = "User must be a Player or an OfflinePlayer";
private Economy economy;
public VaultHook() {
@ -27,7 +27,8 @@ public class VaultHook extends Hook {
@Override
public boolean hook() {
try {
RegisteredServiceProvider<Economy> rsp = Bukkit.getServer().getServicesManager().getRegistration(Economy.class);
RegisteredServiceProvider<Economy> rsp = Bukkit.getServer().getServicesManager()
.getRegistration(Economy.class);
if (rsp == null) {
return false;
}
@ -48,9 +49,9 @@ public class VaultHook extends Hook {
}
// ------ CONVENIENCE METHODS ------
public String format(double amount) {
return economy.format(amount);
return economy.format(amount);
}
/**
@ -66,7 +67,7 @@ public class VaultHook extends Hook {
public double getBalance(User user) {
return this.getBalance(user, user.getWorld());
}
/**
* Get balance of this User for this world.
* If this User is not a Player (or OfflinePlayer), it will always return {@code 0.0D}.
@ -77,13 +78,13 @@ public class VaultHook extends Hook {
* @return the balance of this User for this world.
*/
public double getBalance(User user, World world) {
if (!user.isOfflinePlayer())
return 0.0D;
if (world == null)
return economy.getBalance(user.getOfflinePlayer());
return economy.getBalance(user.getOfflinePlayer(), world.getName());
if (!user.isOfflinePlayer())
return 0.0D;
if (world == null)
return economy.getBalance(user.getOfflinePlayer());
return economy.getBalance(user.getOfflinePlayer(), world.getName());
}
/**
@ -98,7 +99,7 @@ public class VaultHook extends Hook {
public EconomyResponse withdraw(User user, double amount) {
return withdraw(user, amount, user.getWorld());
}
/**
* Withdraws an amount from this User on the balance from this World.
* If the economy plugin don't support world or world is null, It will return general balance.
@ -115,14 +116,14 @@ public class VaultHook extends Hook {
if (amount < 0.0D) {
throw new IllegalArgumentException(AMOUNT_MUST_BE_POSITIVE);
}
if (world == null)
return economy.withdrawPlayer(user.getOfflinePlayer(), amount);
return economy.withdrawPlayer(user.getOfflinePlayer(), amount);
EconomyResponse response = economy.withdrawPlayer(user.getOfflinePlayer(), world.getName(), amount);
if (response == null || response.type == ResponseType.NOT_IMPLEMENTED)
return economy.withdrawPlayer(user.getOfflinePlayer(), amount);
return economy.withdrawPlayer(user.getOfflinePlayer(), amount);
return response;
}
@ -138,7 +139,7 @@ public class VaultHook extends Hook {
public EconomyResponse deposit(User user, double amount) {
return deposit(user, amount, user.getWorld());
}
/**
* Deposits an amount to this User on the balance from this World.
* If the economy plugin don't support world or world is null, It will return general balance.
@ -155,14 +156,14 @@ public class VaultHook extends Hook {
if (amount < 0.0D) {
throw new IllegalArgumentException(AMOUNT_MUST_BE_POSITIVE);
}
if (world == null)
return economy.depositPlayer(user.getOfflinePlayer(), amount);
return economy.depositPlayer(user.getOfflinePlayer(), amount);
EconomyResponse response = economy.depositPlayer(user.getOfflinePlayer(), world.getName(), amount);
if (response == null || response.type == ResponseType.NOT_IMPLEMENTED)
return economy.depositPlayer(user.getOfflinePlayer(), amount);
return economy.depositPlayer(user.getOfflinePlayer(), amount);
return response;
}
@ -182,7 +183,7 @@ public class VaultHook extends Hook {
}
return user.isOfflinePlayer() && economy.has(user.getOfflinePlayer(), amount);
}
/**
* Checks if this User has the amount on the balance from this World.
* If this User is not a Player (or OfflinePlayer), it will always return {@code false}.
@ -197,14 +198,14 @@ public class VaultHook extends Hook {
if (amount < 0.0D) {
throw new IllegalArgumentException(AMOUNT_MUST_BE_POSITIVE);
}
if (!user.isOfflinePlayer()) {
throw new IllegalArgumentException(PLAYER_OR_OFFLINEPLAYER_REQUIRED);
}
if (world == null)
return economy.has(user.getOfflinePlayer(), amount);
return economy.has(user.getOfflinePlayer(), amount);
return economy.has(user.getOfflinePlayer(), world.getName(), amount);
}
}

View File

@ -1,13 +1,10 @@
package world.bentobox.bentobox.listeners;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.World;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
@ -23,7 +20,6 @@ import world.bentobox.bentobox.api.addons.GameModeAddon;
import world.bentobox.bentobox.api.events.island.IslandEvent;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.database.objects.Players;
import world.bentobox.bentobox.lists.Flags;
import world.bentobox.bentobox.managers.BlueprintsManager;
@ -46,12 +42,14 @@ public class JoinLeaveListener implements Listener {
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onPlayerJoin(final PlayerJoinEvent event) {
// Remove them from the cache, just in case they were not removed for some reason
// Remove them from the cache, just in case they were not removed for some
// reason
User.removePlayer(event.getPlayer());
User user = User.getInstance(event.getPlayer());
if (!user.isPlayer() || user.getUniqueId() == null) {
// This should never be the case, but it might be caused by some fake player plugins
// This should never be the case, but it might be caused by some fake player
// plugins
return;
}
UUID playerUUID = event.getPlayer().getUniqueId();
@ -61,18 +59,14 @@ public class JoinLeaveListener implements Listener {
firstTime(user);
}
// Make sure the player is loaded into the cache or create the player if they don't exist
// Make sure the player is loaded into the cache or create the player if they
// don't exist
players.addPlayer(playerUUID);
// Reset island resets if required
plugin.getIWM().getOverWorlds().stream()
.filter(w -> event.getPlayer().getLastPlayed() < plugin.getIWM().getResetEpoch(w))
.forEach(w -> players.setResets(w, playerUUID, 0));
// Automated island ownership transfer
if (plugin.getSettings().isEnableAutoOwnershipTransfer()) {
runAutomatedOwnershipTransfer(user);
}
.filter(w -> event.getPlayer().getLastPlayed() < plugin.getIWM().getResetEpoch(w))
.forEach(w -> players.setResets(w, playerUUID, 0));
// Update the island range of the islands the player owns
updateIslandRange(user);
@ -85,66 +79,71 @@ public class JoinLeaveListener implements Listener {
plugin.logWarning("Player that just logged in has no name! " + playerUUID);
}
// If mobs have to be removed when a player joins, then wipe all the mobs on his island.
if (plugin.getIslands().locationIsOnIsland(event.getPlayer(), user.getLocation()) && Flags.REMOVE_MOBS.isSetForWorld(user.getWorld())) {
// If mobs have to be removed when a player joins, then wipe all the mobs on his
// island.
if (plugin.getIslands().locationIsOnIsland(event.getPlayer(), user.getLocation())
&& Flags.REMOVE_MOBS.isSetForWorld(user.getWorld())) {
Bukkit.getScheduler().runTask(plugin, () -> plugin.getIslands().clearArea(user.getLocation()));
}
// Clear inventory if required
clearPlayersInventory(Util.getWorld(event.getPlayer().getWorld()), user);
// Set island max members and homes based on permissions if this player is the owner of an island
plugin.getIWM().getOverWorlds().stream()
.map(w -> plugin.getIslands().getIsland(w, playerUUID))
.filter(Objects::nonNull)
.filter(i -> playerUUID.equals(i.getOwner()))
.forEach(i -> {
plugin.getIslands().getMaxMembers(i, RanksManager.MEMBER_RANK);
plugin.getIslands().getMaxMembers(i, RanksManager.COOP_RANK);
plugin.getIslands().getMaxMembers(i, RanksManager.TRUSTED_RANK);
plugin.getIslands().getMaxHomes(i);
});
// Set island max members and homes based on permissions if this player is the
// owner of an island
plugin.getIWM().getOverWorlds().stream().map(w -> plugin.getIslands().getIsland(w, playerUUID))
.filter(Objects::nonNull).filter(i -> playerUUID.equals(i.getOwner())).forEach(i -> {
plugin.getIslands().getMaxMembers(i, RanksManager.MEMBER_RANK);
plugin.getIslands().getMaxMembers(i, RanksManager.COOP_RANK);
plugin.getIslands().getMaxMembers(i, RanksManager.TRUSTED_RANK);
plugin.getIslands().getMaxHomes(i);
});
// Add a player to the bStats cache.
plugin.getMetrics().ifPresent(bStats -> bStats.addPlayer(playerUUID));
}
private void firstTime(User user) {
// Make sure the player is loaded into the cache or create the player if they don't exist
// Make sure the player is loaded into the cache or create the player if they
// don't exist
players.addPlayer(user.getUniqueId());
plugin.getIWM().getOverWorlds().stream()
.filter(w -> plugin.getIWM().isCreateIslandOnFirstLoginEnabled(w))
.forEach(w -> {
// Even if that'd be extremely unlikely, it's better to check if the player doesn't have an island already.
if (!(plugin.getIslands().hasIsland(w, user) || plugin.getIslands().inTeam(w, user.getUniqueId()))) {
int delay = plugin.getIWM().getCreateIslandOnFirstLoginDelay(w);
user.sendMessage("commands.island.create.on-first-login",
TextVariables.NUMBER, String.valueOf(delay));
plugin.getIWM().getOverWorlds().stream().filter(w -> plugin.getIWM().isCreateIslandOnFirstLoginEnabled(w))
.forEach(w -> {
// Even if that'd be extremely unlikely, it's better to check if the player
// doesn't have an island already.
if (!(plugin.getIslands().hasIsland(w, user)
|| plugin.getIslands().inTeam(w, user.getUniqueId()))) {
int delay = plugin.getIWM().getCreateIslandOnFirstLoginDelay(w);
user.sendMessage("commands.island.create.on-first-login", TextVariables.NUMBER,
String.valueOf(delay));
Runnable createIsland = () -> {
// should only execute if:
// - abort on logout is false
// - abort on logout is true && user is online
if (!plugin.getIWM().isCreateIslandOnFirstLoginAbortOnLogout(w) || user.isOnline()){
plugin.getIWM().getAddon(w).flatMap(addon -> addon.getPlayerCommand().flatMap(command -> command.getSubCommand("create")))
.ifPresent(command -> command.execute(user, "create", Collections.singletonList(BlueprintsManager.DEFAULT_BUNDLE_NAME)));
Runnable createIsland = () -> {
// should only execute if:
// - abort on logout is false
// - abort on logout is true && user is online
if (!plugin.getIWM().isCreateIslandOnFirstLoginAbortOnLogout(w) || user.isOnline()) {
plugin.getIWM().getAddon(w)
.flatMap(addon -> addon.getPlayerCommand()
.flatMap(command -> command.getSubCommand("create")))
.ifPresent(command -> command.execute(user, "create",
Collections.singletonList(BlueprintsManager.DEFAULT_BUNDLE_NAME)));
}
};
if (delay <= 0) {
Bukkit.getScheduler().runTask(plugin, createIsland);
} else {
Bukkit.getScheduler().runTaskLater(plugin, createIsland, delay * 20L);
}
}
};
if (delay <= 0) {
Bukkit.getScheduler().runTask(plugin, createIsland);
} else {
Bukkit.getScheduler().runTaskLater(plugin, createIsland, delay * 20L);
}
}
});
});
}
/**
* This event will clean players inventory
*
* @param event SwitchWorld event.
*/
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
@ -156,15 +155,16 @@ public class JoinLeaveListener implements Listener {
}
}
/**
* This method clears player inventory and ender chest if given world is quarantined
* in user data file and it is required by plugin settings.
* This method clears player inventory and ender chest if given world is
* quarantined in user data file and it is required by plugin settings.
*
* @param world World where cleaning must occur.
* @param user Targeted user.
* @param user Targeted user.
*/
private void clearPlayersInventory(@Nullable World world, @NonNull User user) {
if (user.getUniqueId() == null || world == null) return;
if (user.getUniqueId() == null || world == null)
return;
// Clear inventory if required
Players playerData = players.getPlayer(user.getUniqueId());
@ -182,89 +182,54 @@ public class JoinLeaveListener implements Listener {
}
}
private void runAutomatedOwnershipTransfer(User user) {
plugin.getIWM().getOverWorlds().stream()
.filter(world -> plugin.getIslands().hasIsland(world, user) && !plugin.getIslands().isOwner(world, user.getUniqueId()))
.forEach(world -> {
Island island = plugin.getIslands().getIsland(world, user);
OfflinePlayer owner = Bukkit.getOfflinePlayer(island.getOwner());
// Converting the setting (in days) to milliseconds.
long inactivityThreshold = plugin.getSettings().getAutoOwnershipTransferInactivityThreshold() * 24 * 60 * 60 * 1000L;
long timestamp = System.currentTimeMillis() - inactivityThreshold;
// We make sure the current owner is inactive.
if (owner.getLastPlayed() != 0 && owner.getLastPlayed() < timestamp) {
// The current owner is inactive
// Now, let's run through all of the island members (except the player who's just joined) and see who's active.
// Sadly, this will make us calculate the owner inactivity again... :(
List<UUID> candidates = Arrays.asList((UUID[]) island.getMemberSet().stream()
.filter(uuid -> !user.getUniqueId().equals(uuid))
.filter(uuid -> Bukkit.getOfflinePlayer(uuid).getLastPlayed() != 0
&& Bukkit.getOfflinePlayer(uuid).getLastPlayed() < timestamp)
.toArray());
if (!candidates.isEmpty() && !plugin.getSettings().isAutoOwnershipTransferIgnoreRanks()) {
// Ranks are not ignored, our candidates can only have the highest rank
// TODO Complete this section
}
}
});
}
private void updateIslandRange(User user) {
plugin.getIWM().getOverWorlds().stream()
.filter(world -> plugin.getIslands().isOwner(world, user.getUniqueId()))
.forEach(world -> {
Island island = plugin.getIslands().getIsland(world, user);
if (island != null) {
// Check if new owner has a different range permission than the island size
int range = user.getPermissionValue(plugin.getIWM().getAddon(island.getWorld()).map(GameModeAddon::getPermissionPrefix).orElse("") + "island.range", island.getRawProtectionRange());
// Range cannot be greater than the island distance
range = Math.min(range, plugin.getIWM().getIslandDistance(island.getWorld()));
// Range can go up or down
if (range != island.getRawProtectionRange()) {
user.sendMessage("commands.admin.setrange.range-updated", TextVariables.NUMBER, String.valueOf(range));
int oldRange = island.getProtectionRange();
island.setProtectionRange(range);
plugin.getIslands().getIslands().stream()
.filter(island -> island.getOwner() != null && island.getOwner().equals(user.getUniqueId()))
.forEach(island -> {
// Check if new owner has a different range permission than the island size
int range = user.getPermissionValue(plugin.getIWM().getAddon(island.getWorld())
.map(GameModeAddon::getPermissionPrefix).orElse("") + "island.range",
island.getRawProtectionRange());
// Range cannot be greater than the island distance
range = Math.min(range, plugin.getIWM().getIslandDistance(island.getWorld()));
// Range can go up or down
if (range != island.getRawProtectionRange()) {
user.sendMessage("commands.admin.setrange.range-updated", TextVariables.NUMBER,
String.valueOf(range));
int oldRange = island.getProtectionRange();
island.setProtectionRange(range);
plugin.log("Island protection range changed from " + oldRange + " to "
+ island.getProtectionRange() + " for " + user.getName() + " due to permission.");
// Call Protection Range Change event. Does not support canceling.
IslandEvent.builder()
.island(island)
.location(island.getProtectionCenter())
.reason(IslandEvent.Reason.RANGE_CHANGE)
.involvedPlayer(user.getUniqueId())
.admin(true)
.protectionRange(island.getProtectionRange(), oldRange)
.build();
}
}
});
plugin.log("Island protection range changed from " + oldRange + " to "
+ island.getProtectionRange() + " for " + user.getName() + " due to permission.");
// Call Protection Range Change event. Does not support canceling.
IslandEvent.builder().island(island).location(island.getProtectionCenter())
.reason(IslandEvent.Reason.RANGE_CHANGE).involvedPlayer(user.getUniqueId()).admin(true)
.protectionRange(island.getProtectionRange(), oldRange).build();
}
});
}
@EventHandler(priority = EventPriority.NORMAL)
public void onPlayerQuit(final PlayerQuitEvent event) {
// Remove any coops if all the island players have left
plugin.getIWM().getOverWorlds().forEach(w -> {
Island island = plugin.getIslands().getIsland(w, User.getInstance(event.getPlayer()));
// Are there any online players still for this island?
if (island != null && Bukkit.getOnlinePlayers().stream()
.filter(p -> !event.getPlayer().equals(p))
.noneMatch(p -> plugin.getIslands().getMembers(w, event.getPlayer().getUniqueId()).contains(p.getUniqueId()))) {
// No, there are no more players online on this island
// Tell players they are being removed
island.getMembers().entrySet().stream()
.filter(e -> e.getValue() == RanksManager.COOP_RANK)
.forEach(e -> User.getInstance(e.getKey())
.sendMessage("commands.island.team.uncoop.all-members-logged-off", TextVariables.NAME, plugin.getPlayers().getName(island.getOwner())));
// Remove any coop players on this island
island.removeRank(RanksManager.COOP_RANK);
}
});
// Go through all the islands this player is a member of, check if all members
// have left, remove coops
plugin.getIslands().getIslands().stream()
.filter(island -> island.getMembers().containsKey(event.getPlayer().getUniqueId())).forEach(island -> {
// Are there any online players still for this island?
if (Bukkit.getOnlinePlayers().stream().filter(p -> !event.getPlayer().equals(p))
.noneMatch(p -> island.getMemberSet().contains(p.getUniqueId()))) {
// No, there are no more players online on this island
// Tell players they are being removed
island.getMembers().entrySet().stream().filter(e -> e.getValue() == RanksManager.COOP_RANK)
.forEach(e -> User.getInstance(e.getKey()).sendMessage(
"commands.island.team.uncoop.all-members-logged-off", TextVariables.NAME,
plugin.getPlayers().getName(island.getOwner())));
// Remove any coop players on this island
island.removeRank(RanksManager.COOP_RANK);
}
});
// Remove any coop associations from the player logging out
plugin.getIslands().clearRank(RanksManager.COOP_RANK, event.getPlayer().getUniqueId());
players.save(event.getPlayer().getUniqueId());

View File

@ -1,163 +0,0 @@
package world.bentobox.bentobox.listeners;
import java.util.Objects;
import java.util.Optional;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.event.entity.EntityPortalEvent;
import org.bukkit.event.player.PlayerPortalEvent;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.database.objects.Island;
/**
* Abstracts PlayerPortalEvent and EntityPortalEvent
* @author tastybento
* @deprecated replaced not used in new listeners.
* @since 1.12.1
*/
@Deprecated(since="1.21.0", forRemoval=true)
public class PlayerEntityPortalEvent {
private final EntityPortalEvent epe;
private final PlayerPortalEvent ppe;
/**
* Create a hybrid PlayerEntityPortalEvent
* @param epe - EntityPortalEvent
*/
public PlayerEntityPortalEvent(EntityPortalEvent epe) {
this.ppe = null;
this.epe = epe;
}
/**
* Create a hybrid PlayerEntityPortalEvent
* @param ppe - PlayerPortalEvent
*/
public PlayerEntityPortalEvent(PlayerPortalEvent ppe) {
this.ppe = ppe;
this.epe = null;
}
/**
* Returns whether the server will attempt to create a destination portal or not.
* Only applicable to {@link PlayerPortalEvent}
* @return whether there should create be a destination portal created
*/
public boolean getCanCreatePortal() {
return epe == null && ppe.getCanCreatePortal();
}
/**
* Returns the entity involved in this event
* @return Entity who is involved in this event
*/
@NonNull
public Entity getEntity() {
return epe == null ? ppe.getPlayer() : epe.getEntity();
}
/**
* Gets the location this player moved from
* @return Location the player or entity moved from
*/
@NonNull
public Location getFrom() {
return epe == null ? ppe.getFrom() : epe.getFrom();
}
/**
* Gets the location this player moved to
* @return Location the player moved to
*/
@Nullable
public Location getTo() {
return epe == null ? ppe.getTo() : epe.getTo();
}
/**
* @return true if constructed with an {@link EntityPortalEvent}
*/
public boolean isEntityPortalEvent() {
return epe != null;
}
/**
* @return true if constructed with an {@link PlayerPortalEvent}
*/
public boolean isPlayerPortalEvent() {
return ppe != null;
}
/**
* Sets the cancellation state of this event. A cancelled event will not be executed in the server, but will still pass to other plugins
* If a move or teleport event is cancelled, the player will be moved or teleported back to the Location as defined by getFrom(). This will not fire an event
* Specified by: setCancelled(...) in Cancellable
* @param cancel true if you wish to cancel this event
*/
public void setCancelled(boolean cancel) {
if (epe == null) {
ppe.setCancelled(cancel);
} else {
epe.setCancelled(cancel);
}
}
/**
* Sets whether the server should attempt to create a destination portal or not.
* Only applicable to {@link PlayerPortalEvent}
* @param canCreatePortal Sets whether there should be a destination portal created
*/
public void setCanCreatePortal(boolean canCreatePortal) {
if (ppe != null) {
ppe.setCanCreatePortal(canCreatePortal);
}
}
/**
* Set the Block radius to search in for available portals.
* @param searchRadius the radius in which to search for a portal from the location
*/
public void setSearchRadius(int searchRadius) {
if (epe == null) {
ppe.setSearchRadius(searchRadius);
} else {
epe.setSearchRadius(searchRadius);
}
}
/**
* Sets the location that this player will move to
* @param to New Location this player or entity will move to
*/
public void setTo(Location to) {
if (epe == null) {
ppe.setTo(to);
} else {
epe.setTo(to);
}
}
/**
* Get island at the from location
* @return optional island at from location
*/
public Optional<Island> getIsland() {
return BentoBox.getInstance().getIslands().getProtectedIslandAt(getFrom());
}
/**
* Get the from world
* @return from world
*/
@NonNull
public World getWorld() {
return Objects.requireNonNull(getFrom().getWorld(), "From world is null!");
}
}

View File

@ -1,489 +0,0 @@
package world.bentobox.bentobox.listeners;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.World.Environment;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityPortalEnterEvent;
import org.bukkit.event.entity.EntityPortalEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerPortalEvent;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import org.bukkit.util.Vector;
import org.eclipse.jdt.annotation.NonNull;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.addons.GameModeAddon;
import world.bentobox.bentobox.blueprints.Blueprint;
import world.bentobox.bentobox.blueprints.BlueprintPaster;
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBundle;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.Util;
import world.bentobox.bentobox.util.teleport.SafeSpotTeleport;
/**
* Handles teleportation via the Nether/End portals to the Nether and End dimensions of the worlds added by the GameModeAddons.
*
* @author tastybento
* @deprecated replaced by better listeners.
* @see world.bentobox.bentobox.listeners.teleports.PlayerTeleportListener
* @see world.bentobox.bentobox.listeners.teleports.EntityTeleportListener
* @since 1.12.1
*/
@Deprecated(since="1.21.0", forRemoval=true)
public class PortalTeleportationListener implements Listener {
private final BentoBox plugin;
private final Set<UUID> inPortal;
private final Set<UUID> inTeleport;
public PortalTeleportationListener(@NonNull BentoBox plugin) {
this.plugin = plugin;
inPortal = new HashSet<>();
inTeleport = new HashSet<>();
}
/**
* Fires the event if nether or end is disabled at the system level
* @param e - EntityPortalEnterEvent
*/
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onPlayerPortal(EntityPortalEnterEvent e) {
if (!(e.getEntity() instanceof Player)) {
return;
}
Entity entity = e.getEntity();
Material type = e.getLocation().getBlock().getType();
UUID uuid = entity.getUniqueId();
if (inPortal.contains(uuid) || !plugin.getIWM().inWorld(Util.getWorld(e.getLocation().getWorld()))) {
return;
}
inPortal.add(uuid);
if (!Bukkit.getAllowNether() && type.equals(Material.NETHER_PORTAL)) {
// Schedule a time
Bukkit.getScheduler().runTaskLater(plugin, () -> {
// Check again if still in portal
if (inPortal.contains(uuid)) {
this.onIslandPortal(new PlayerPortalEvent((Player)entity, e.getLocation(), null, TeleportCause.NETHER_PORTAL, 0, false, 0));
}
}, 40);
return;
}
// End portals are instant transfer
if (!Bukkit.getAllowEnd() && (type.equals(Material.END_PORTAL) || type.equals(Material.END_GATEWAY))) {
PlayerPortalEvent en = new PlayerPortalEvent((Player)entity,
e.getLocation(),
null,
type.equals(Material.END_PORTAL) ? TeleportCause.END_PORTAL : TeleportCause.END_GATEWAY,
0,
false,
0);
this.onIslandPortal(en);
}
}
/**
* Handles non-player portal use.
*
* @param e - event
*/
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onEntityPortal(EntityPortalEvent e) {
if (plugin.getIWM().inWorld(e.getFrom())) {
Optional<Material> mat = Arrays.stream(BlockFace.values())
.map(bf -> e.getFrom().getBlock().getRelative(bf).getType())
.filter(m -> m.equals(Material.NETHER_PORTAL)
|| m.equals(Material.END_PORTAL)
|| m.equals(Material.END_GATEWAY))
.findFirst();
if (mat.isEmpty()) {
e.setCancelled(true);
} else if (mat.get().equals(Material.NETHER_PORTAL)){
processPortal(new PlayerEntityPortalEvent(e), Environment.NETHER);
} else {
processPortal(new PlayerEntityPortalEvent(e), Environment.THE_END);
}
}
}
/**
* Remove inPortal flag only when player exits the portal
* @param e player move event
*/
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onExitPortal(PlayerMoveEvent e) {
if (!inPortal.contains(e.getPlayer().getUniqueId())) {
return;
}
if (e.getTo() != null && !e.getTo().getBlock().getType().equals(Material.NETHER_PORTAL)) {
inPortal.remove(e.getPlayer().getUniqueId());
inTeleport.remove(e.getPlayer().getUniqueId());
}
}
/**
* Handles nether or end portals
* @param e - event
*/
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onIslandPortal(PlayerPortalEvent e) {
switch (e.getCause()) {
case END_GATEWAY, END_PORTAL -> processPortal(new PlayerEntityPortalEvent(e), Environment.THE_END);
case NETHER_PORTAL -> processPortal(new PlayerEntityPortalEvent(e), Environment.NETHER);
default -> {
// Do nothing
}
}
}
/**
* Process the portal action
* @param e - event
* @param env - environment that this relates to - NETHER or THE_END
* @return true if portal happens, false if not
*/
private boolean processPortal(final PlayerEntityPortalEvent e, final Environment env) {
World fromWorld = e.getFrom().getWorld();
World overWorld = Util.getWorld(fromWorld);
if (overWorld == null || fromWorld == null || !plugin.getIWM().inWorld(overWorld)) {
// Do nothing special
return false;
}
if (!isGenerate(overWorld, env)) {
e.setCancelled(true);
return false;
}
if (!Bukkit.getAllowNether()) {
e.setCancelled(true);
}
if (inTeleport.contains(e.getEntity().getUniqueId())) {
return false;
}
inTeleport.add(e.getEntity().getUniqueId());
// STANDARD NETHER OR END
if (!isIslands(overWorld, env)) {
handleStandardNetherOrEnd(e, fromWorld, overWorld, env);
return true;
}
// FROM NETHER OR END
// If entering a portal in the other world, teleport to a portal in overworld if there is one
if (fromWorld.getEnvironment().equals(env)) {
handleFromNetherOrEnd(e, overWorld, env);
return true;
}
// TO NETHER OR END
World toWorld = getNetherEndWorld(overWorld, env);
// Set whether portals should be created or not
e.setCanCreatePortal(plugin.getIWM().getAddon(overWorld).map(gm -> isMakePortals(gm, env)).orElse(false));
// Set the destination location
// If portals cannot be created, then destination is the spawn point, otherwise it's the vector
e.setTo(getTo(e, env, toWorld));
// Find the distance from edge of island's protection and set the search radius
e.getIsland().ifPresent(i -> setSeachRadius(e, i));
// Check if there is an island there or not
if (e.getEntity().getType().equals(EntityType.PLAYER)
&& plugin.getIWM().isPasteMissingIslands(overWorld)
&& !plugin.getIWM().isUseOwnGenerator(overWorld)
&& isGenerate(overWorld, env)
&& isIslands(overWorld, env)
&& getNetherEndWorld(overWorld, env) != null
&& e.getIsland().filter(i -> !hasPartnerIsland(i, env)).map(i -> {
// No nether island present so paste the default one
e.setCancelled(true);
pasteNewIsland((Player)e.getEntity(), e.getTo(), i, env);
return true;
}).orElse(false)) {
// All done here
return true;
}
if (e.getCanCreatePortal()) {
// Let the server teleport
return true;
}
if (env.equals(Environment.THE_END)) {
// Prevent death from hitting the ground
e.getEntity().setVelocity(new Vector(0,0,0));
e.getEntity().setFallDistance(0);
}
// If we do not generate portals, teleportation should happen manually with safe spot builder.
// Otherwise, we could end up with situations when player is placed in mid air, if teleportation
// is done instantly.
// Our safe spot task is triggered in next tick, however, end teleportation happens in the same tick.
// It is placed outside THE_END check, as technically it could happen with the nether portal too.
e.setCancelled(true);
// If there is a portal to go to already, then the player will go there
Bukkit.getScheduler().runTask(plugin, () -> {
if (!e.getEntity().getWorld().equals(toWorld)) {
// Else manually teleport entity
new SafeSpotTeleport.Builder(plugin)
.entity(e.getEntity())
.location(e.getTo())
.portal()
.thenRun(() -> {
e.getEntity().setVelocity(new Vector(0,0,0));
e.getEntity().setFallDistance(0);
})
.build();
}
});
return true;
}
/**
* Set the destination of this portal action
* @param e - event
* @param env - environment
* @param toWorld - to world
*/
Location getTo(PlayerEntityPortalEvent e, Environment env, World toWorld)
{
// Null check - not that useful
if (e.getFrom().getWorld() == null || toWorld == null)
{
return null;
}
Location toLocation = Objects.requireNonNullElse(e.getIsland().map(island -> island.getSpawnPoint(env)).
orElse(e.getFrom().toVector().toLocation(toWorld)), e.getFrom().toVector().toLocation(toWorld));
// Limit Y to the min/max world height.
toLocation.setY(Math.max(Math.min(toLocation.getY(), toWorld.getMaxHeight()), toWorld.getMinHeight()));
if (!e.getCanCreatePortal())
{
// Legacy portaling
return toLocation;
}
// Make portals
// For anywhere other than the end - it is the player's location that is used
if (!env.equals(Environment.THE_END))
{
return toLocation;
}
// If the-end then we want the platform to always be generated in the same place no matter where
// they enter the portal
final int x = e.getFrom().getBlockX();
final int z = e.getFrom().getBlockZ();
final int y = e.getFrom().getBlockY();
int i = x;
int j = z;
int k = y;
// If the from is not a portal, then we have to find it
if (!e.getFrom().getBlock().getType().equals(Material.END_PORTAL))
{
// Find the portal - due to speed, it is possible that the player will be below or above the portal
for (k = toWorld.getMinHeight(); (k < e.getWorld().getMaxHeight()) &&
!e.getWorld().getBlockAt(x, k, z).getType().equals(Material.END_PORTAL); k++);
}
// Find the maximum x and z corner
for (; (i < x + 5) && e.getWorld().getBlockAt(i, k, z).getType().equals(Material.END_PORTAL); i++) ;
for (; (j < z + 5) && e.getWorld().getBlockAt(x, k, j).getType().equals(Material.END_PORTAL); j++) ;
// Mojang end platform generation is:
// AIR
// AIR
// OBSIDIAN
// and player is placed on second air block above obsidian.
// If Y coordinate is below 2, then obsidian platform is not generated and player falls in void.
return new Location(toWorld, i, Math.max(toWorld.getMinHeight() + 2, k), j);
}
/**
* Check if vanilla portals should be used
* @param gm - game mode
* @param env - environment
* @return true or false
*/
private boolean isMakePortals(GameModeAddon gm, Environment env) {
return env.equals(Environment.NETHER) ?
gm.getWorldSettings().isMakeNetherPortals() && Bukkit.getAllowNether() :
gm.getWorldSettings().isMakeEndPortals() && Bukkit.getAllowEnd();
}
/**
* Check if nether or end are generated
* @param overWorld - game world
* @param env - environment
* @return true or false
*/
private boolean isGenerate(World overWorld, Environment env) {
return env.equals(Environment.NETHER) ? plugin.getIWM().isNetherGenerate(overWorld) : plugin.getIWM().isEndGenerate(overWorld);
}
/**
* Check if nether or end islands are generated
* @param overWorld - over world
* @param env - environment
* @return true or false
*/
private boolean isIslands(World overWorld, Environment env) {
return env.equals(Environment.NETHER) ? plugin.getIWM().isNetherIslands(overWorld) : plugin.getIWM().isEndIslands(overWorld);
}
/**
* Get the nether or end world
* @param overWorld - over world
* @param env - environment
* @return nether or end world
*/
private World getNetherEndWorld(World overWorld, Environment env) {
return env.equals(Environment.NETHER) ? plugin.getIWM().getNetherWorld(overWorld) : plugin.getIWM().getEndWorld(overWorld);
}
/**
* Check if the island has a nether or end island already
* @param i - island
* @param env - environment
* @return true or false
*/
private boolean hasPartnerIsland(Island i, Environment env) {
return env.equals(Environment.NETHER) ? i.hasNetherIsland() : i.hasEndIsland();
}
/**
* Check if the default nether or end are allowed by the server settings
* @param env - environment
* @return true or false
*/
private boolean isAllowedOnServer(Environment env) {
return env.equals(Environment.NETHER) ? Bukkit.getAllowNether() : Bukkit.getAllowEnd();
}
/**
* Handle teleport from nether or end to overworld
* @param e - event
* @param overWorld - over world
* @param env - environment
*/
private void handleFromNetherOrEnd(PlayerEntityPortalEvent e, World overWorld, Environment env) {
// Standard portals
if (plugin.getIWM().getAddon(overWorld).map(gm -> isMakePortals(gm, env)).orElse(false)) {
e.setTo(e.getFrom().toVector().toLocation(overWorld));
// Find distance from edge of island's protection
plugin.getIslands().getIslandAt(e.getFrom()).ifPresent(i -> setSeachRadius(e, i));
return;
}
// Custom portals
e.setCancelled(true);
// If this is from the island nether or end, then go to the same vector, otherwise try island home location
Location to = plugin.getIslands().getIslandAt(e.getFrom()).map(i -> i.getSpawnPoint(Environment.NORMAL)).orElse(e.getFrom().toVector().toLocation(overWorld));
e.setTo(to);
// Else other worlds teleport to the nether
new SafeSpotTeleport.Builder(plugin)
.entity(e.getEntity())
.location(to)
.portal()
.build();
}
/**
* Handle teleport from or to standard nether or end
* @param e - PlayerEntityPortalEvent
* @param fromWorld - from world
* @param overWorld - over world
* @param env - environment involved
*/
private void handleStandardNetherOrEnd(PlayerEntityPortalEvent e, World fromWorld, World overWorld, Environment env) {
if (fromWorld.getEnvironment() != env) {
World toWorld = Objects.requireNonNull(getNetherEndWorld(overWorld, env));
Location spawnPoint = toWorld.getSpawnLocation();
// If going to the nether and nether portals are active then just teleport to approx location
if (env.equals(Environment.NETHER) && plugin.getIWM().getWorldSettings(overWorld).isMakeNetherPortals()) {
spawnPoint = e.getFrom().toVector().toLocation(toWorld);
}
// If spawn is set as 0,63,0 in the End then move it to 100, 50 ,0.
if (env.equals(Environment.THE_END) && spawnPoint.getBlockX() == 0 && spawnPoint.getBlockZ() == 0) {
// Set to the default end spawn
spawnPoint = new Location(toWorld, 100, 50, 0);
toWorld.setSpawnLocation(100, 50, 0);
}
if (isAllowedOnServer(env)) {
// To Standard Nether or end
e.setTo(spawnPoint);
} else {
// Teleport to standard nether or end
new SafeSpotTeleport.Builder(plugin)
.entity(e.getEntity())
.location(spawnPoint)
.portal()
.build();
}
}
// From standard nether or end
else if (e.getEntity() instanceof Player player){
e.setCancelled(true);
plugin.getIslands().homeTeleportAsync(overWorld, player);
}
}
void setSeachRadius(PlayerEntityPortalEvent e, Island i) {
if (!i.onIsland(e.getFrom())) return;
// Find max x or max z
int x = Math.abs(i.getProtectionCenter().getBlockX() - e.getFrom().getBlockX());
int z = Math.abs(i.getProtectionCenter().getBlockZ() - e.getFrom().getBlockZ());
int diff = Math.max(plugin.getSettings().getMinPortalSearchRadius(), i.getProtectionRange() - Math.max(x, z));
if (diff > 0 && diff < 128) {
e.setSearchRadius(diff);
}
}
/**
* Pastes the default nether or end island and teleports the player to the island's spawn point
* @param player - player to teleport after pasting
* @param to - the fallback location if a spawn point is not part of the blueprint
* @param island - the island
* @param env - NETHER or THE_END
*/
private void pasteNewIsland(Player player, Location to, Island island, Environment env) {
// Paste then teleport player
plugin.getIWM().getAddon(island.getWorld()).ifPresent(addon -> {
// Get the default bundle's nether or end blueprint
BlueprintBundle bb = plugin.getBlueprintsManager().getDefaultBlueprintBundle(addon);
if (bb != null) {
Blueprint bp = plugin.getBlueprintsManager().getBlueprints(addon).get(bb.getBlueprint(env));
if (bp != null) {
new BlueprintPaster(plugin, bp,
to.getWorld(),
island).paste().thenAccept(b -> new SafeSpotTeleport.Builder(plugin)
.entity(player)
.location(island.getSpawnPoint(env) == null ? to : island.getSpawnPoint(env))
// No need to use portal because there will be no portal on the other end
.build());
} else {
plugin.logError("Could not paste default island in nether or end. Is there a nether-island or end-island blueprint?");
}
}
});
}
}

View File

@ -0,0 +1,57 @@
package world.bentobox.bentobox.listeners;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.eclipse.jdt.annotation.NonNull;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.managers.IslandsManager;
/**
* Sets the player's primary island based on where they teleported or moved to
* @author tastybento
*
*/
public class PrimaryIslandListener implements Listener {
private final IslandsManager im;
/**
* @param plugin - plugin object
*/
public PrimaryIslandListener(@NonNull BentoBox plugin) {
this.im = plugin.getIslands();
}
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onPlayerJoin(final PlayerJoinEvent event) {
setIsland(event.getPlayer(), event.getPlayer().getLocation());
}
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onPlayerMove(final PlayerMoveEvent event) {
if (event.getTo() != null && !event.getFrom().toVector().equals(event.getTo().toVector())) {
setIsland(event.getPlayer(), event.getTo());
}
}
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onPlayerMove(final PlayerTeleportEvent event) {
if (event.getTo() != null) {
setIsland(event.getPlayer(), event.getTo());
}
}
private void setIsland(Player player, Location location) {
im.getIslandAt(location)
.filter(i -> player.getUniqueId().equals(i.getOwner()))
.ifPresent(i -> im.setPrimaryIsland(player.getUniqueId(), i));
}
}

View File

@ -38,20 +38,19 @@ public class CommandCycleClick implements ClickHandler {
World world = panel.getWorld().orElse(user.getWorld());
Island island = plugin.getIslands().getIsland(world, user.getUniqueId());
if (island != null && island.getOwner() != null && island.isAllowed(user, Flags.CHANGE_SETTINGS)) {
RanksManager rm = plugin.getRanksManager();
int currentRank = island.getRankCommand(command);
if (click.equals(ClickType.LEFT)) {
if (currentRank == RanksManager.OWNER_RANK) {
island.setRankCommand(command, RanksManager.MEMBER_RANK);
} else {
island.setRankCommand(command, rm.getRankUpValue(currentRank));
island.setRankCommand(command, RanksManager.getInstance().getRankUpValue(currentRank));
}
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1F, 1F);
} else if (click.equals(ClickType.RIGHT)) {
if (currentRank == RanksManager.MEMBER_RANK) {
island.setRankCommand(command, RanksManager.OWNER_RANK);
} else {
island.setRankCommand(command, rm.getRankDownValue(currentRank));
island.setRankCommand(command, RanksManager.getInstance().getRankDownValue(currentRank));
}
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1F, 1F);
}

View File

@ -4,7 +4,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.World;
@ -16,12 +15,14 @@ import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.panels.Panel;
import world.bentobox.bentobox.api.panels.PanelItem;
import world.bentobox.bentobox.api.panels.PanelItem.ClickHandler;
import world.bentobox.bentobox.api.panels.TabbedPanel;
import world.bentobox.bentobox.api.panels.builders.PanelBuilder;
import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.lists.Flags;
import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.bentobox.panels.settings.SettingsTab;
import world.bentobox.bentobox.util.Util;
/**
@ -31,12 +32,19 @@ import world.bentobox.bentobox.util.Util;
public class CommandRankClickListener implements ClickHandler {
private final BentoBox plugin = BentoBox.getInstance();
private Island island;
/* (non-Javadoc)
* @see world.bentobox.bentobox.api.panels.PanelItem.ClickHandler#onClick(world.bentobox.bentobox.api.panels.Panel, world.bentobox.bentobox.api.user.User, org.bukkit.event.inventory.ClickType, int)
*/
@Override
public boolean onClick(Panel panel, User user, ClickType clickType, int slot) {
// This click listener is used with TabbedPanel and SettingsTabs only
TabbedPanel tp = (TabbedPanel)panel;
SettingsTab st = (SettingsTab)tp.getActiveTab();
// Get the island for this tab
island = st.getIsland();
// Get the world
if (!user.inWorld()) {
user.sendMessage("general.errors.wrong-world");
@ -55,17 +63,16 @@ public class CommandRankClickListener implements ClickHandler {
return true;
}
// Get the user's island
Island island = plugin.getIslands().getIsland(panel.getWorld().orElse(user.getWorld()), user.getUniqueId());
if (island == null || island.getOwner() == null || !island.isAllowed(user, Flags.CHANGE_SETTINGS)) {
user.sendMessage("general.errors.insufficient-rank",
TextVariables.RANK,
user.getTranslation(plugin.getRanksManager().getRank(Objects.requireNonNull(island).getRank(user))));
// Check if user has rank enough on the island
//Island island = plugin.getIslands().getIsland(panel.getWorld().orElse(user.getWorld()), user.getUniqueId());
if (!island.isAllowed(user, Flags.CHANGE_SETTINGS)) {
String rank = user.getTranslation(RanksManager.getInstance().getRank(Objects.requireNonNull(island).getRank(user)));
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK, rank);
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_METAL_HIT, 1F, 1F);
return true;
}
String panelName = user.getTranslation("protection.flags.COMMAND_RANKS.name");
if (panel.getName().equals(panelName)) {
// This is a click on the panel
@ -100,7 +107,6 @@ public class CommandRankClickListener implements ClickHandler {
* @return panel item for this command
*/
public PanelItem getPanelItem(String c, User user, World world) {
Island island = plugin.getIslands().getIsland(world, user);
PanelItemBuilder pib = new PanelItemBuilder();
pib.name(c);
pib.clickHandler(new CommandCycleClick(this, c));
@ -108,7 +114,7 @@ public class CommandRankClickListener implements ClickHandler {
// TODO: use specific layout
String d = user.getTranslation("protection.panel.flag-item.description-layout", TextVariables.DESCRIPTION, "");
pib.description(d);
plugin.getRanksManager().getRanks().forEach((reference, score) -> {
RanksManager.getInstance().getRanks().forEach((reference, score) -> {
if (score >= RanksManager.MEMBER_RANK && score < island.getRankCommand(c)) {
pib.description(user.getTranslation("protection.panel.flag-item.blocked-rank") + user.getTranslation(reference));
} else if (score <= RanksManager.OWNER_RANK && score > island.getRankCommand(c)) {
@ -126,7 +132,7 @@ public class CommandRankClickListener implements ClickHandler {
.filter(c -> c.getWorld() != null && c.getWorld().equals(world))
.forEach(c -> result.addAll(getCmdRecursively("/", c)));
if (result.size() > 49) {
Bukkit.getLogger().severe("Number of rank setting commands is too big for GUI");
plugin.logError("Number of rank setting commands is too big for GUI");
result.subList(49, result.size()).clear();
}
return result;

View File

@ -48,6 +48,7 @@ public class GeoMobLimitTab implements Tab, ClickHandler {
private final User user;
private final EntityLimitTabType type;
private final World world;
private TabbedPanel parent;
/**
* @param user - user viewing the tab
@ -61,7 +62,6 @@ public class GeoMobLimitTab implements Tab, ClickHandler {
this.world = world;
}
@Override
public boolean onClick(Panel panel, User user, ClickType clickType, int slot) {
// Case panel to Tabbed Panel to get the active page
@ -140,4 +140,14 @@ public class GeoMobLimitTab implements Tab, ClickHandler {
return pib.build();
}
@Override
public TabbedPanel getParentPanel() {
return parent;
}
@Override
public void setParentPanel(TabbedPanel parent) {
this.parent = parent;
}
}

View File

@ -210,7 +210,8 @@ public class BlockInteractionListener extends FlagListener
return true;
}
if (Tag.SIGNS.isTagged(type) && block.getState() instanceof Sign sign && !sign.isWaxed()) {
if ((Tag.ALL_HANGING_SIGNS.isTagged(type) || Tag.SIGNS.isTagged(type)) && block.getState() instanceof Sign sign
&& !sign.isWaxed()) {
// If waxed, then sign cannot be edited otherwise check
this.checkIsland(e, player, loc, Flags.SIGN_EDITING);
return true;

View File

@ -4,6 +4,7 @@ import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Tag;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.type.CaveVinesPlant;
import org.bukkit.entity.AbstractArrow;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.EnderCrystal;
@ -76,14 +77,26 @@ public class BreakBlocksListener extends FlagListener {
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onPlayerInteract(final PlayerInteractEvent e)
{
// Only handle hitting things
if (!e.getAction().equals(Action.LEFT_CLICK_BLOCK) || e.getClickedBlock() == null)
{
return;
}
Player p = e.getPlayer();
Location l = e.getClickedBlock().getLocation();
Material m = e.getClickedBlock().getType();
// Check for berry picking
if (e.getAction() == Action.RIGHT_CLICK_BLOCK && (e.getClickedBlock().getType() == Material.CAVE_VINES || e.getClickedBlock().getType() == Material.CAVE_VINES_PLANT)) {
if (!((CaveVinesPlant) e.getClickedBlock().getBlockData()).isBerries()) {
return;
}
this.checkIsland(e, p, l, Flags.HARVEST);
return;
}
if (e.getAction() == Action.RIGHT_CLICK_BLOCK && e.getClickedBlock().getType() == Material.SWEET_BERRY_BUSH) {
this.checkIsland(e, p, l, Flags.HARVEST);
return;
}
// Only handle hitting things
if (!(e.getAction() == Action.LEFT_CLICK_BLOCK) || e.getClickedBlock() == null)
{
return;
}
switch (m)
{
case CAKE -> this.checkIsland(e, p, l, Flags.BREAK_BLOCKS);

View File

@ -17,40 +17,34 @@ import world.bentobox.bentobox.lists.Flags;
*/
public class DyeListener extends FlagListener {
/**
* Prevent dying signs.
* @param e - event
*/
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onPlayerInteract(final PlayerInteractEvent e)
{
if (e.getClickedBlock() == null || e.getItem() == null)
{
return;
}
/**
* Prevent dying signs.
* @param e - event
*/
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onPlayerInteract(final PlayerInteractEvent e) {
if (e.getClickedBlock() == null || e.getItem() == null) {
return;
}
if (e.getAction().equals(Action.RIGHT_CLICK_BLOCK) &&
e.getClickedBlock().getType().name().contains("SIGN") &&
(e.getItem().getType().name().contains("DYE") || e.getItem().getType().equals(Material.GLOW_INK_SAC)))
{
this.checkIsland(e, e.getPlayer(), e.getClickedBlock().getLocation(), Flags.DYE);
}
}
if (e.getAction().equals(Action.RIGHT_CLICK_BLOCK) && e.getClickedBlock().getType().name().contains("SIGN")
&& (e.getItem().getType().name().contains("DYE")
|| e.getItem().getType().equals(Material.GLOW_INK_SAC))) {
this.checkIsland(e, e.getPlayer(), e.getClickedBlock().getLocation(), Flags.DYE);
}
}
/**
* Prevents from interacting with sheep.
* @param e - event
*/
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onPlayerInteract(final SheepDyeWoolEvent e) {
if (e.getPlayer() == null) {
// Sheep is not dyed by the player.
return;
}
/**
* Prevents from interacting with sheep.
* @param e - event
*/
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onPlayerInteract(final SheepDyeWoolEvent e)
{
if (e.getPlayer() == null)
{
// Sheep is not dyed by the player.
return;
}
this.checkIsland(e, e.getPlayer(), e.getPlayer().getLocation(), Flags.DYE);
}
this.checkIsland(e, e.getPlayer(), e.getPlayer().getLocation(), Flags.DYE);
}
}

View File

@ -22,7 +22,27 @@ import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryOpenEvent;
import org.bukkit.inventory.AbstractHorseInventory;
import org.bukkit.inventory.AnvilInventory;
import org.bukkit.inventory.BeaconInventory;
import org.bukkit.inventory.BrewerInventory;
import org.bukkit.inventory.CartographyInventory;
import org.bukkit.inventory.ChiseledBookshelfInventory;
import org.bukkit.inventory.CraftingInventory;
import org.bukkit.inventory.DoubleChestInventory;
import org.bukkit.inventory.EnchantingInventory;
import org.bukkit.inventory.FurnaceInventory;
import org.bukkit.inventory.GrindstoneInventory;
import org.bukkit.inventory.HorseInventory;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.JukeboxInventory;
import org.bukkit.inventory.LecternInventory;
import org.bukkit.inventory.LlamaInventory;
import org.bukkit.inventory.LoomInventory;
import org.bukkit.inventory.MerchantInventory;
import org.bukkit.inventory.SmithingInventory;
import org.bukkit.inventory.StonecutterInventory;
import world.bentobox.bentobox.api.flags.FlagListener;
import world.bentobox.bentobox.lists.Flags;
@ -69,6 +89,12 @@ public class InventoryListener extends FlagListener
public void onInventoryClick(InventoryClickEvent e)
{
Player player = (Player) e.getWhoClicked();
// Special inventory types
if (checkSpecificInventories(e, player, e.getInventory())) {
return;
}
// Inventory holders
InventoryHolder inventoryHolder = e.getInventory().getHolder();
if (inventoryHolder == null || !(e.getWhoClicked() instanceof Player))
@ -130,7 +156,6 @@ public class InventoryListener extends FlagListener
}
else if (inventoryHolder instanceof ChestBoat)
{
// TODO: 1.19 added chest boat. Remove compatibility check when 1.18 is dropped.
this.checkIsland(e, player, e.getInventory().getLocation(), Flags.CHEST);
}
else if (!(inventoryHolder instanceof Player))
@ -141,6 +166,69 @@ public class InventoryListener extends FlagListener
}
private boolean checkSpecificInventories(InventoryClickEvent e, Player player, Inventory inventory) {
if (e.getInventory() instanceof AbstractHorseInventory) {
this.checkIsland(e, player, e.getInventory().getLocation(), Flags.MOUNT_INVENTORY);
return true;
} else if (e.getInventory() instanceof AnvilInventory) {
this.checkIsland(e, player, e.getInventory().getLocation(), Flags.ANVIL);
return true;
} else if (e.getInventory() instanceof BeaconInventory) {
this.checkIsland(e, player, e.getInventory().getLocation(), Flags.BEACON);
return true;
} else if (e.getInventory() instanceof BrewerInventory) {
this.checkIsland(e, player, e.getInventory().getLocation(), Flags.BREWING);
return true;
} else if (e.getInventory() instanceof CartographyInventory) {
this.checkIsland(e, player, e.getInventory().getLocation(), Flags.CARTOGRAPHY);
return true;
} else if (e.getInventory() instanceof ChiseledBookshelfInventory) {
this.checkIsland(e, player, e.getInventory().getLocation(), Flags.BOOKSHELF);
return true;
} else if (e.getInventory() instanceof CraftingInventory) {
this.checkIsland(e, player, e.getInventory().getLocation(), Flags.CRAFTING);
return true;
} else if (e.getInventory() instanceof DoubleChestInventory) {
checkInvHolder(e.getInventory().getLocation(), e, player);
return true;
} else if (e.getInventory() instanceof EnchantingInventory) {
this.checkIsland(e, player, e.getInventory().getLocation(), Flags.ENCHANTING);
return true;
} else if (e.getInventory() instanceof FurnaceInventory) {
this.checkIsland(e, player, e.getInventory().getLocation(), Flags.FURNACE);
return true;
} else if (e.getInventory() instanceof GrindstoneInventory) {
this.checkIsland(e, player, e.getInventory().getLocation(), Flags.GRINDSTONE);
return true;
} else if (e.getInventory() instanceof HorseInventory) {
this.checkIsland(e, player, e.getInventory().getLocation(), Flags.MOUNT_INVENTORY);
return true;
} else if (e.getInventory() instanceof JukeboxInventory) {
this.checkIsland(e, player, e.getInventory().getLocation(), Flags.JUKEBOX);
return true;
} else if (e.getInventory() instanceof LecternInventory) {
this.checkIsland(e, player, e.getInventory().getLocation(), Flags.LECTERN);
return true;
} else if (e.getInventory() instanceof LlamaInventory) {
this.checkIsland(e, player, e.getInventory().getLocation(), Flags.MOUNT_INVENTORY);
return true;
} else if (e.getInventory() instanceof LoomInventory) {
this.checkIsland(e, player, e.getInventory().getLocation(), Flags.LOOM);
return true;
} else if (e.getInventory() instanceof MerchantInventory) {
this.checkIsland(e, player, e.getInventory().getLocation(), Flags.TRADING);
return true;
} else if (e.getInventory() instanceof SmithingInventory) {
this.checkIsland(e, player, e.getInventory().getLocation(), Flags.SMITHING);
return true;
} else if (e.getInventory() instanceof StonecutterInventory) {
this.checkIsland(e, player, e.getInventory().getLocation(), Flags.STONECUTTING);
return true;
}
return false;
}
/**
* This method runs check based on clicked chest type.
* @param l location of chest.

View File

@ -162,7 +162,10 @@ public class LockAndBanListener extends FlagListener {
} else {
// There's nothing much we can do.
// We'll try to teleport him to the spawn...
PaperLib.teleportAsync(player, player.getWorld().getSpawnLocation());
Location l = player.getWorld().getSpawnLocation();
if (l != null) {
PaperLib.teleportAsync(player, l);
}
// Switch him back to the default gamemode. He may die, sorry :(
player.setGameMode(getIWM().getDefaultGameMode(player.getWorld()));

View File

@ -15,6 +15,7 @@ import world.bentobox.bentobox.lists.Flags;
/**
* Listener for {@link Flags#CROP_TRAMPLE, Flags#PRESSURE_PLATE, Flags#TURTLE_EGGS, Flags#BUTTON}
* @author tastybento
*
*/

View File

@ -4,6 +4,7 @@ import java.util.Set;
import org.bukkit.Material;
import org.bukkit.Tag;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@ -23,7 +24,8 @@ import world.bentobox.bentobox.lists.Flags;
*/
public class PlaceBlocksListener extends FlagListener
{
public static final Set<Material> SEEDS = Set.of(Material.BEETROOT_SEEDS, Material.MELON_SEEDS, Material.WHEAT_SEEDS);
public static final Set<Material> SEEDS = Set.of(Material.MELON_SEEDS, Material.WHEAT_SEEDS,
Material.SWEET_BERRIES);
/**
* Check blocks being placed in general
*
@ -42,8 +44,15 @@ public class PlaceBlocksListener extends FlagListener
// Books can only be placed on lecterns and as such are protected by the LECTERN flag.
return;
}
// Glowberries
if (e.getItemInHand().getType() == Material.GLOW_BERRIES
&& e.getBlock().getRelative(BlockFace.UP).equals(e.getBlockAgainst())) {
this.checkIsland(e, e.getPlayer(), e.getBlock().getLocation(), Flags.CROP_PLANTING);
return;
}
// Crops
if (against.equals(Material.FARMLAND) && SEEDS.contains(e.getItemInHand().getType())) {
if (against.equals(Material.FARMLAND) && (SEEDS.contains(e.getItemInHand().getType())
|| Tag.ITEMS_VILLAGER_PLANTABLE_SEEDS.isTagged(e.getItemInHand().getType()))) {
this.checkIsland(e, e.getPlayer(), e.getBlock().getLocation(), Flags.CROP_PLANTING);
} else {
this.checkIsland(e, e.getPlayer(), e.getBlock().getLocation(), Flags.PLACE_BLOCKS);

View File

@ -4,6 +4,7 @@ import java.util.Arrays;
import java.util.Comparator;
import java.util.Objects;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.World;
@ -18,6 +19,7 @@ import org.bukkit.event.inventory.ClickType;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.addons.GameModeAddon;
import world.bentobox.bentobox.api.events.flags.InvincibleVistorFlagDamageRemovalEvent;
import world.bentobox.bentobox.api.flags.FlagListener;
import world.bentobox.bentobox.api.panels.Panel;
import world.bentobox.bentobox.api.panels.PanelItem;
@ -130,13 +132,21 @@ public class InvincibleVisitorsListener extends FlagListener implements ClickHan
World world = e.getEntity().getWorld();
if (!(e.getEntity() instanceof Player p)
|| !getIWM().inWorld(world)
|| e.getEntity().hasMetadata("NPC")
|| p.hasMetadata("NPC")
|| !getIWM().getIvSettings(world).contains(e.getCause().name())
|| getIslands().userIsOnIsland(world, User.getInstance(e.getEntity()))
|| getIslands().userIsOnIsland(world, User.getInstance(p))
|| PVPAllowed(p.getLocation())
) {
return;
}
// Fire event
InvincibleVistorFlagDamageRemovalEvent event = new InvincibleVistorFlagDamageRemovalEvent(p, e.getCause());
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) {
// Give others a chance to ignore the protection
return;
}
// Player is a visitor and should be protected from damage
e.setCancelled(true);
// Handle the void - teleport player back to island in a safe spot
@ -169,12 +179,12 @@ public class InvincibleVisitorsListener extends FlagListener implements ClickHan
World world = e.getEntity().getWorld();
if (!(e.getTarget() instanceof Player p) ||
!this.getIWM().inWorld(world) ||
e.getTarget().hasMetadata("NPC") ||
this.getIslands().userIsOnIsland(world, User.getInstance(e.getTarget())) ||
this.PVPAllowed(p.getLocation()) ||
e.getReason() == EntityTargetEvent.TargetReason.TARGET_DIED ||
!this.getIWM().getIvSettings(world).contains(DamageCause.ENTITY_ATTACK.name()))
!this.getIWM().inWorld(world) ||
e.getTarget().hasMetadata("NPC") ||
this.getIslands().userIsOnIsland(world, User.getInstance(e.getTarget())) ||
this.PVPAllowed(p.getLocation()) ||
e.getReason() == EntityTargetEvent.TargetReason.TARGET_DIED ||
!this.getIWM().getIvSettings(world).contains(DamageCause.ENTITY_ATTACK.name()))
{
return;
}
@ -182,5 +192,6 @@ public class InvincibleVisitorsListener extends FlagListener implements ClickHan
// Cancel targeting event.
e.setCancelled(true);
}
}

View File

@ -20,6 +20,7 @@ import world.bentobox.bentobox.util.Util;
/**
* Handles respawning back on island
*
* @author tastybento
*
*/
@ -29,6 +30,7 @@ public class IslandRespawnListener extends FlagListener {
/**
* Tag players who die in island space and have an island
*
* @param e - event
*/
@EventHandler(priority = EventPriority.LOW)
@ -40,7 +42,8 @@ public class IslandRespawnListener extends FlagListener {
if (!Flags.ISLAND_RESPAWN.isSetForWorld(world)) {
return; // world doesn't have the island respawn flag
}
if (!getIslands().hasIsland(world, e.getEntity().getUniqueId()) && !getIslands().inTeam(world, e.getEntity().getUniqueId())) {
if (!getIslands().hasIsland(world, e.getEntity().getUniqueId())
&& !getIslands().inTeam(world, e.getEntity().getUniqueId())) {
return; // doesn't have an island in this world
}
@ -49,6 +52,7 @@ public class IslandRespawnListener extends FlagListener {
/**
* Place players back on their island if respawn on island is true and active
*
* @param e - event
*/
@EventHandler(priority = EventPriority.HIGHEST)
@ -65,7 +69,8 @@ public class IslandRespawnListener extends FlagListener {
World w = Util.getWorld(world);
String ownerName = e.getPlayer().getName();
if (w != null) {
final Location respawnLocation = getIslands().getSafeHomeLocation(w, User.getInstance(e.getPlayer().getUniqueId()), "");
final Location respawnLocation = getIslands().getPrimaryIsland(world, e.getPlayer().getUniqueId())
.getSpawnPoint(world.getEnvironment());
if (respawnLocation != null) {
e.setRespawnLocation(respawnLocation);
// Get the island owner name

View File

@ -1,8 +1,10 @@
package world.bentobox.bentobox.listeners.flags.worldsettings;
import org.bukkit.entity.EntityType;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
import world.bentobox.bentobox.api.flags.FlagListener;
@ -19,7 +21,14 @@ public class LimitMobsListener extends FlagListener {
*/
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onMobSpawn(CreatureSpawnEvent e) {
if (getIWM().inWorld(e.getLocation()) && getIWM().getMobLimitSettings(e.getLocation().getWorld()).contains(e.getEntityType().name())) {
check(e, e.getEntityType());
if (e.getSpawnReason().equals(SpawnReason.JOCKEY) ) {
e.getEntity().getPassengers().forEach(pass -> check(e, pass.getType()));
}
}
private void check(CreatureSpawnEvent e, EntityType type) {
if (getIWM().inWorld(e.getLocation()) && getIWM().getMobLimitSettings(e.getLocation().getWorld()).contains(type.name())) {
e.setCancelled(true);
}
}

View File

@ -19,6 +19,7 @@ import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
@ -274,14 +275,54 @@ public abstract class AbstractTeleportListener
// If the from is not a portal, then we have to find it
if (!fromLocation.getBlock().getType().equals(Material.END_PORTAL))
{
// Find the portal - due to speed, it is possible that the player will be below or above the portal
for (k = toWorld.getMinHeight(); (k < fromWorld.getMaxHeight()) &&
!fromWorld.getBlockAt(x, k, z).getType().equals(Material.END_PORTAL); k++);
// Search portal block 5 blocks in all directions from starting location. Return the first one.
boolean continueSearch = true;
// simplistic search pattern to look at all blocks from the middle outwards by preferring
// Y location first, then Z and as last X
// Proper implementation would require queue and distance calculation.
for (int offsetX = 0; continueSearch && offsetX < 10; offsetX++)
{
// Change sign based on mod value.
int posX = x + ((offsetX % 2 == 0) ? 1 : -1) * (offsetX / 2);
for (int offsetZ = 0; continueSearch && offsetZ < 10; offsetZ++)
{
// Change sign based on mod value.
int posZ = z + ((offsetZ % 2 == 0) ? 1 : -1) * (offsetZ / 2);
for (int offsetY = 0; continueSearch && offsetY < 10; offsetY++)
{
// Change sign based on mod value.
int posY = y + ((offsetY % 2 == 0) ? 1 : -1) * (offsetY / 2);
if (fromWorld.getBlockAt(posX, posY, posZ).getType().equals(Material.END_PORTAL))
{
i = posX;
j = posZ;
k = posY;
continueSearch = false;
}
}
}
}
}
// Find the maximum x and z corner
for (; (i < x + 5) && fromWorld.getBlockAt(i, k, z).getType().equals(Material.END_PORTAL); i++) ;
for (; (j < z + 5) && fromWorld.getBlockAt(x, k, j).getType().equals(Material.END_PORTAL); j++) ;
// Find the maximum x and z corner using relative block search.
Block portalBlock = fromWorld.getBlockAt(i, k, j);
while (portalBlock.getRelative(1, 0, 0).getType() == Material.END_PORTAL)
{
portalBlock = portalBlock.getRelative(1, 0, 0);
i++;
}
while (portalBlock.getRelative(0, 0, 1).getType() == Material.END_PORTAL)
{
portalBlock = portalBlock.getRelative(0, 0, 1);
j++;
}
// Mojang end platform generation is:
// AIR
@ -289,7 +330,7 @@ public abstract class AbstractTeleportListener
// OBSIDIAN
// and player is placed on second air block above obsidian.
// If Y coordinate is below 2, then obsidian platform is not generated and player falls in void.
return new Location(toWorld, i, Math.max(toWorld.getMinHeight() + 2, k), j);
return new Location(toWorld, i - 0.5, Math.min(toWorld.getMaxHeight() - 2, Math.max(toWorld.getMinHeight() + 2, k)), j - 0.5);
}
@ -311,7 +352,7 @@ public abstract class AbstractTeleportListener
/**
* This method returns if missing islands should be generated uppon teleportation.
* This method returns if missing islands should be generated upon teleportation.
* Can happen only in non-custom generators.
* @param overWorld OverWorld
* @return {@code true} if missing islands must be pasted, {@code false} otherwise.

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