Compare commits
111 Commits
Author | SHA1 | Date |
---|---|---|
tastybento | fc9b00233b | |
tastybento | 1fd4a9043f | |
tastybento | ee3b0bfcc2 | |
tastybento | 35704b3fd3 | |
tastybento | 60fa60372d | |
tastybento | 8b0a5a3d0b | |
tastybento | ca15740a8c | |
tastybento | 5e5707f2a2 | |
tastybento | ef58838c41 | |
BONNe | 885d2449d9 | |
tastybento | 888b485f82 | |
tastybento | e2d9c2ce34 | |
tastybento | 8aba736383 | |
tastybento | 50276cb8e5 | |
tastybento | d701b7e43c | |
tastybento | 2fc3396a8f | |
tastybento | 99717f5b60 | |
tastybento | 290158e6ef | |
tastybento | 7126e837ed | |
tastybento | 45e5621d4c | |
tastybento | f536a13c59 | |
tastybento | f4780659e3 | |
tastybento | aad50eab38 | |
tastybento | 24d81da907 | |
tastybento | d288528a17 | |
tastybento | 83698c267f | |
tastybento | 52a280dc0b | |
tastybento | 4a0d44c035 | |
tastybento | b8e1f33800 | |
tastybento | d8891796cd | |
tastybento | 61e7c22bbc | |
tastybento | b1fe76c45d | |
tastybento | 5afd454fb3 | |
tastybento | a55c51412d | |
tastybento | 01a8055379 | |
tastybento | 744665a16e | |
tastybento | 3e4ff33d30 | |
tastybento | 09ede87971 | |
tastybento | b1418c144f | |
tastybento | 15335eb992 | |
tastybento | e33823d0c0 | |
tastybento | 63cc0a01d9 | |
tastybento | 6949432cb6 | |
tastybento | 69a22e917e | |
tastybento | ffb955b22b | |
tastybento | 3de0ff236e | |
tastybento | c86eb6a19c | |
tastybento | e7055c6cba | |
tastybento | 5834dcbb59 | |
tastybento | 2c75939bc3 | |
tastybento | e570401912 | |
tastybento | f6f4da1c89 | |
tastybento | 6106b661e9 | |
tastybento | e1536fcae0 | |
tastybento | 1c19703f44 | |
tastybento | 24b7d26fbe | |
tastybento | ec60991aeb | |
tastybento | 33000f9371 | |
tastybento | 83eaa50b49 | |
tastybento | 1215a43766 | |
tastybento | 81f765df36 | |
tastybento | 0e6a25d74b | |
tastybento | 2b19d43c85 | |
tastybento | 1bce4ec1b9 | |
tastybento | ea8562f351 | |
tastybento | d8f2c12fe5 | |
tastybento | b734d579a1 | |
tastybento | 44454f5854 | |
tastybento | da590ce319 | |
tastybento | 6599e3de80 | |
tastybento | 3c6e3d1286 | |
tastybento | d77c94c30c | |
tastybento | d240e9c8d8 | |
tastybento | 75412a4674 | |
tastybento | 40e96b9169 | |
tastybento | db2b97d2fc | |
tastybento | 5ad2ba1cd9 | |
tastybento | 6127cdced1 | |
tastybento | 91998b4e24 | |
tastybento | 06ca7a311a | |
tastybento | e4e92b9634 | |
tastybento | 0c4a4ba862 | |
tastybento | 3907cba08f | |
tastybento | 4064c9a241 | |
tastybento | b45c842c2f | |
tastybento | e2a4233f69 | |
tastybento | 977c82015b | |
tastybento | 6db04f872b | |
tastybento | 6fccf80477 | |
tastybento | eef3dcbc46 | |
tastybento | 253e5d7101 | |
tastybento | 1c4be17690 | |
tastybento | dc42f51168 | |
tastybento | 1fb6a8a27c | |
tastybento | 4170616e47 | |
tastybento | 7f532b1257 | |
tastybento | c39bd75837 | |
tastybento | 4810c4c4ad | |
tastybento | cb7c63a520 | |
tastybento | dcc3992762 | |
tastybento | 57164dd846 | |
tastybento | 69017860a0 | |
tastybento | 24c68a0d95 | |
tastybento | 994019836a | |
tastybento | bbafa6d340 | |
tastybento | 19d81c70c6 | |
tastybento | 7d52325196 | |
tastybento | 2d08365afc | |
tastybento | a3a4a70921 | |
tastybento | bb9ed87175 | |
tastybento | 0e833de22a |
|
@ -13,11 +13,11 @@ jobs:
|
|||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
|
||||
- name: Set up JDK 17
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'adopt'
|
||||
java-version: '17'
|
||||
java-version: '21'
|
||||
- name: Cache SonarCloud packages
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
[![Discord](https://img.shields.io/discord/272499714048524288.svg?logo=discord)](https://discord.bentobox.world)
|
||||
[![Build Status](https://ci.codemc.org/buildStatus/icon?job=BentoBoxWorld/BentoBox)](https://ci.codemc.org/job/BentoBoxWorld/job/BentoBox/)
|
||||
[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=BentoBoxWorld_BentoBox&metric=ncloc)](https://sonarcloud.io/dashboard?id=BentoBoxWorld_BentoBox)
|
||||
[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=BentoBoxWorld_BentoBox&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=BentoBoxWorld_BentoBox)
|
||||
[![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=BentoBoxWorld_BentoBox&metric=reliability_rating)](https://sonarcloud.io/dashboard?id=BentoBoxWorld_BentoBox)
|
||||
[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=BentoBoxWorld_BentoBox&metric=security_rating)](https://sonarcloud.io/dashboard?id=BentoBoxWorld_BentoBox)
|
||||
|
@ -112,9 +111,10 @@ repositories {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly 'world.bentobox:bentobox:PUT-VERSION-HERE'
|
||||
compileOnly 'world.bentobox:bentobox:PUT-VERSION-HERE-SNAPSHOT'
|
||||
}
|
||||
```
|
||||
**Note:** Due to a Gradle issue with versions for Maven, you need to use -SNAPSHOT at the end.
|
||||
|
||||
### History
|
||||
|
||||
|
|
61
pom.xml
61
pom.xml
|
@ -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.4-R0.1-SNAPSHOT</spigot.version>
|
||||
<spigot.version>1.20.5-R0.1-SNAPSHOT</spigot.version>
|
||||
<!-- Might differ from the last Spigot release for short periods
|
||||
of time -->
|
||||
<paper.version>1.20.4-R0.1-SNAPSHOT</paper.version>
|
||||
<paper.version>1.20.6-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>2.1.0</build.version>
|
||||
<build.version>2.4.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>
|
||||
|
@ -145,6 +145,10 @@
|
|||
</pluginRepositories>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>jitpack.io</id>
|
||||
<url>https://jitpack.io</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spigot-repo</id>
|
||||
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots</url>
|
||||
|
@ -153,10 +157,6 @@
|
|||
<id>codemc-repo</id>
|
||||
<url>https://repo.codemc.org/repository/maven-public</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>jitpack.io</id>
|
||||
<url>https://jitpack.io</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>placeholderapi-repo</id>
|
||||
<url>https://repo.extendedclip.com/content/repositories/placeholderapi/</url>
|
||||
|
@ -189,6 +189,17 @@
|
|||
<id>MG-Dev Jenkins CI Maven Repository</id>
|
||||
<url>https://ci.mg-dev.eu/plugin/repository/everything</url>
|
||||
</repository>
|
||||
<!-- For MythicMobs -->
|
||||
<repository>
|
||||
<id>nexus</id>
|
||||
<name>Lumine Releases</name>
|
||||
<url>https://mvn.lumine.io/repository/maven-public/</url>
|
||||
</repository>
|
||||
<!-- For Multipaper -->
|
||||
<repository>
|
||||
<id>clojars</id>
|
||||
<url>https://repo.clojars.org/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
|
@ -217,13 +228,19 @@
|
|||
<version>3.11.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- Spigot API -->
|
||||
<!-- Spigot API -->
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>${spigot.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc....</groupId>
|
||||
<artifactId>spigot</artifactId>
|
||||
<version>1.20.6-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc.</groupId>
|
||||
<artifactId>spigot</artifactId>
|
||||
|
@ -297,6 +314,12 @@
|
|||
<version>${myworlds.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.lumine</groupId>
|
||||
<artifactId>Mythic-Dist</artifactId>
|
||||
<version>5.3.5</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- Shaded APIs -->
|
||||
<dependency>
|
||||
<groupId>com.github.TheBusyBiscuit</groupId>
|
||||
|
@ -342,16 +365,23 @@
|
|||
<dependency>
|
||||
<groupId>com.github.Slimefun</groupId>
|
||||
<artifactId>Slimefun4</artifactId>
|
||||
<version>RC-36</version>
|
||||
<version>RC-37</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- ItemsAdder -->
|
||||
<dependency>
|
||||
<groupId>com.github.LoneDev6</groupId>
|
||||
<artifactId>api-itemsadder</artifactId>
|
||||
<version>3.6.1</version>
|
||||
<version>3.6.3-beta-14</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- Multipaper -->
|
||||
<dependency>
|
||||
<groupId>com.github.puregero</groupId>
|
||||
<artifactId>multilib</artifactId>
|
||||
<version>1.1.13</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
@ -474,9 +504,10 @@
|
|||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.3.1-SNAPSHOT</version>
|
||||
<version>3.4.0</version>
|
||||
<configuration>
|
||||
<minimizeJar>true</minimizeJar>
|
||||
<dependencyReducedPomLocation>${project.build.directory}/dependency-reduced-pom.xml</dependencyReducedPomLocation>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>org.bstats</pattern>
|
||||
|
@ -488,9 +519,13 @@
|
|||
</relocation>
|
||||
<relocation>
|
||||
<pattern>io.papermc.lib</pattern>
|
||||
<shadedPattern>world.bentobox.bentobox.paperlib</shadedPattern> <!-- Replace this -->
|
||||
<shadedPattern>world.bentobox.bentobox.paperlib</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
<relocation>
|
||||
<pattern>com.github.puregero.multilib</pattern>
|
||||
<shadedPattern>world.bentobox.bentobox.multilib</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
<artifactSet>
|
||||
<excludes>
|
||||
<exclude>org.apache.maven.shared:*</exclude>
|
||||
|
|
|
@ -11,10 +11,8 @@ import org.bstats.charts.AdvancedPie;
|
|||
import org.bstats.charts.SimpleBarChart;
|
||||
import org.bstats.charts.SimplePie;
|
||||
import org.bstats.charts.SingleLineChart;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import world.bentobox.bentobox.api.addons.GameModeAddon;
|
||||
import world.bentobox.bentobox.api.flags.Flag;
|
||||
|
||||
/**
|
||||
* @author Poslovitch
|
||||
|
@ -59,7 +57,6 @@ public class BStats {
|
|||
registerGameModeAddonsChart();
|
||||
registerHooksChart();
|
||||
registerPlayersPerServerChart();
|
||||
registerFlagsDisplayModeChart();
|
||||
|
||||
// Single Line charts
|
||||
registerIslandsCountChart();
|
||||
|
@ -171,27 +168,6 @@ public class BStats {
|
|||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the "flags display mode" of all the online players.
|
||||
* @since 1.6.0
|
||||
*/
|
||||
private void registerFlagsDisplayModeChart() {
|
||||
metrics.addCustomChart(new AdvancedPie("flagsDisplayMode", () -> {
|
||||
Map<String, Integer> values = new HashMap<>();
|
||||
|
||||
Bukkit.getOnlinePlayers().forEach(player -> {
|
||||
Flag.Mode mode = plugin.getPlayers().getFlagsDisplayMode(player.getUniqueId());
|
||||
if (values.containsKey(mode.name())) {
|
||||
values.put(mode.name(), values.get(mode.name()) + 1);
|
||||
} else {
|
||||
values.put(mode.name(), 1);
|
||||
}
|
||||
});
|
||||
|
||||
return values;
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the enabled addons (except GameModeAddons) of this server as bar chart.
|
||||
* @since 1.17.1
|
||||
|
|
|
@ -25,8 +25,10 @@ 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.MultipaperHook;
|
||||
import world.bentobox.bentobox.hooks.MultiverseCoreHook;
|
||||
import world.bentobox.bentobox.hooks.MyWorldsHook;
|
||||
import world.bentobox.bentobox.hooks.MythicMobsHook;
|
||||
import world.bentobox.bentobox.hooks.SlimefunHook;
|
||||
import world.bentobox.bentobox.hooks.VaultHook;
|
||||
import world.bentobox.bentobox.hooks.placeholders.PlaceholderAPIHook;
|
||||
|
@ -36,6 +38,7 @@ 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.SeedWorldMakerListener;
|
||||
import world.bentobox.bentobox.listeners.StandardSpawnProtectionListener;
|
||||
import world.bentobox.bentobox.listeners.teleports.EntityTeleportListener;
|
||||
import world.bentobox.bentobox.listeners.teleports.PlayerTeleportListener;
|
||||
|
@ -101,6 +104,8 @@ public class BentoBox extends JavaPlugin implements Listener {
|
|||
|
||||
@Override
|
||||
public void onEnable(){
|
||||
setInstance(this);
|
||||
|
||||
if (!ServerCompatibility.getInstance().checkCompatibility().isCanLaunch()) {
|
||||
// The server's most likely incompatible.
|
||||
// Show a warning
|
||||
|
@ -122,7 +127,6 @@ public class BentoBox extends JavaPlugin implements Listener {
|
|||
|
||||
// Save the default config from config.yml
|
||||
saveDefaultConfig();
|
||||
setInstance(this);
|
||||
// Load Flags
|
||||
flagsManager = new FlagsManager(this);
|
||||
|
||||
|
@ -183,8 +187,14 @@ public class BentoBox extends JavaPlugin implements Listener {
|
|||
|
||||
private void completeSetup(long loadTime) {
|
||||
final long enableStart = System.currentTimeMillis();
|
||||
|
||||
hooksManager.registerHook(new MultipaperHook());
|
||||
|
||||
hooksManager.registerHook(new VaultHook());
|
||||
|
||||
// MythicMobs
|
||||
hooksManager.registerHook(new MythicMobsHook());
|
||||
|
||||
hooksManager.registerHook(new PlaceholderAPIHook());
|
||||
// Setup the Placeholders manager
|
||||
placeholdersManager = new PlaceholdersManager(this);
|
||||
|
@ -206,20 +216,6 @@ public class BentoBox extends JavaPlugin implements Listener {
|
|||
return;
|
||||
}
|
||||
|
||||
// Save islands & players data every X minutes
|
||||
Bukkit.getScheduler().runTaskTimer(instance, () -> {
|
||||
if (!playersManager.isSaveTaskRunning()) {
|
||||
playersManager.saveAll(true);
|
||||
} else {
|
||||
getLogger().warning("Tried to start a player data save task while the previous auto save was still running!");
|
||||
}
|
||||
if (!islandsManager.isSaveTaskRunning()) {
|
||||
islandsManager.saveAll(true);
|
||||
} else {
|
||||
getLogger().warning("Tried to start a island data save task while the previous auto save was still running!");
|
||||
}
|
||||
}, getSettings().getDatabaseBackupPeriod() * 20 * 60L, getSettings().getDatabaseBackupPeriod() * 20 * 60L);
|
||||
|
||||
// Make sure all flag listeners are registered.
|
||||
flagsManager.registerListeners();
|
||||
|
||||
|
@ -319,6 +315,8 @@ public class BentoBox extends JavaPlugin implements Listener {
|
|||
manager.registerEvents(islandDeletionManager, this);
|
||||
// Primary Island Listener
|
||||
manager.registerEvents(new PrimaryIslandListener(this), this);
|
||||
// Seed world chunk generator
|
||||
manager.registerEvents(new SeedWorldMakerListener(this), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -429,7 +427,7 @@ public class BentoBox extends JavaPlugin implements Listener {
|
|||
* @return the ranksManager
|
||||
* @deprecated Just use {@code RanksManager.getInstance()}
|
||||
*/
|
||||
@Deprecated(since = "2.0.0")
|
||||
@Deprecated(since = "2.0.0", forRemoval = true)
|
||||
public RanksManager getRanksManager() {
|
||||
return RanksManager.getInstance();
|
||||
}
|
||||
|
|
|
@ -195,6 +195,12 @@ public class Settings implements ConfigObject {
|
|||
@ConfigEntry(path = "island.concurrent-islands")
|
||||
private int islandNumber = 1;
|
||||
|
||||
@ConfigComment("Hide used blueprints.")
|
||||
@ConfigComment("Blueprints can have a maximum use when players have concurrent islands.")
|
||||
@ConfigComment("If this is true, then ones that are used up will not be shown in the island create menu.")
|
||||
@ConfigEntry(path = "island.hide-used-blueprints", since = "2.3.0")
|
||||
private boolean hideUsedBlueprints = false;
|
||||
|
||||
// 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")
|
||||
|
@ -1014,4 +1020,18 @@ public class Settings implements ConfigObject {
|
|||
this.islandNumber = islandNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the hideUsedBlueprints
|
||||
*/
|
||||
public boolean isHideUsedBlueprints() {
|
||||
return hideUsedBlueprints;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param hideUsedBlueprints the hideUsedBlueprints to set
|
||||
*/
|
||||
public void setHideUsedBlueprints(boolean hideUsedBlueprints) {
|
||||
this.hideUsedBlueprints = hideUsedBlueprints;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@ import org.bukkit.configuration.file.FileConfiguration;
|
|||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.event.Listener;
|
||||
|
||||
import com.github.puregero.multilib.MultiLib;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.api.addons.request.AddonRequestHandler;
|
||||
import world.bentobox.bentobox.api.flags.Flag;
|
||||
|
@ -45,6 +47,8 @@ public abstract class Addon {
|
|||
|
||||
protected Addon() {
|
||||
state = State.DISABLED;
|
||||
// If the config is updated, update the config.
|
||||
MultiLib.onString(getPlugin(), "bentobox-config-update", v -> this.reloadConfig());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,6 +8,8 @@ import org.bukkit.generator.ChunkGenerator;
|
|||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import com.github.puregero.multilib.MultiLib;
|
||||
|
||||
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||
import world.bentobox.bentobox.api.configuration.WorldSettings;
|
||||
import world.bentobox.bentobox.util.Util;
|
||||
|
@ -129,7 +131,10 @@ public abstract class GameModeAddon extends Addon {
|
|||
* in-game and need to be saved.
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public abstract void saveWorldSettings();
|
||||
public void saveWorldSettings() {
|
||||
// Inform other servers
|
||||
MultiLib.notify("bentobox-config-update", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines if the game mode uses the latest {@link ChunkGenerator} API or
|
||||
|
|
|
@ -28,7 +28,7 @@ public abstract class Pladdon extends JavaPlugin {
|
|||
String parentFolder = getFile().getParent();
|
||||
if (parentFolder == null || !parentFolder.endsWith(ADDONS_FOLDER)) {
|
||||
// Jar is in the wrong place. Let's move it
|
||||
moveJar();
|
||||
//moveJar();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -520,6 +520,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
|
|||
|
||||
/**
|
||||
* Convenience method to check if a user has a team.
|
||||
* Consider checking the island itself {@link Island#inTeam(UUID)}
|
||||
*
|
||||
* @param world - the world to check
|
||||
* @param user - the User
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
package world.bentobox.bentobox.api.commands.admin;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
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.events.island.IslandEvent;
|
||||
|
@ -16,6 +20,9 @@ import world.bentobox.bentobox.util.Util;
|
|||
|
||||
public class AdminDeleteCommand extends ConfirmableCommand {
|
||||
|
||||
private @Nullable UUID targetUUID;
|
||||
private Island island;
|
||||
|
||||
public AdminDeleteCommand(CompositeCommand parent) {
|
||||
super(parent, "delete");
|
||||
}
|
||||
|
@ -29,56 +36,93 @@ public class AdminDeleteCommand extends ConfirmableCommand {
|
|||
|
||||
@Override
|
||||
public boolean canExecute(User user, String label, List<String> args) {
|
||||
if (args.size() != 1) {
|
||||
showHelp(this, user);
|
||||
if (args.isEmpty()) {
|
||||
this.showHelp(this, user);
|
||||
return false;
|
||||
}
|
||||
// Get target
|
||||
UUID targetUUID = Util.getUUID(args.get(0));
|
||||
// Convert name to a UUID
|
||||
targetUUID = Util.getUUID(args.get(0));
|
||||
if (targetUUID == null) {
|
||||
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
|
||||
return false;
|
||||
}
|
||||
Island island = getIslands().getIsland(getWorld(), user);
|
||||
if (island == null) {
|
||||
// Check island exists
|
||||
if (!getIslands().hasIsland(getWorld(), targetUUID) && !getIslands().inTeam(getWorld(), targetUUID)) {
|
||||
user.sendMessage("general.errors.player-has-no-island");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (args.size() == 1) {
|
||||
// Check if player is owner of any islands
|
||||
if (getIslands().getIslands(getWorld(), targetUUID).stream().filter(Island::hasTeam)
|
||||
.anyMatch(is -> targetUUID.equals(is.getOwner()))) {
|
||||
user.sendMessage("commands.admin.delete.cannot-delete-owner");
|
||||
return false;
|
||||
}
|
||||
// This is a delete everything request
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get the island
|
||||
User target = User.getInstance(targetUUID);
|
||||
// They named the island to go to
|
||||
Map<String, IslandInfo> names = getNameIslandMap(target);
|
||||
final String name = String.join(" ", args.subList(1, args.size()));
|
||||
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);
|
||||
island = info.island;
|
||||
}
|
||||
|
||||
// Team members should be kicked before deleting otherwise the whole team will become weird
|
||||
if (getIslands().inTeam(getWorld(), targetUUID) && user.getUniqueId().equals(island.getOwner())) {
|
||||
if (island.hasTeam() && targetUUID.equals(island.getOwner())) {
|
||||
user.sendMessage("commands.admin.delete.cannot-delete-owner");
|
||||
return false;
|
||||
}
|
||||
if (names.size() == 1) {
|
||||
// This is the only island they have so, no need to specify it
|
||||
island = null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(User user, String label, List<String> args) {
|
||||
// Get target
|
||||
UUID targetUUID = getPlayers().getUUID(args.get(0));
|
||||
// Confirm
|
||||
askConfirmation(user, () -> deletePlayer(user, targetUUID));
|
||||
if (island == null) {
|
||||
// Delete the player entirely
|
||||
askConfirmation(user, () -> deletePlayer(user));
|
||||
} else {
|
||||
// Just delete the player's island
|
||||
askConfirmation(user, () -> deleteIsland(user, island));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void deletePlayer(User user, UUID targetUUID) {
|
||||
private void deleteIsland(User user, Island oldIsland) {
|
||||
// Fire island preclear event
|
||||
IslandEvent.builder().involvedPlayer(user.getUniqueId()).reason(Reason.PRECLEAR).island(oldIsland)
|
||||
.oldIsland(oldIsland).location(oldIsland.getCenter()).build();
|
||||
user.sendMessage("commands.admin.delete.deleted-island", TextVariables.XYZ,
|
||||
Util.xyz(oldIsland.getCenter().toVector()));
|
||||
getIslands().deleteIsland(oldIsland, true, targetUUID);
|
||||
|
||||
}
|
||||
|
||||
private void deletePlayer(User user) {
|
||||
// Delete player and island
|
||||
for (Island oldIsland : getIslands().getIslands(getWorld(), targetUUID)) {
|
||||
// Fire island preclear event
|
||||
IslandEvent.builder()
|
||||
.involvedPlayer(user.getUniqueId())
|
||||
.reason(Reason.PRECLEAR)
|
||||
.island(oldIsland)
|
||||
.oldIsland(oldIsland)
|
||||
.location(oldIsland.getCenter())
|
||||
.build();
|
||||
user.sendMessage("commands.admin.delete.deleted-island", TextVariables.XYZ, Util.xyz(oldIsland.getCenter().toVector()));
|
||||
getIslands().deleteIsland(oldIsland, true, targetUUID);
|
||||
deleteIsland(user, oldIsland);
|
||||
}
|
||||
// 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)
|
||||
// Remove target from any and all islands in the world
|
||||
getIslands().removePlayer(getWorld(), targetUUID);
|
||||
if (target.isPlayer() && target.isOnline()) {
|
||||
cleanUp(target);
|
||||
|
@ -120,6 +164,31 @@ public class AdminDeleteCommand extends ConfirmableCommand {
|
|||
Util.runCommands(target, target.getName(), getIWM().getOnLeaveCommands(getWorld()), "leave");
|
||||
}
|
||||
|
||||
private record IslandInfo(Island island, boolean islandName) {
|
||||
}
|
||||
|
||||
private Map<String, IslandInfo> getNameIslandMap(User target) {
|
||||
Map<String, IslandInfo> islandMap = new HashMap<>();
|
||||
int index = 0;
|
||||
for (Island island : getIslands().getIslands(getWorld(), target.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 = target.getTranslation("protection.flags.ENTER_EXIT_MESSAGES.island", TextVariables.NAME,
|
||||
target.getName(), TextVariables.DISPLAY_NAME, target.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;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
|
||||
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
|
||||
|
@ -127,7 +196,16 @@ public class AdminDeleteCommand extends ConfirmableCommand {
|
|||
// Don't show every player on the server. Require at least the first letter
|
||||
return Optional.empty();
|
||||
}
|
||||
List<String> options = new ArrayList<>(Util.getOnlinePlayerList(user));
|
||||
return Optional.of(Util.tabLimit(options, lastArg));
|
||||
if (args.size() == 2) {
|
||||
return Optional.of(Util.tabLimit(new ArrayList<>(Util.getOnlinePlayerList(user)), lastArg));
|
||||
}
|
||||
if (args.size() == 3) {
|
||||
UUID target = Util.getUUID(args.get(1));
|
||||
return target == null ? Optional.empty()
|
||||
: Optional.of(Util.tabLimit(new ArrayList<>(getNameIslandMap(User.getInstance(target)).keySet()),
|
||||
lastArg));
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
package world.bentobox.bentobox.api.commands.admin;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
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;
|
||||
|
||||
public class AdminEmptyTrashCommand extends ConfirmableCommand {
|
||||
|
||||
/**
|
||||
* Clear trash for player, or all unowned islands in trash
|
||||
* @param parent - admin command
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public AdminEmptyTrashCommand(CompositeCommand parent) {
|
||||
super(parent, "emptytrash");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
setPermission("admin.trash");
|
||||
setOnlyPlayer(false);
|
||||
setParametersHelp("commands.admin.emptytrash.parameters");
|
||||
setDescription("commands.admin.emptytrash.description");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(User user, String label, List<String> args) {
|
||||
if (args.size() > 1) {
|
||||
// Show help
|
||||
showHelp(this, user);
|
||||
return false;
|
||||
}
|
||||
// Get target player
|
||||
UUID targetUUID = args.isEmpty() ? null : getPlayers().getUUID(args.get(0));
|
||||
if (!args.isEmpty() && targetUUID == null) {
|
||||
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
|
||||
return false;
|
||||
}
|
||||
// Remove trash for this player
|
||||
final List<Island> islands = getIslands().getQuarantinedIslandByUser(getWorld(), targetUUID);
|
||||
if (islands.isEmpty()) {
|
||||
if (args.isEmpty()) {
|
||||
user.sendMessage("commands.admin.trash.no-unowned-in-trash");
|
||||
} else {
|
||||
user.sendMessage("commands.admin.trash.no-islands-in-trash");
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
this.askConfirmation(user, () -> {
|
||||
getIslands().deleteQuarantinedIslandByUser(getWorld(), targetUUID);
|
||||
user.sendMessage("commands.admin.emptytrash.success");
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -49,9 +49,6 @@ public class AdminInfoCommand extends CompositeCommand {
|
|||
Island island = getIslands().getIsland(getWorld(), targetUUID);
|
||||
if (island != null) {
|
||||
new IslandInfo(island).showAdminInfo(user, getAddon());
|
||||
if (!getIslands().getQuarantinedIslandByUser(getWorld(), targetUUID).isEmpty()) {
|
||||
user.sendMessage("commands.admin.info.islands-in-trash");
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
user.sendMessage("general.errors.player-has-no-island");
|
||||
|
|
|
@ -16,7 +16,6 @@ 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.flags.Flag;
|
||||
import world.bentobox.bentobox.api.flags.Flag.Mode;
|
||||
import world.bentobox.bentobox.api.flags.Flag.Type;
|
||||
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||
import world.bentobox.bentobox.api.panels.builders.TabbedPanelBuilder;
|
||||
|
@ -206,11 +205,9 @@ public class AdminSettingsCommand extends CompositeCommand {
|
|||
switch (f.getType()) {
|
||||
case PROTECTION -> {
|
||||
island.setFlag(f, rank);
|
||||
getIslands().save(island);
|
||||
}
|
||||
case SETTING -> {
|
||||
island.setSettingsFlag(f, activeState);
|
||||
getIslands().save(island);
|
||||
}
|
||||
case WORLD_SETTING -> f.setSetting(getWorld(), activeState);
|
||||
default -> {
|
||||
|
@ -226,12 +223,11 @@ public class AdminSettingsCommand extends CompositeCommand {
|
|||
user.sendMessage("general.errors.use-in-game");
|
||||
return false;
|
||||
}
|
||||
getPlayers().setFlagsDisplayMode(user.getUniqueId(), Mode.EXPERT);
|
||||
if (args.isEmpty()) {
|
||||
new TabbedPanelBuilder()
|
||||
.user(user)
|
||||
.world(getWorld())
|
||||
.tab(1, new SettingsTab(getWorld(), user, Flag.Type.WORLD_SETTING))
|
||||
.tab(1, new SettingsTab(getWorld(), user, Flag.Type.WORLD_SETTING, Flag.Mode.EXPERT))
|
||||
.tab(2, new WorldDefaultSettingsTab(getWorld(), user))
|
||||
.startingSlot(1)
|
||||
.size(54)
|
||||
|
@ -242,8 +238,8 @@ public class AdminSettingsCommand extends CompositeCommand {
|
|||
new TabbedPanelBuilder()
|
||||
.user(user)
|
||||
.world(island.getWorld())
|
||||
.island(island).tab(1, new SettingsTab(user, Flag.Type.PROTECTION))
|
||||
.tab(2, new SettingsTab(user, Flag.Type.SETTING))
|
||||
.island(island).tab(1, new SettingsTab(getWorld(), user, Flag.Type.PROTECTION, Flag.Mode.EXPERT))
|
||||
.tab(2, new SettingsTab(getWorld(), user, Flag.Type.SETTING, Flag.Mode.EXPERT))
|
||||
.startingSlot(1)
|
||||
.size(54)
|
||||
.build().openPanel();
|
||||
|
|
|
@ -1,87 +0,0 @@
|
|||
package world.bentobox.bentobox.api.commands.admin;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.commons.lang.math.NumberUtils;
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
|
||||
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 AdminSwitchtoCommand extends ConfirmableCommand {
|
||||
|
||||
private UUID targetUUID;
|
||||
private @NonNull List<Island> islands;
|
||||
|
||||
/**
|
||||
* Switch player's island to the numbered one in trash
|
||||
* @param parent - admin command
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public AdminSwitchtoCommand(CompositeCommand parent) {
|
||||
super(parent, "switchto");
|
||||
islands = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
setPermission("admin.switchto");
|
||||
setOnlyPlayer(false);
|
||||
setParametersHelp("commands.admin.switchto.parameters");
|
||||
setDescription("commands.admin.switchto.description");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canExecute(User user, String label, List<String> args) {
|
||||
if (args.size() != 2) {
|
||||
// Show help
|
||||
showHelp(this, user);
|
||||
return false;
|
||||
}
|
||||
// Get target player
|
||||
targetUUID = Util.getUUID(args.get(0));
|
||||
if (targetUUID == null) {
|
||||
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
|
||||
return false;
|
||||
}
|
||||
// Check island number
|
||||
islands = getIslands().getQuarantinedIslandByUser(getWorld(), targetUUID);
|
||||
if (islands.isEmpty()) {
|
||||
user.sendMessage("commands.admin.trash.no-islands-in-trash");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(User user, String label, List<String> args) {
|
||||
if (NumberUtils.isDigits(args.get(1))) {
|
||||
try {
|
||||
int n = Integer.parseInt(args.get(1));
|
||||
if (n < 1 || n > islands.size()) {
|
||||
user.sendMessage("commands.admin.switchto.out-of-range", TextVariables.NUMBER, String.valueOf(islands.size()), TextVariables.LABEL, getTopLabel());
|
||||
return false;
|
||||
}
|
||||
this.askConfirmation(user, () -> {
|
||||
if (getIslands().switchIsland(getWorld(), targetUUID, islands.get(n -1))) {
|
||||
user.sendMessage("commands.admin.switchto.success");
|
||||
} else {
|
||||
user.sendMessage("commands.admin.switchto.cannot-switch");
|
||||
}
|
||||
});
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
showHelp(this, user);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,7 +1,10 @@
|
|||
package world.bentobox.bentobox.api.commands.admin;
|
||||
|
||||
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 java.util.UUID;
|
||||
|
||||
|
@ -17,11 +20,17 @@ import world.bentobox.bentobox.database.objects.Island;
|
|||
import world.bentobox.bentobox.util.Util;
|
||||
import world.bentobox.bentobox.util.teleport.SafeSpotTeleport;
|
||||
|
||||
/**
|
||||
* Enables admins to teleport to a player's island, nether or end islands,
|
||||
*
|
||||
* For example /acid tp tastybento [island name] would teleport to tastybento's [named] island
|
||||
*
|
||||
*/
|
||||
public class AdminTeleportCommand extends CompositeCommand {
|
||||
|
||||
private static final String NOT_SAFE = "general.errors.no-safe-location-found";
|
||||
private @Nullable UUID targetUUID;
|
||||
private @Nullable User userToTeleport;
|
||||
private Location warpSpot;
|
||||
|
||||
/**
|
||||
* @param parent - parent command
|
||||
|
@ -41,12 +50,12 @@ public class AdminTeleportCommand extends CompositeCommand {
|
|||
|
||||
@Override
|
||||
public boolean canExecute(User user, String label, List<String> args) {
|
||||
if (args.size() != 1 && args.size() != 2) {
|
||||
if (args.isEmpty()) {
|
||||
this.showHelp(this, user);
|
||||
return false;
|
||||
}
|
||||
// Check for console or not
|
||||
if (!user.isPlayer() && args.size() != 2) {
|
||||
if (!user.isPlayer()) {
|
||||
user.sendMessage("general.errors.use-in-game");
|
||||
return false;
|
||||
}
|
||||
|
@ -62,25 +71,6 @@ public class AdminTeleportCommand extends CompositeCommand {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (args.size() == 2) {
|
||||
// We are trying to teleport another player
|
||||
UUID playerToTeleportUUID = Util.getUUID(args.get(1));
|
||||
if (playerToTeleportUUID == null) {
|
||||
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(1));
|
||||
return false;
|
||||
} else {
|
||||
userToTeleport = User.getInstance(playerToTeleportUUID);
|
||||
if (!userToTeleport.isOnline()) {
|
||||
user.sendMessage("general.errors.offline-player");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(User user, String label, List<String> args) {
|
||||
World world = getWorld();
|
||||
if (getLabel().equals("tpnether")) {
|
||||
world = getPlugin().getIWM().getNetherWorld(getWorld());
|
||||
|
@ -91,19 +81,46 @@ public class AdminTeleportCommand extends CompositeCommand {
|
|||
user.sendMessage(NOT_SAFE);
|
||||
return false;
|
||||
}
|
||||
Location warpSpot = getSpot(world);
|
||||
// Get default location if there are no arguments
|
||||
warpSpot = getSpot(world);
|
||||
if (warpSpot == null) {
|
||||
user.sendMessage(NOT_SAFE);
|
||||
return false;
|
||||
}
|
||||
if (args.size() == 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// They named the island to go to
|
||||
Map<String, IslandInfo> names = getNameIslandMap(User.getInstance(targetUUID));
|
||||
final String name = String.join(" ", args.subList(1, args.size()));
|
||||
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 if (names.size() > 1) {
|
||||
IslandInfo info = names.get(name);
|
||||
Island island = info.island;
|
||||
warpSpot = island.getSpawnPoint(world.getEnvironment()) != null
|
||||
? island.getSpawnPoint(world.getEnvironment())
|
||||
: island.getProtectionCenter().toVector().toLocation(world);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(User user, String label, List<String> args) {
|
||||
Objects.requireNonNull(warpSpot);
|
||||
// Otherwise, ask the admin to go to a safe spot
|
||||
String failureMessage = user.getTranslation("commands.admin.tp.manual", "[location]", warpSpot.getBlockX() + " " + warpSpot.getBlockY() + " "
|
||||
+ warpSpot.getBlockZ());
|
||||
// Set the player
|
||||
Player player = args.size() == 2 ? userToTeleport.getPlayer() : user.getPlayer();
|
||||
Player player = args.size() == 2 ? user.getPlayer() : user.getPlayer();
|
||||
if (args.size() == 2) {
|
||||
failureMessage = userToTeleport.getTranslation(NOT_SAFE);
|
||||
failureMessage = user.getTranslation(NOT_SAFE);
|
||||
}
|
||||
|
||||
// Teleport
|
||||
|
@ -124,6 +141,31 @@ public class AdminTeleportCommand extends CompositeCommand {
|
|||
return island.getSpawnPoint(world.getEnvironment()) != null ? island.getSpawnPoint(world.getEnvironment()) : island.getProtectionCenter().toVector().toLocation(world);
|
||||
}
|
||||
|
||||
private record IslandInfo(Island island, boolean islandName) {
|
||||
}
|
||||
|
||||
private Map<String, IslandInfo> getNameIslandMap(User target) {
|
||||
Map<String, IslandInfo> islandMap = new HashMap<>();
|
||||
int index = 0;
|
||||
for (Island island : getIslands().getIslands(getWorld(), target.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 = target.getTranslation("protection.flags.ENTER_EXIT_MESSAGES.island", TextVariables.NAME,
|
||||
target.getName(), TextVariables.DISPLAY_NAME, target.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;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
|
||||
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
|
||||
|
@ -131,8 +173,17 @@ public class AdminTeleportCommand extends CompositeCommand {
|
|||
// Don't show every player on the server. Require at least the first letter
|
||||
return Optional.empty();
|
||||
}
|
||||
List<String> options = new ArrayList<>(Util.getOnlinePlayerList(user));
|
||||
return Optional.of(Util.tabLimit(options, lastArg));
|
||||
if (args.size() == 2) {
|
||||
return Optional.of(Util.tabLimit(new ArrayList<>(Util.getOnlinePlayerList(user)), lastArg));
|
||||
}
|
||||
|
||||
if (args.size() == 3) {
|
||||
UUID target = Util.getUUID(args.get(1));
|
||||
return target == null ? Optional.empty()
|
||||
: Optional
|
||||
.of(Util.tabLimit(new ArrayList<>(getNameIslandMap(User.getInstance(target)).keySet()), lastArg));
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,199 @@
|
|||
package world.bentobox.bentobox.api.commands.admin;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
import world.bentobox.bentobox.util.Util;
|
||||
import world.bentobox.bentobox.util.teleport.SafeSpotTeleport;
|
||||
|
||||
/**
|
||||
* Enables admins to teleport to a player's island, nether or end islands, or to teleport another player
|
||||
* to a player's island
|
||||
*
|
||||
* For example /acid tp tastybento boxmanager would teleport BoxManager to tastybento's overwold island
|
||||
*
|
||||
* If the user has multiple islands, then the format is:
|
||||
* [admin_command] [user with island] [island to go to]
|
||||
*/
|
||||
public class AdminTeleportUserCommand extends CompositeCommand {
|
||||
|
||||
private static final String NOT_SAFE = "general.errors.no-safe-location-found";
|
||||
private @Nullable UUID targetUUID;
|
||||
private @Nullable User userToTeleport;
|
||||
|
||||
/**
|
||||
* @param parent - parent command
|
||||
* @param tpCommand - should be "tp", "tpnether" or "tpend"
|
||||
*/
|
||||
public AdminTeleportUserCommand(CompositeCommand parent, String tpCommand) {
|
||||
super(parent, tpCommand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
// Permission
|
||||
setPermission("admin.tp");
|
||||
setParametersHelp("commands.admin.tp.parameters");
|
||||
setDescription("commands.admin.tp.description");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canExecute(User user, String label, List<String> args) {
|
||||
if (args.isEmpty() || args.size() > 3) {
|
||||
this.showHelp(this, user);
|
||||
return false;
|
||||
}
|
||||
// Check for console or not
|
||||
if (!user.isPlayer() && args.size() == 1) {
|
||||
user.sendMessage("general.errors.use-in-game");
|
||||
return false;
|
||||
}
|
||||
// Convert name to a UUID
|
||||
targetUUID = Util.getUUID(args.get(0));
|
||||
if (targetUUID == null) {
|
||||
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
|
||||
return false;
|
||||
}
|
||||
// Check island exists
|
||||
if (!getIslands().hasIsland(getWorld(), targetUUID) && !getIslands().inTeam(getWorld(), targetUUID)) {
|
||||
user.sendMessage("general.errors.player-has-no-island");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (args.size() == 2) {
|
||||
// We are trying to teleport another player
|
||||
UUID playerToTeleportUUID = Util.getUUID(args.get(1));
|
||||
if (playerToTeleportUUID == null) {
|
||||
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(1));
|
||||
return false;
|
||||
} else {
|
||||
userToTeleport = User.getInstance(playerToTeleportUUID);
|
||||
if (!userToTeleport.isOnline()) {
|
||||
user.sendMessage("general.errors.offline-player");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(User user, String label, List<String> args) {
|
||||
World world = getWorld();
|
||||
if (getLabel().equals("tpnether")) {
|
||||
world = getPlugin().getIWM().getNetherWorld(getWorld());
|
||||
} else if (getLabel().equals("tpend")) {
|
||||
world = getPlugin().getIWM().getEndWorld(getWorld());
|
||||
}
|
||||
if (world == null) {
|
||||
user.sendMessage(NOT_SAFE);
|
||||
return false;
|
||||
}
|
||||
// Get default location if there are no arguments
|
||||
Location warpSpot = getSpot(world);
|
||||
if (warpSpot == null) {
|
||||
user.sendMessage(NOT_SAFE);
|
||||
return false;
|
||||
}
|
||||
// See if there is a quoted island name
|
||||
if (args.size() == 2) {
|
||||
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);
|
||||
Island island = info.island;
|
||||
warpSpot = island.getSpawnPoint(world.getEnvironment()) != null
|
||||
? island.getSpawnPoint(world.getEnvironment())
|
||||
: island.getProtectionCenter().toVector().toLocation(world);
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, ask the admin to go to a safe spot
|
||||
String failureMessage = user.getTranslation("commands.admin.tp.manual", "[location]", warpSpot.getBlockX() + " " + warpSpot.getBlockY() + " "
|
||||
+ warpSpot.getBlockZ());
|
||||
// Set the player
|
||||
Player player = args.size() == 2 ? userToTeleport.getPlayer() : user.getPlayer();
|
||||
if (args.size() == 2) {
|
||||
failureMessage = userToTeleport.getTranslation(NOT_SAFE);
|
||||
}
|
||||
|
||||
// Teleport
|
||||
new SafeSpotTeleport.Builder(getPlugin())
|
||||
.entity(player)
|
||||
.location(warpSpot)
|
||||
.failureMessage(failureMessage)
|
||||
.thenRun(() -> user.sendMessage("general.success"))
|
||||
.build();
|
||||
return true;
|
||||
}
|
||||
|
||||
private Location getSpot(World world) {
|
||||
Island island = getIslands().getIsland(world, targetUUID);
|
||||
if (island == null) {
|
||||
return null;
|
||||
}
|
||||
return island.getSpawnPoint(world.getEnvironment()) != null ? island.getSpawnPoint(world.getEnvironment()) : island.getProtectionCenter().toVector().toLocation(world);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
|
||||
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
|
||||
if (args.isEmpty()) {
|
||||
// Don't show every player on the server. Require at least the first letter
|
||||
return Optional.empty();
|
||||
}
|
||||
if (args.size() == 1) {
|
||||
return Optional.of(Util.tabLimit(new ArrayList<>(Util.getOnlinePlayerList(user)), lastArg));
|
||||
}
|
||||
if (args.size() == 2) {
|
||||
return Optional.of(Util.tabLimit(new ArrayList<>(getNameIslandMap(user).keySet()), lastArg));
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
package world.bentobox.bentobox.api.commands.admin;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
import world.bentobox.bentobox.util.IslandInfo;
|
||||
|
||||
public class AdminTrashCommand extends CompositeCommand {
|
||||
|
||||
/**
|
||||
* A command for viewing islands in the database trash
|
||||
* @param parent - admin command
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public AdminTrashCommand(CompositeCommand parent) {
|
||||
super(parent, "trash");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
setPermission("admin.trash");
|
||||
setOnlyPlayer(false);
|
||||
setParametersHelp("commands.admin.trash.parameters");
|
||||
setDescription("commands.admin.trash.description");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(User user, String label, List<String> args) {
|
||||
if (args.size() > 1) {
|
||||
// Show help
|
||||
showHelp(this, user);
|
||||
return false;
|
||||
}
|
||||
// Get target player
|
||||
UUID targetUUID = args.isEmpty() ? null : getPlayers().getUUID(args.get(0));
|
||||
if (!args.isEmpty() && targetUUID == null) {
|
||||
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
|
||||
return false;
|
||||
}
|
||||
// Show trash can info for this player
|
||||
List<Island> islands = getIslands().getQuarantinedIslandByUser(getWorld(), targetUUID);
|
||||
if (islands.isEmpty()) {
|
||||
if (args.isEmpty()) {
|
||||
user.sendMessage("commands.admin.trash.no-unowned-in-trash");
|
||||
} else {
|
||||
user.sendMessage("commands.admin.trash.no-islands-in-trash");
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
if (targetUUID == null) {
|
||||
showTrash(user, islands);
|
||||
} else {
|
||||
getIslands().getQuarantineCache().values().forEach(v -> showTrash(user, v));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void showTrash(User user, List<Island> islands) {
|
||||
user.sendMessage("commands.admin.trash.title");
|
||||
for (int i = 0; i < islands.size(); i++) {
|
||||
user.sendMessage("commands.admin.trash.count", TextVariables.NUMBER, String.valueOf(i+1));
|
||||
new IslandInfo(islands.get(i)).showInfo(user);
|
||||
}
|
||||
user.sendMessage("commands.admin.trash.use-switch", TextVariables.LABEL, getTopLabel());
|
||||
user.sendMessage("commands.admin.trash.use-emptytrash", TextVariables.LABEL, getTopLabel());
|
||||
|
||||
}
|
||||
}
|
|
@ -116,7 +116,6 @@ public class AdminUnregisterCommand extends ConfirmableCommand {
|
|||
targetIsland.getMembers().clear();
|
||||
targetIsland.log(new LogEntry.Builder("UNREGISTER").data("player", targetUUID.toString())
|
||||
.data("admin", user.getUniqueId().toString()).build());
|
||||
getIslands().save(targetIsland);
|
||||
user.sendMessage("commands.admin.unregister.unregistered-island", TextVariables.XYZ, Util.xyz(targetIsland.getCenter().toVector()),
|
||||
TextVariables.NAME, getPlayers().getName(targetUUID));
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ import world.bentobox.bentobox.api.commands.admin.resets.AdminResetsCommand;
|
|||
import world.bentobox.bentobox.api.commands.admin.team.AdminTeamAddCommand;
|
||||
import world.bentobox.bentobox.api.commands.admin.team.AdminTeamCommand;
|
||||
import world.bentobox.bentobox.api.commands.admin.team.AdminTeamDisbandCommand;
|
||||
import world.bentobox.bentobox.api.commands.admin.team.AdminTeamFixCommand;
|
||||
import world.bentobox.bentobox.api.commands.admin.team.AdminTeamKickCommand;
|
||||
import world.bentobox.bentobox.api.commands.admin.team.AdminTeamSetownerCommand;
|
||||
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||
|
@ -63,7 +62,6 @@ public abstract class DefaultAdminCommand extends CompositeCommand {
|
|||
new AdminTeamKickCommand(this);
|
||||
new AdminTeamDisbandCommand(this);
|
||||
new AdminTeamSetownerCommand(this);
|
||||
new AdminTeamFixCommand(this);
|
||||
// Blueprints
|
||||
new AdminBlueprintCommand(this);
|
||||
// Register/unregister islands
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.bukkit.Particle;
|
|||
|
||||
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
|
||||
import world.bentobox.bentobox.api.commands.admin.range.AdminRangeDisplayCommand;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.bentobox.blueprints.BlueprintClipboard;
|
||||
import world.bentobox.bentobox.managers.BlueprintsManager;
|
||||
|
@ -23,7 +24,6 @@ public class AdminBlueprintCommand extends ConfirmableCommand {
|
|||
|
||||
// Map containing selection cuboid display tasks
|
||||
private Map<User, Integer> displayClipboards;
|
||||
private static final Particle PARTICLE = Particle.REDSTONE;
|
||||
private static final Particle.DustOptions PARTICLE_DUST_OPTIONS = new Particle.DustOptions(Color.RED, 1.0F);
|
||||
|
||||
public AdminBlueprintCommand(CompositeCommand parent) {
|
||||
|
@ -99,26 +99,38 @@ public class AdminBlueprintCommand extends ConfirmableCommand {
|
|||
|
||||
// Drawing x-axes
|
||||
for (int x = minX; x <= maxX; x++) {
|
||||
user.spawnParticle(PARTICLE, PARTICLE_DUST_OPTIONS, x + 0.5, minY + 0.5, minZ + 0.5);
|
||||
user.spawnParticle(PARTICLE, PARTICLE_DUST_OPTIONS, x + 0.5, maxY + 0.5, minZ + 0.5);
|
||||
user.spawnParticle(PARTICLE, PARTICLE_DUST_OPTIONS, x + 0.5, minY + 0.5, maxZ + 0.5);
|
||||
user.spawnParticle(PARTICLE, PARTICLE_DUST_OPTIONS, x + 0.5, maxY + 0.5, maxZ + 0.5);
|
||||
user.spawnParticle(AdminRangeDisplayCommand.PARTICLE, PARTICLE_DUST_OPTIONS, x + 0.5, minY + 0.5,
|
||||
minZ + 0.5);
|
||||
user.spawnParticle(AdminRangeDisplayCommand.PARTICLE, PARTICLE_DUST_OPTIONS, x + 0.5, maxY + 0.5,
|
||||
minZ + 0.5);
|
||||
user.spawnParticle(AdminRangeDisplayCommand.PARTICLE, PARTICLE_DUST_OPTIONS, x + 0.5, minY + 0.5,
|
||||
maxZ + 0.5);
|
||||
user.spawnParticle(AdminRangeDisplayCommand.PARTICLE, PARTICLE_DUST_OPTIONS, x + 0.5, maxY + 0.5,
|
||||
maxZ + 0.5);
|
||||
}
|
||||
|
||||
// Drawing y-axes
|
||||
for (int y = minY; y <= maxY; y++) {
|
||||
user.spawnParticle(PARTICLE, PARTICLE_DUST_OPTIONS, minX + 0.5, y + 0.5, minZ + 0.5);
|
||||
user.spawnParticle(PARTICLE, PARTICLE_DUST_OPTIONS, maxX + 0.5, y + 0.5, minZ + 0.5);
|
||||
user.spawnParticle(PARTICLE, PARTICLE_DUST_OPTIONS, minX + 0.5, y + 0.5, maxZ + 0.5);
|
||||
user.spawnParticle(PARTICLE, PARTICLE_DUST_OPTIONS, maxX + 0.5, y + 0.5, maxZ + 0.5);
|
||||
user.spawnParticle(AdminRangeDisplayCommand.PARTICLE, PARTICLE_DUST_OPTIONS, minX + 0.5, y + 0.5,
|
||||
minZ + 0.5);
|
||||
user.spawnParticle(AdminRangeDisplayCommand.PARTICLE, PARTICLE_DUST_OPTIONS, maxX + 0.5, y + 0.5,
|
||||
minZ + 0.5);
|
||||
user.spawnParticle(AdminRangeDisplayCommand.PARTICLE, PARTICLE_DUST_OPTIONS, minX + 0.5, y + 0.5,
|
||||
maxZ + 0.5);
|
||||
user.spawnParticle(AdminRangeDisplayCommand.PARTICLE, PARTICLE_DUST_OPTIONS, maxX + 0.5, y + 0.5,
|
||||
maxZ + 0.5);
|
||||
}
|
||||
|
||||
// Drawing z-axes
|
||||
for (int z = minZ; z <= maxZ; z++) {
|
||||
user.spawnParticle(PARTICLE, PARTICLE_DUST_OPTIONS, minX + 0.5, minY + 0.5, z + 0.5);
|
||||
user.spawnParticle(PARTICLE, PARTICLE_DUST_OPTIONS, maxX + 0.5, minY + 0.5, z + 0.5);
|
||||
user.spawnParticle(PARTICLE, PARTICLE_DUST_OPTIONS, minX + 0.5, maxY + 0.5, z + 0.5);
|
||||
user.spawnParticle(PARTICLE, PARTICLE_DUST_OPTIONS, maxX + 0.5, maxY + 0.5, z + 0.5);
|
||||
user.spawnParticle(AdminRangeDisplayCommand.PARTICLE, PARTICLE_DUST_OPTIONS, minX + 0.5, minY + 0.5,
|
||||
z + 0.5);
|
||||
user.spawnParticle(AdminRangeDisplayCommand.PARTICLE, PARTICLE_DUST_OPTIONS, maxX + 0.5, minY + 0.5,
|
||||
z + 0.5);
|
||||
user.spawnParticle(AdminRangeDisplayCommand.PARTICLE, PARTICLE_DUST_OPTIONS, minX + 0.5, maxY + 0.5,
|
||||
z + 0.5);
|
||||
user.spawnParticle(AdminRangeDisplayCommand.PARTICLE, PARTICLE_DUST_OPTIONS, maxX + 0.5, maxY + 0.5,
|
||||
z + 0.5);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -40,7 +40,6 @@ public class NamePrompt extends StringPrompt {
|
|||
@Override
|
||||
public Prompt acceptInput(@NonNull ConversationContext context, String input) {
|
||||
if (island.renameHome(oldName, input)) {
|
||||
plugin.getIslands().save(island);
|
||||
Bukkit.getScheduler().runTask(plugin, () -> user.sendMessage("general.success"));
|
||||
} else {
|
||||
Bukkit.getScheduler().runTask(plugin, () -> user.sendMessage("commands.island.renamehome.already-exists"));
|
||||
|
|
|
@ -5,7 +5,6 @@ import java.util.HashSet;
|
|||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.event.EventHandler;
|
||||
|
@ -18,6 +17,7 @@ import world.bentobox.bentobox.api.events.island.IslandDeletedEvent;
|
|||
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 AdminPurgeCommand extends CompositeCommand implements Listener {
|
||||
|
||||
|
@ -82,7 +82,7 @@ public class AdminPurgeCommand extends CompositeCommand implements Listener {
|
|||
user.sendMessage("commands.admin.purge.confirm", TextVariables.LABEL, this.getTopLabel());
|
||||
return false;
|
||||
}
|
||||
} catch(Exception e) {
|
||||
} catch (NumberFormatException e) {
|
||||
user.sendMessage("commands.admin.purge.number-error");
|
||||
return false;
|
||||
}
|
||||
|
@ -120,29 +120,42 @@ public class AdminPurgeCommand extends CompositeCommand implements Listener {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a set of islands that are older than the parameter in days
|
||||
* @param days days
|
||||
* @return set of islands
|
||||
*/
|
||||
Set<String> getOldIslands(int days) {
|
||||
long currentTimeMillis = System.currentTimeMillis();
|
||||
double daysInMilliseconds = days * 1000 * 3600 * 24;
|
||||
Set<String> oldIslands = new HashSet<>();
|
||||
|
||||
// Process islands in one pass, logging and adding to the set if applicable
|
||||
getPlugin().getIslands().getIslands().stream()
|
||||
.filter(i -> !i.isSpawn())
|
||||
.filter(i -> !i.getPurgeProtected())
|
||||
.filter(i -> i.getWorld().equals(this.getWorld()))
|
||||
.filter(Island::isOwned)
|
||||
.filter(i -> i.getMembers().size() == 1)
|
||||
.filter(i -> ((double)(System.currentTimeMillis() - Bukkit.getOfflinePlayer(i.getOwner()).getLastPlayed()) / 1000 / 3600 / 24) > days)
|
||||
.forEach(i -> {
|
||||
Date date = new Date(Bukkit.getOfflinePlayer(i.getOwner()).getLastPlayed());
|
||||
BentoBox.getInstance().log("Will purge " +
|
||||
BentoBox.getInstance().getPlayers().getName(i.getOwner()) +
|
||||
" last logged in " + (int)((double)(System.currentTimeMillis() - Bukkit.getOfflinePlayer(i.getOwner()).getLastPlayed()) / 1000 / 3600 / 24) + " days ago. " + date);
|
||||
});
|
||||
return getPlugin().getIslands().getIslands().stream()
|
||||
.filter(i -> !i.isSpawn())
|
||||
.filter(i -> !i.getPurgeProtected())
|
||||
.filter(i -> i.getWorld().equals(this.getWorld()))
|
||||
.filter(Island::isOwned)
|
||||
.filter(i -> i.getMembers().size() == 1)
|
||||
.filter(i -> ((double)(System.currentTimeMillis() - Bukkit.getOfflinePlayer(i.getOwner()).getLastPlayed()) / 1000 / 3600 / 24) > days)
|
||||
.map(Island::getUniqueId)
|
||||
.collect(Collectors.toSet());
|
||||
.filter(i -> !i.isSpawn()).filter(i -> !i.getPurgeProtected())
|
||||
.filter(i -> i.getWorld().equals(this.getWorld())).filter(Island::isOwned).filter(
|
||||
i -> i.getMemberSet().stream()
|
||||
.allMatch(member -> (currentTimeMillis
|
||||
- Bukkit.getOfflinePlayer(member).getLastPlayed()) > daysInMilliseconds))
|
||||
.forEach(i -> {
|
||||
// Add the unique island ID to the set
|
||||
oldIslands.add(i.getUniqueId());
|
||||
BentoBox.getInstance().log("Will purge island at " + Util.xyz(i.getCenter().toVector()) + " in "
|
||||
+ i.getWorld().getName());
|
||||
// Log each member's last login information
|
||||
i.getMemberSet().forEach(member -> {
|
||||
Date lastLogin = new Date(Bukkit.getOfflinePlayer(member).getLastPlayed());
|
||||
BentoBox.getInstance()
|
||||
.log("Player " + BentoBox.getInstance().getPlayers().getName(member)
|
||||
+ " last logged in "
|
||||
+ (int) ((currentTimeMillis - Bukkit.getOfflinePlayer(member).getLastPlayed())
|
||||
/ 1000 / 3600 / 24)
|
||||
+ " days ago. " + lastLogin);
|
||||
});
|
||||
BentoBox.getInstance().log("+-----------------------------------------+");
|
||||
});
|
||||
|
||||
return oldIslands;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
package world.bentobox.bentobox.api.commands.admin.range;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
import world.bentobox.bentobox.util.Util;
|
||||
|
||||
/**
|
||||
* @author Poslovitch
|
||||
*/
|
||||
public abstract class AbstractAdminRangeCommand extends CompositeCommand {
|
||||
|
||||
protected @Nullable UUID targetUUID;
|
||||
protected Island targetIsland;
|
||||
|
||||
public AbstractAdminRangeCommand(CompositeCommand parent, String string) {
|
||||
super(parent, string);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canExecute(User user, String label, @NonNull List<String> args) {
|
||||
if (args.size() <= 1) {
|
||||
showHelp(this, user);
|
||||
return false;
|
||||
}
|
||||
|
||||
targetUUID = Util.getUUID(args.get(0));
|
||||
if (targetUUID == null) {
|
||||
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Util.isInteger(args.get(1), true) || Integer.parseInt(args.get(1)) < 0) {
|
||||
user.sendMessage("general.errors.must-be-positive-number", TextVariables.NUMBER, args.get(1));
|
||||
return false;
|
||||
}
|
||||
// Check if the player has more than one island
|
||||
Map<String, Island> islands = getIslandsXYZ(targetUUID);
|
||||
if (islands.size() == 0) {
|
||||
user.sendMessage("general.errors.player-has-no-island");
|
||||
return false;
|
||||
} else if (args.size() == 2) {
|
||||
// If they only have one island, 2 args are fine
|
||||
if (islands.size() == 1) {
|
||||
targetIsland = islands.values().iterator().next();
|
||||
return true;
|
||||
} else {
|
||||
// They need to specify which island
|
||||
user.sendMessage("commands.admin.unregister.errors.player-has-more-than-one-island");
|
||||
user.sendMessage("commands.admin.unregister.errors.specify-island-location");
|
||||
return false;
|
||||
}
|
||||
} else if (args.size() != 3) {
|
||||
// No location
|
||||
user.sendMessage("commands.admin.unregister.errors.specify-island-location");
|
||||
return false;
|
||||
} else if (!islands.containsKey(args.get(2))) {
|
||||
if (args.get(2).equalsIgnoreCase("help")) {
|
||||
this.showHelp(this, user);
|
||||
return false;
|
||||
}
|
||||
user.sendMessage("commands.admin.unregister.errors.unknown-island-location");
|
||||
return false;
|
||||
}
|
||||
targetIsland = islands.get(args.get(2));
|
||||
return true;
|
||||
}
|
||||
|
||||
protected Map<String, Island> getIslandsXYZ(UUID target) {
|
||||
return getIslands().getOwnedIslands(getWorld(), target).stream()
|
||||
.collect(Collectors.toMap(island -> Util.xyz(island.getCenter().toVector()), island -> island));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
|
||||
String lastArg = !args.isEmpty() ? args.get(args.size() - 1) : "";
|
||||
if (args.isEmpty()) {
|
||||
// Don't show every player on the server. Require at least the first letter
|
||||
return Optional.empty();
|
||||
} else if (args.size() == 3) {
|
||||
List<String> options = new ArrayList<>(Util.getOnlinePlayerList(user));
|
||||
return Optional.of(Util.tabLimit(options, lastArg));
|
||||
} else if (args.size() > 4) {
|
||||
// Find out which user
|
||||
UUID uuid = getPlayers().getUUID(args.get(2));
|
||||
if (uuid != null) {
|
||||
return Optional.of(Util.tabLimit(new ArrayList<>(getIslandsXYZ(uuid).keySet()), lastArg));
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
|
@ -1,23 +1,18 @@
|
|||
package world.bentobox.bentobox.api.commands.admin.range;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
|
||||
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||
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.util.Util;
|
||||
|
||||
/**
|
||||
* @since 1.10.0
|
||||
* @author Poslovitch
|
||||
*/
|
||||
public class AdminRangeAddCommand extends CompositeCommand {
|
||||
public class AdminRangeAddCommand extends AbstractAdminRangeCommand {
|
||||
|
||||
public AdminRangeAddCommand(AdminRangeCommand parent) {
|
||||
super(parent, "add");
|
||||
|
@ -32,53 +27,28 @@ public class AdminRangeAddCommand extends CompositeCommand {
|
|||
|
||||
@Override
|
||||
public boolean execute(User user, String label, @NonNull List<String> args) {
|
||||
if (args.size() != 2) {
|
||||
showHelp(this, user);
|
||||
return false;
|
||||
}
|
||||
int newRange = targetIsland.getProtectionRange() + Integer.parseInt(args.get(1));
|
||||
|
||||
UUID targetUUID = Util.getUUID(args.get(0));
|
||||
if (targetUUID == null) {
|
||||
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
|
||||
if (newRange > targetIsland.getRange()) {
|
||||
user.sendMessage("commands.admin.range.invalid-value.too-high", TextVariables.NUMBER,
|
||||
String.valueOf(targetIsland.getRange()));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(getIslands().hasIsland(getWorld(), targetUUID) || getIslands().inTeam(getWorld(), targetUUID))) {
|
||||
user.sendMessage("general.errors.player-has-no-island");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Util.isInteger(args.get(1), true) || Integer.parseInt(args.get(1)) < 0) {
|
||||
user.sendMessage("general.errors.must-be-positive-number", TextVariables.NUMBER, args.get(1));
|
||||
return false;
|
||||
}
|
||||
|
||||
Island island = Objects.requireNonNull(getIslands().getIsland(getWorld(), targetUUID));
|
||||
int newRange = island.getProtectionRange() + Integer.parseInt(args.get(1));
|
||||
|
||||
if (newRange > island.getRange()) {
|
||||
user.sendMessage("commands.admin.range.invalid-value.too-high", TextVariables.NUMBER, String.valueOf(island.getRange()));
|
||||
return false;
|
||||
} else if (newRange == island.getProtectionRange()) {
|
||||
} else if (newRange == targetIsland.getProtectionRange()) {
|
||||
user.sendMessage("commands.admin.range.invalid-value.same-as-before", TextVariables.NUMBER, args.get(1));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get old range for event
|
||||
int oldRange = island.getProtectionRange();
|
||||
int oldRange = targetIsland.getProtectionRange();
|
||||
|
||||
// Well, now it can be applied without taking any risks!
|
||||
island.setProtectionRange(newRange);
|
||||
targetIsland.setProtectionRange(newRange);
|
||||
|
||||
// Call Protection Range Change event. Does not support cancelling.
|
||||
IslandEvent.builder()
|
||||
.island(island)
|
||||
.location(island.getCenter())
|
||||
.reason(IslandEvent.Reason.RANGE_CHANGE)
|
||||
.involvedPlayer(targetUUID)
|
||||
.admin(true)
|
||||
.protectionRange(newRange, oldRange)
|
||||
.build();
|
||||
.island(targetIsland).location(targetIsland.getCenter())
|
||||
.reason(IslandEvent.Reason.RANGE_CHANGE).involvedPlayer(targetUUID).admin(true)
|
||||
.protectionRange(newRange, oldRange).build();
|
||||
|
||||
user.sendMessage("commands.admin.range.add.success",
|
||||
TextVariables.NAME, args.get(0), TextVariables.NUMBER, args.get(1),
|
||||
|
@ -86,4 +56,6 @@ public class AdminRangeAddCommand extends CompositeCommand {
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -31,4 +31,5 @@ public class AdminRangeCommand extends CompositeCommand {
|
|||
showHelp(this, user);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -13,6 +13,7 @@ import org.bukkit.Particle;
|
|||
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
import world.bentobox.bentobox.util.Util;
|
||||
|
||||
/**
|
||||
* @author Poslovitch
|
||||
|
@ -23,6 +24,9 @@ public class AdminRangeDisplayCommand extends CompositeCommand {
|
|||
private static final String DISPLAY = "display";
|
||||
private static final String SHOW = "show";
|
||||
private static final String HIDE = "hide";
|
||||
public static final Particle PARTICLE = Util.findFirstMatchingEnum(Particle.class, "REDSTONE", "DUST");
|
||||
private static final Particle PARTICLE2 = Util.findFirstMatchingEnum(Particle.class, "VILLAGER_HAPPY",
|
||||
"HAPPY_VILLAGER");
|
||||
|
||||
// Map of users to which ranges must be displayed
|
||||
private final Map<User, Integer> displayRanges = new HashMap<>();
|
||||
|
@ -76,11 +80,11 @@ public class AdminRangeDisplayCommand extends CompositeCommand {
|
|||
|
||||
// Draw the default protected area if island protected zone is different
|
||||
if (island.getProtectionRange() != getPlugin().getIWM().getIslandProtectionRange(getWorld())) {
|
||||
drawZone(user, Particle.VILLAGER_HAPPY, null, island, getPlugin().getIWM().getIslandProtectionRange(getWorld()));
|
||||
drawZone(user, PARTICLE2, null, island, getPlugin().getIWM().getIslandProtectionRange(getWorld()));
|
||||
}
|
||||
|
||||
// Draw the island area
|
||||
drawZone(user, Particle.REDSTONE, new Particle.DustOptions(Color.GRAY, 1.0F), island, island.getRange());
|
||||
drawZone(user, PARTICLE, new Particle.DustOptions(Color.GRAY, 1.0F), island, island.getRange());
|
||||
});
|
||||
}, 20, 30));
|
||||
}
|
||||
|
|
|
@ -1,23 +1,19 @@
|
|||
package world.bentobox.bentobox.api.commands.admin.range;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
|
||||
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||
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.util.Util;
|
||||
|
||||
/**
|
||||
* @since 1.10.0
|
||||
* @author Poslovitch
|
||||
*/
|
||||
public class AdminRangeRemoveCommand extends CompositeCommand {
|
||||
public class AdminRangeRemoveCommand extends AbstractAdminRangeCommand {
|
||||
|
||||
public AdminRangeRemoveCommand(AdminRangeCommand parent) {
|
||||
super(parent, "remove");
|
||||
|
@ -32,48 +28,31 @@ public class AdminRangeRemoveCommand extends CompositeCommand {
|
|||
|
||||
@Override
|
||||
public boolean execute(User user, String label, @NonNull List<String> args) {
|
||||
if (args.size() != 2) {
|
||||
showHelp(this, user);
|
||||
return false;
|
||||
}
|
||||
|
||||
UUID targetUUID = Util.getUUID(args.get(0));
|
||||
if (targetUUID == null) {
|
||||
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(getIslands().hasIsland(getWorld(), targetUUID) || getIslands().inTeam(getWorld(), targetUUID))) {
|
||||
user.sendMessage("general.errors.player-has-no-island");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Util.isInteger(args.get(1), true) || Integer.parseInt(args.get(1)) < 0) {
|
||||
user.sendMessage("general.errors.must-be-positive-number", TextVariables.NUMBER, args.get(1));
|
||||
return false;
|
||||
}
|
||||
|
||||
Island island = Objects.requireNonNull(getIslands().getIsland(getWorld(), targetUUID));
|
||||
int newRange = island.getProtectionRange() - Integer.parseInt(args.get(1));
|
||||
int newRange = targetIsland.getProtectionRange() - Integer.parseInt(args.get(1));
|
||||
|
||||
if (newRange <= 1) {
|
||||
user.sendMessage("commands.admin.range.invalid-value.too-low", TextVariables.NUMBER, String.valueOf(island.getRange()));
|
||||
user.sendMessage("commands.admin.range.invalid-value.too-low", TextVariables.NUMBER,
|
||||
String.valueOf(targetIsland.getRange()));
|
||||
return false;
|
||||
} else if (newRange == island.getProtectionRange()) {
|
||||
} else if (newRange == targetIsland.getProtectionRange()) {
|
||||
user.sendMessage("commands.admin.range.invalid-value.same-as-before", TextVariables.NUMBER, args.get(1));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get old range for event
|
||||
int oldRange = island.getProtectionRange();
|
||||
int oldRange = targetIsland.getProtectionRange();
|
||||
|
||||
// Well, now it can be applied without taking any risks!
|
||||
island.setProtectionRange(newRange);
|
||||
targetIsland.setProtectionRange(newRange);
|
||||
|
||||
// Call Protection Range Change event. Does not support cancelling.
|
||||
IslandEvent.builder()
|
||||
.island(island)
|
||||
.location(island.getCenter())
|
||||
.island(targetIsland).location(targetIsland.getCenter())
|
||||
.reason(IslandEvent.Reason.RANGE_CHANGE)
|
||||
.involvedPlayer(targetUUID)
|
||||
.admin(true)
|
||||
|
|
|
@ -1,19 +1,14 @@
|
|||
package world.bentobox.bentobox.api.commands.admin.range;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||
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.util.Util;
|
||||
|
||||
public class AdminRangeSetCommand extends CompositeCommand {
|
||||
public class AdminRangeSetCommand extends AbstractAdminRangeCommand {
|
||||
|
||||
public AdminRangeSetCommand(CompositeCommand parent) {
|
||||
super(parent, "set");
|
||||
|
@ -28,23 +23,6 @@ public class AdminRangeSetCommand extends CompositeCommand {
|
|||
|
||||
@Override
|
||||
public boolean execute(User user, String label, List<String> args) {
|
||||
if (args.size() != 2) {
|
||||
// Show help
|
||||
showHelp(this, user);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get target player
|
||||
UUID targetUUID = Util.getUUID(args.get(0));
|
||||
if (targetUUID == null) {
|
||||
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
|
||||
return false;
|
||||
}
|
||||
if (!(getIslands().hasIsland(getWorld(), targetUUID) || getIslands().inTeam(getWorld(), targetUUID))) {
|
||||
user.sendMessage("general.errors.player-has-no-island");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get new range
|
||||
if (!Util.isInteger(args.get(1), true) || Integer.parseInt(args.get(1)) < 0) {
|
||||
user.sendMessage("general.errors.must-be-positive-number", TextVariables.NUMBER, args.get(1));
|
||||
|
@ -52,33 +30,30 @@ public class AdminRangeSetCommand extends CompositeCommand {
|
|||
}
|
||||
int range = Integer.parseInt(args.get(1));
|
||||
|
||||
// Get island
|
||||
Island island = Objects.requireNonNull(getIslands().getIsland(getWorld(), targetUUID));
|
||||
|
||||
// Do some sanity checks to make sure the new protection range won't cause problems
|
||||
if (range < 1) {
|
||||
user.sendMessage("commands.admin.range.invalid-value.too-low", TextVariables.NUMBER, args.get(1));
|
||||
return false;
|
||||
}
|
||||
if (range > island.getRange() * 2) {
|
||||
user.sendMessage("commands.admin.range.invalid-value.too-high", TextVariables.NUMBER, String.valueOf(2 * island.getRange()));
|
||||
if (range > targetIsland.getRange() * 2) {
|
||||
user.sendMessage("commands.admin.range.invalid-value.too-high", TextVariables.NUMBER,
|
||||
String.valueOf(2 * targetIsland.getRange()));
|
||||
return false;
|
||||
}
|
||||
if (range == island.getProtectionRange()) {
|
||||
if (range == targetIsland.getProtectionRange()) {
|
||||
user.sendMessage("commands.admin.range.invalid-value.same-as-before", TextVariables.NUMBER, args.get(1));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get old range for event
|
||||
int oldRange = island.getProtectionRange();
|
||||
int oldRange = targetIsland.getProtectionRange();
|
||||
|
||||
// Well, now it can be applied without taking any risks!
|
||||
island.setProtectionRange(range);
|
||||
targetIsland.setProtectionRange(range);
|
||||
|
||||
// Call Protection Range Change event. Does not support canceling.
|
||||
IslandEvent.builder()
|
||||
.island(island)
|
||||
.location(island.getCenter())
|
||||
.island(targetIsland).location(targetIsland.getCenter())
|
||||
.reason(IslandEvent.Reason.RANGE_CHANGE)
|
||||
.involvedPlayer(targetUUID)
|
||||
.admin(true)
|
||||
|
@ -90,14 +65,4 @@ public class AdminRangeSetCommand extends CompositeCommand {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
|
||||
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
|
||||
if (args.isEmpty()) {
|
||||
// Don't show every player on the server. Require at least the first letter
|
||||
return Optional.empty();
|
||||
}
|
||||
List<String> options = new ArrayList<>(Util.getOnlinePlayerList(user));
|
||||
return Optional.of(Util.tabLimit(options, lastArg));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,17 +44,17 @@ public class AdminTeamAddCommand extends CompositeCommand {
|
|||
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(1));
|
||||
return false;
|
||||
}
|
||||
if (!getIslands().hasIsland(getWorld(), ownerUUID)) {
|
||||
Island island = getIslands().getPrimaryIsland(getWorld(), ownerUUID);
|
||||
if (island == null || !getIslands().hasIsland(getWorld(), ownerUUID)) {
|
||||
user.sendMessage("general.errors.player-has-no-island");
|
||||
return false;
|
||||
}
|
||||
Island island = getIslands().getPrimaryIsland(getWorld(), ownerUUID);
|
||||
if (getIslands().inTeam(getWorld(), ownerUUID) && island != null && !ownerUUID.equals(island.getOwner())) {
|
||||
if (getIslands().inTeam(getWorld(), ownerUUID) && !ownerUUID.equals(island.getOwner())) {
|
||||
user.sendMessage("commands.admin.team.add.name-not-owner", TextVariables.NAME, args.get(0));
|
||||
new IslandInfo(island).showMembers(user);
|
||||
return false;
|
||||
}
|
||||
if (getIslands().inTeam(getWorld(), targetUUID)) {
|
||||
if (getIWM().getWorldSettings(getWorld()).isDisallowTeamMemberIslands() && island.inTeam(targetUUID)) {
|
||||
user.sendMessage("commands.island.team.invite.errors.already-on-team");
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -52,10 +52,7 @@ public class AdminTeamDisbandCommand extends CompositeCommand {
|
|||
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
|
||||
return false;
|
||||
}
|
||||
if (!getIslands().inTeam(getWorld(), targetUUID)) {
|
||||
user.sendMessage("general.errors.player-is-not-owner", TextVariables.NAME, args.get(0));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the island the player is an owner of
|
||||
Map<String, Island> islands = getIslandsXYZ(targetUUID);
|
||||
if (islands.isEmpty()) {
|
||||
|
@ -77,12 +74,17 @@ public class AdminTeamDisbandCommand extends CompositeCommand {
|
|||
// Get the only island
|
||||
island = islands.values().iterator().next();
|
||||
}
|
||||
// Check that the target owns the island
|
||||
if (island.getOwner() == null || !island.getOwner().equals(targetUUID)) {
|
||||
user.sendMessage("general.errors.player-is-not-owner", TextVariables.NAME, args.get(0));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private Map<String, Island> getIslandsXYZ(UUID target) {
|
||||
return getIslands().getOwnedIslands(getWorld(), target).stream().filter(is -> is.getMemberSet().size() > 1) // Filter for teams
|
||||
.collect(Collectors.toMap(island -> Util.xyz(island.getCenter().toVector()), island -> island));
|
||||
.collect(Collectors.toMap(is -> Util.xyz(is.getCenter().toVector()), is -> is));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
package world.bentobox.bentobox.api.commands.admin.team;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
|
||||
public class AdminTeamFixCommand extends CompositeCommand {
|
||||
|
||||
|
||||
public AdminTeamFixCommand(CompositeCommand parent) {
|
||||
super(parent, "fix");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
setPermission("mod.team.fix");
|
||||
setDescription("commands.admin.team.fix.description");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canExecute(User user, String label, List<String> args) {
|
||||
// If args are not right, show help
|
||||
if (!args.isEmpty()) {
|
||||
showHelp(this, user);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public boolean execute(User user, String label, List<String> args) {
|
||||
getIslands().checkTeams(user, getWorld());
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,9 +1,17 @@
|
|||
package world.bentobox.bentobox.api.commands.admin.team;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
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.commands.ConfirmableCommand;
|
||||
import world.bentobox.bentobox.api.events.island.IslandEvent;
|
||||
import world.bentobox.bentobox.api.events.team.TeamEvent;
|
||||
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||
|
@ -17,7 +25,11 @@ import world.bentobox.bentobox.util.Util;
|
|||
*
|
||||
* @author tastybento
|
||||
*/
|
||||
public class AdminTeamSetownerCommand extends CompositeCommand {
|
||||
public class AdminTeamSetownerCommand extends ConfirmableCommand {
|
||||
|
||||
private @Nullable UUID targetUUID;
|
||||
private Island island;
|
||||
private @Nullable UUID previousOwnerUUID;
|
||||
|
||||
public AdminTeamSetownerCommand(CompositeCommand parent) {
|
||||
super(parent, "setowner");
|
||||
|
@ -28,35 +40,50 @@ public class AdminTeamSetownerCommand extends CompositeCommand {
|
|||
setPermission("mod.team.setowner");
|
||||
setParametersHelp("commands.admin.team.setowner.parameters");
|
||||
setDescription("commands.admin.team.setowner.description");
|
||||
this.setOnlyPlayer(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(User user, String label, List<String> args) {
|
||||
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
|
||||
UUID targetUUID = Util.getUUID(args.get(0));
|
||||
targetUUID = Util.getUUID(args.get(0));
|
||||
if (targetUUID == null) {
|
||||
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
|
||||
return false;
|
||||
}
|
||||
if (!getIslands().inTeam(getWorld(), targetUUID)) {
|
||||
user.sendMessage("general.errors.not-in-team");
|
||||
// Check that user is on an island
|
||||
Optional<Island> opIsland = getIslands().getIslandAt(user.getLocation());
|
||||
if (opIsland.isEmpty()) {
|
||||
user.sendMessage("commands.admin.team.setowner.must-be-on-island");
|
||||
return false;
|
||||
}
|
||||
Island island = getIslands().getPrimaryIsland(getWorld(), targetUUID);
|
||||
UUID previousOwnerUUID = island.getOwner();
|
||||
island = opIsland.get();
|
||||
previousOwnerUUID = island.getOwner();
|
||||
if (targetUUID.equals(previousOwnerUUID)) {
|
||||
user.sendMessage("commands.admin.team.setowner.already-owner", TextVariables.NAME, args.get(0));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get the User corresponding to the current owner
|
||||
public boolean execute(User user, String label, List<String> args) {
|
||||
Objects.requireNonNull(island);
|
||||
Objects.requireNonNull(targetUUID);
|
||||
|
||||
this.askConfirmation(user, user.getTranslation("commands.admin.team.setowner.confirmation", TextVariables.NAME,
|
||||
args.get(0), TextVariables.XYZ, Util.xyz(island.getCenter().toVector())), () -> changeOwner(user));
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
protected void changeOwner(User user) {
|
||||
User target = User.getInstance(targetUUID);
|
||||
|
||||
// Fire event so add-ons know
|
||||
// Call the setowner event
|
||||
TeamEvent.builder().island(island).reason(TeamEvent.Reason.SETOWNER).involvedPlayer(targetUUID).admin(true)
|
||||
|
@ -70,8 +97,20 @@ public class AdminTeamSetownerCommand extends CompositeCommand {
|
|||
.build();
|
||||
|
||||
// Make new owner
|
||||
getIslands().setOwner(getWorld(), user, targetUUID);
|
||||
user.sendMessage("commands.admin.team.setowner.success", TextVariables.NAME, args.get(0));
|
||||
getIslands().setOwner(user, targetUUID, island, RanksManager.MEMBER_RANK);
|
||||
user.sendMessage("commands.admin.team.setowner.success", TextVariables.NAME, target.getName());
|
||||
|
||||
// Report if this made player have more islands than expected
|
||||
// Get how many islands this player has
|
||||
int num = this.getIslands().getNumberOfConcurrentIslands(targetUUID, getWorld());
|
||||
int max = target.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("commands.admin.team.setowner.extra-islands", TextVariables.NUMBER, String.valueOf(num),
|
||||
"[max]", String.valueOf(max));
|
||||
}
|
||||
|
||||
// Call the rank change event for the old island owner
|
||||
if (previousOwnerUUID != null) {
|
||||
|
@ -80,6 +119,13 @@ public class AdminTeamSetownerCommand extends CompositeCommand {
|
|||
.reason(IslandEvent.Reason.RANK_CHANGE)
|
||||
.rankChange(RanksManager.OWNER_RANK, island.getRank(previousOwnerUUID)).build();
|
||||
}
|
||||
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> options = Bukkit.getOnlinePlayers().stream().map(Player::getName).toList();
|
||||
return Optional.of(Util.tabLimit(options, lastArg));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ public class IslandBanCommand extends CompositeCommand {
|
|||
user.sendMessage("commands.island.ban.cannot-ban-yourself");
|
||||
return false;
|
||||
}
|
||||
if (getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()).getMemberSet().contains(targetUUID)) {
|
||||
if (getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()).inTeam(targetUUID)) {
|
||||
user.sendMessage("commands.island.ban.cannot-ban-member");
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -51,7 +51,8 @@ public class IslandCreateCommand extends CompositeCommand {
|
|||
}
|
||||
}
|
||||
// Check if this player is on a team in this world
|
||||
if (getIslands().inTeam(getWorld(), user.getUniqueId()) && island != null
|
||||
if (getIWM().getWorldSettings(getWorld()).isDisallowTeamMemberIslands()
|
||||
&& 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");
|
||||
|
@ -86,22 +87,49 @@ public class IslandCreateCommand extends CompositeCommand {
|
|||
user.sendMessage("commands.island.create.unknown-blueprint");
|
||||
return false;
|
||||
}
|
||||
// Check perm
|
||||
if (!getPlugin().getBlueprintsManager().checkPerm(getAddon(), user, Util.sanitizeInput(args.get(0)))) {
|
||||
return false;
|
||||
}
|
||||
// Check maximum uses
|
||||
if (checkMaxUses(user, name)) {
|
||||
return false;
|
||||
}
|
||||
// Make island
|
||||
return makeIsland(user, name);
|
||||
} else {
|
||||
if (getPlugin().getSettings().getIslandNumber() > 1
|
||||
&& checkMaxUses(user, BlueprintsManager.DEFAULT_BUNDLE_NAME)) {
|
||||
return false;
|
||||
}
|
||||
// Show panel only if there are multiple bundles available
|
||||
if (getPlugin().getBlueprintsManager().getBlueprintBundles(getAddon()).size() > 1) {
|
||||
// Show panel
|
||||
IslandCreationPanel.openPanel(this, user, label);
|
||||
IslandCreationPanel.openPanel(this, user, label, false);
|
||||
return true;
|
||||
}
|
||||
return makeIsland(user, BlueprintsManager.DEFAULT_BUNDLE_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkMaxUses(User user, String name) {
|
||||
if (getPlugin().getBlueprintsManager().getBlueprintBundles(getAddon()).containsKey(name)) {
|
||||
int maxTimes = getPlugin().getBlueprintsManager().getBlueprintBundles(getAddon()).get(name).getTimes();
|
||||
// Check how many times this player has used this bundle
|
||||
if (maxTimes > 0 && getBundleUses(user, name) >= maxTimes) {
|
||||
user.sendMessage("commands.island.create.max-uses");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private long getBundleUses(User user, String name) {
|
||||
return getIslands().getIslands(getWorld(), user).stream()
|
||||
.filter(is -> is.getMetaData("bundle").map(mdv -> name.equalsIgnoreCase(mdv.asString())).orElse(false))
|
||||
.count();
|
||||
}
|
||||
|
||||
private boolean makeIsland(User user, String name) {
|
||||
user.sendMessage("commands.island.create.creating-island");
|
||||
try {
|
||||
|
|
|
@ -76,7 +76,7 @@ public class IslandExpelCommand extends CompositeCommand {
|
|||
return false;
|
||||
}
|
||||
// Or team member
|
||||
if (island.getMemberSet().contains(targetUUID)) {
|
||||
if (island.inTeam(targetUUID)) {
|
||||
user.sendMessage("commands.island.expel.cannot-expel-member");
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ 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;
|
||||
|
@ -41,7 +40,7 @@ public class IslandGoCommand extends DelayedTeleportCommand {
|
|||
user.sendMessage("commands.island.go.teleport");
|
||||
return false;
|
||||
}
|
||||
Set<Island> islands = getIslands().getIslands(getWorld(), user.getUniqueId());
|
||||
List<Island> islands = getIslands().getIslands(getWorld(), user.getUniqueId());
|
||||
if (islands.isEmpty()) {
|
||||
user.sendMessage("general.errors.no-island");
|
||||
return false;
|
||||
|
@ -86,7 +85,7 @@ public class IslandGoCommand extends DelayedTeleportCommand {
|
|||
return true;
|
||||
}
|
||||
|
||||
private boolean checkReserved(User user, Set<Island> islands) {
|
||||
private boolean checkReserved(User user, List<Island> islands) {
|
||||
for (Island island : islands) {
|
||||
if (island.isReserved()) {
|
||||
// Send player to create an island
|
||||
|
|
|
@ -3,7 +3,6 @@ package world.bentobox.bentobox.api.commands.island;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
|
||||
|
@ -14,7 +13,7 @@ import world.bentobox.bentobox.util.Util;
|
|||
|
||||
public class IslandHomesCommand extends ConfirmableCommand {
|
||||
|
||||
private Set<Island> islands;
|
||||
private List<Island> islands;
|
||||
|
||||
public IslandHomesCommand(CompositeCommand islandCommand) {
|
||||
super(islandCommand, "homes");
|
||||
|
|
|
@ -111,7 +111,7 @@ public class IslandResetCommand extends ConfirmableCommand {
|
|||
// Show panel only if there are multiple bundles available
|
||||
if (getPlugin().getBlueprintsManager().getBlueprintBundles(getAddon()).size() > 1) {
|
||||
// Show panel - once the player selected a bundle, this will re-run this command
|
||||
IslandCreationPanel.openPanel(this, user, label);
|
||||
IslandCreationPanel.openPanel(this, user, label, true);
|
||||
} else {
|
||||
resetIsland(user, BlueprintsManager.DEFAULT_BUNDLE_NAME);
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ public class IslandSethomeCommand extends ConfirmableCommand {
|
|||
|
||||
// Check number of homes
|
||||
|
||||
int maxHomes = getIslands().getIslands(getWorld(), user).stream().mapToInt(getIslands()::getMaxHomes).sum();
|
||||
int maxHomes = getIslands().getMaxHomes(island);
|
||||
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");
|
||||
|
|
|
@ -75,12 +75,6 @@ public class IslandSetnameCommand extends CompositeCommand {
|
|||
name = ChatColor.translateAlternateColorCodes('&', name);
|
||||
}
|
||||
|
||||
// Check if the name doesn't already exist in the gamemode
|
||||
if (getSettings().isNameUniqueness() && getIslands().nameExists(getWorld(), name)) {
|
||||
user.sendMessage("commands.island.setname.name-already-exists");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -49,7 +49,8 @@ public class IslandSettingsCommand extends CompositeCommand {
|
|||
.user(user)
|
||||
.island(island)
|
||||
.world(island.getWorld())
|
||||
.tab(1, new SettingsTab(user, Flag.Type.PROTECTION)).tab(2, new SettingsTab(user, Flag.Type.SETTING))
|
||||
.tab(1, new SettingsTab(getWorld(), user, Flag.Type.PROTECTION))
|
||||
.tab(2, new SettingsTab(getWorld(), user, Flag.Type.SETTING))
|
||||
.startingSlot(1)
|
||||
.size(54)
|
||||
.hideIfEmpty()
|
||||
|
|
|
@ -1,95 +0,0 @@
|
|||
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
|
||||
* @since 1.8.0
|
||||
*/
|
||||
public class Invite {
|
||||
|
||||
/**
|
||||
* Type of invitation
|
||||
*
|
||||
*/
|
||||
public enum Type {
|
||||
COOP,
|
||||
TEAM,
|
||||
TRUST
|
||||
}
|
||||
|
||||
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, Island island) {
|
||||
this.type = type;
|
||||
this.inviter = inviter;
|
||||
this.invitee = invitee;
|
||||
this.island = island;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the type
|
||||
*/
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the inviter
|
||||
*/
|
||||
public UUID getInviter() {
|
||||
return inviter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the invitee
|
||||
*/
|
||||
public UUID getInvitee() {
|
||||
return invitee;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the island
|
||||
*/
|
||||
public Island getIsland() {
|
||||
return island;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(invitee, inviter, type);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (!(obj instanceof Invite other)) {
|
||||
return false;
|
||||
}
|
||||
return Objects.equals(invitee, other.invitee) && Objects.equals(inviter, other.inviter) && type == other.type;
|
||||
}
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
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;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,24 +1,10 @@
|
|||
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;
|
||||
|
||||
|
@ -26,40 +12,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.Database;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
import world.bentobox.bentobox.database.objects.TeamInvite;
|
||||
import world.bentobox.bentobox.managers.RanksManager;
|
||||
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;
|
||||
|
@ -84,9 +45,11 @@ public class IslandTeamCommand extends CompositeCommand {
|
|||
|
||||
private IslandTeamTrustCommand trustCommand;
|
||||
|
||||
private final Database<TeamInvite> handler;
|
||||
|
||||
public IslandTeamCommand(CompositeCommand parent) {
|
||||
super(parent, "team");
|
||||
inviteMap = new HashMap<>();
|
||||
handler = new Database<>(parent.getAddon(), TeamInvite.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -120,13 +83,12 @@ public class IslandTeamCommand extends CompositeCommand {
|
|||
|
||||
@Override
|
||||
public boolean canExecute(User user, String label, List<String> args) {
|
||||
this.user = user;
|
||||
// Player issuing the command must have an island
|
||||
island = getIslands().getPrimaryIsland(getWorld(), user.getUniqueId());
|
||||
Island island = getIslands().getPrimaryIsland(getWorld(), user.getUniqueId());
|
||||
if (island == null) {
|
||||
if (isInvited(user.getUniqueId())) {
|
||||
// Player has an invite, so show the invite
|
||||
build();
|
||||
new IslandTeamGUI(getPlugin(), this, user, island).build();
|
||||
return true;
|
||||
}
|
||||
user.sendMessage("general.errors.no-island");
|
||||
|
@ -155,511 +117,11 @@ public class IslandTeamCommand extends CompositeCommand {
|
|||
@Override
|
||||
public boolean execute(User user, String label, List<String> args) {
|
||||
// Show the panel
|
||||
build();
|
||||
new IslandTeamGUI(getPlugin(), this, user, getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()))
|
||||
.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 onlineMemberCount = island.getMemberSet(RanksManager.MEMBER_RANK).stream()
|
||||
.filter(uuid -> Util.getOnlinePlayerList(user).contains(Bukkit.getOfflinePlayer(uuid).getName()))
|
||||
.count();
|
||||
|
||||
// Show header:
|
||||
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();
|
||||
|
||||
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
|
||||
message.add(user.getTranslation("commands.island.team.info.rank-layout.owner", TextVariables.RANK,
|
||||
user.getTranslation(RanksManager.OWNER_RANK_REF)));
|
||||
} else {
|
||||
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())));
|
||||
}
|
||||
message.addAll(displayOnOffline(user, rank, island, onlineMembers));
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
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)) {
|
||||
message.add(getMemberStatus(user, member, onlineMembers.contains(member)));
|
||||
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
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();
|
||||
|
@ -673,8 +135,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, @NonNull Island island) {
|
||||
inviteMap.put(invitee, new Invite(type, inviter, invitee, island));
|
||||
public void addInvite(TeamInvite.Type type, @NonNull UUID inviter, @NonNull UUID invitee, @NonNull Island island) {
|
||||
handler.saveObjectAsync(new TeamInvite(type, inviter, invitee, island.getUniqueId()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -684,7 +146,7 @@ public class IslandTeamCommand extends CompositeCommand {
|
|||
* @since 1.8.0
|
||||
*/
|
||||
public boolean isInvited(@NonNull UUID invitee) {
|
||||
return inviteMap.containsKey(invitee);
|
||||
return handler.objectExists(invitee.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -695,7 +157,7 @@ public class IslandTeamCommand extends CompositeCommand {
|
|||
*/
|
||||
@Nullable
|
||||
public UUID getInviter(UUID invitee) {
|
||||
return isInvited(invitee) ? inviteMap.get(invitee).getInviter() : null;
|
||||
return isInvited(invitee) ? handler.loadObject(invitee.toString()).getInviter() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -705,8 +167,8 @@ public class IslandTeamCommand extends CompositeCommand {
|
|||
* @since 1.8.0
|
||||
*/
|
||||
@Nullable
|
||||
public Invite getInvite(UUID invitee) {
|
||||
return inviteMap.get(invitee);
|
||||
public TeamInvite getInvite(UUID invitee) {
|
||||
return handler.loadObject(invitee.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -715,7 +177,7 @@ public class IslandTeamCommand extends CompositeCommand {
|
|||
* @since 1.8.0
|
||||
*/
|
||||
public void removeInvite(@NonNull UUID invitee) {
|
||||
inviteMap.remove(invitee);
|
||||
handler.deleteID(invitee.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -731,4 +193,52 @@ public class IslandTeamCommand extends CompositeCommand {
|
|||
protected IslandTeamTrustCommand getTrustCommand() {
|
||||
return trustCommand;
|
||||
}
|
||||
|
||||
public IslandTeamInviteCommand getInviteCommand() {
|
||||
return inviteCommand;
|
||||
}
|
||||
|
||||
public IslandTeamInviteAcceptCommand getAcceptCommand() {
|
||||
return acceptCommand;
|
||||
}
|
||||
|
||||
public IslandTeamInviteRejectCommand getRejectCommand() {
|
||||
return rejectCommand;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the kickCommand
|
||||
*/
|
||||
public IslandTeamKickCommand getKickCommand() {
|
||||
return kickCommand;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the leaveCommand
|
||||
*/
|
||||
public IslandTeamLeaveCommand getLeaveCommand() {
|
||||
return leaveCommand;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the setOwnerCommand
|
||||
*/
|
||||
public IslandTeamSetownerCommand getSetOwnerCommand() {
|
||||
return setOwnerCommand;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the uncoopCommand
|
||||
*/
|
||||
public IslandTeamUncoopCommand getUncoopCommand() {
|
||||
return uncoopCommand;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the unTrustCommand
|
||||
*/
|
||||
public IslandTeamUntrustCommand getUnTrustCommand() {
|
||||
return unTrustCommand;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,10 +8,10 @@ import java.util.UUID;
|
|||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||
import world.bentobox.bentobox.api.commands.island.team.Invite.Type;
|
||||
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.TeamInvite.Type;
|
||||
import world.bentobox.bentobox.managers.RanksManager;
|
||||
import world.bentobox.bentobox.util.Util;
|
||||
|
||||
|
@ -99,7 +99,7 @@ public class IslandTeamCoopCommand 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.COOP, user.getUniqueId(), target.getUniqueId(), island);
|
||||
itc.addInvite(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,
|
||||
|
|
|
@ -0,0 +1,568 @@
|
|||
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.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
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;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
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.database.objects.TeamInvite;
|
||||
import world.bentobox.bentobox.database.objects.TeamInvite.Type;
|
||||
import world.bentobox.bentobox.managers.RanksManager;
|
||||
import world.bentobox.bentobox.util.Util;
|
||||
|
||||
public class IslandTeamGUI {
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
private static final String NAME = ".name";
|
||||
|
||||
private static final String TIPS = "commands.island.team.gui.tips.";
|
||||
|
||||
private final User user;
|
||||
|
||||
private final Island island;
|
||||
|
||||
private int rankView = RanksManager.OWNER_RANK;
|
||||
|
||||
private @Nullable TemplateItem border;
|
||||
|
||||
private @Nullable TemplateItem background;
|
||||
|
||||
private final IslandTeamCommand parent;
|
||||
|
||||
private final BentoBox plugin;
|
||||
|
||||
|
||||
/**
|
||||
* Displays the team management GUI
|
||||
* @param plugin BentoBox
|
||||
* @param parent IslandTeamCommand object
|
||||
* @param user user who is opening the GUI
|
||||
* @param island island that the GUI is managing
|
||||
*/
|
||||
public IslandTeamGUI(BentoBox plugin, IslandTeamCommand parent, User user, Island island) {
|
||||
this.parent = parent;
|
||||
this.plugin = plugin;
|
||||
this.user = user;
|
||||
this.island = island;
|
||||
// Panels
|
||||
if (!new File(plugin.getDataFolder() + File.separator + "panels", "team_panel.yml").exists()) {
|
||||
plugin.saveResource("panels/team_panel.yml", false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method builds this GUI.
|
||||
*/
|
||||
public void build() {
|
||||
// Start building panel.
|
||||
TemplatedPanelBuilder panelBuilder = new TemplatedPanelBuilder();
|
||||
panelBuilder.user(user);
|
||||
panelBuilder.world(user.getWorld());
|
||||
|
||||
panelBuilder.template("team_panel", new File(plugin.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.parent.getInviteCommand().getPermission())
|
||||
|| island.getRank(user) < island.getRankCommand(parent.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().noneMatch(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();
|
||||
new IslandTeamInviteGUI(parent, false, island).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
|
||||
createDescription(builder);
|
||||
createClickHandler(builder, template.actions());
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private void createClickHandler(PanelItemBuilder builder, @NonNull List<ActionRecords> actions) {
|
||||
builder.clickHandler((panel, user, clickType, clickSlot) -> {
|
||||
if (actions.stream().noneMatch(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)) {
|
||||
rankView = RanksManager.getInstance().getRankDownValue(rankView);
|
||||
if (rankView <= RanksManager.VISITOR_RANK) {
|
||||
rankView = 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)) {
|
||||
rankView = RanksManager.getInstance().getRankUpValue(rankView);
|
||||
if (rankView >= RanksManager.OWNER_RANK) {
|
||||
rankView = 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;
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void createDescription(PanelItemBuilder builder) {
|
||||
RanksManager.getInstance().getRanks().forEach((reference, score) -> {
|
||||
if (rankView == 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 < rankView) {
|
||||
builder.description(user.getTranslation("protection.panel.flag-item.blocked-rank")
|
||||
+ user.getTranslation(reference));
|
||||
} else if (score <= RanksManager.OWNER_RANK && score > rankView) {
|
||||
builder.description(user.getTranslation("protection.panel.flag-item.blocked-rank")
|
||||
+ user.getTranslation(reference));
|
||||
} else if (score == rankView) {
|
||||
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"));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (parent.isInvited(user.getUniqueId()) && user.hasPermission(parent.getAcceptCommand().getPermission())) {
|
||||
TeamInvite invite = parent.getInvite(user.getUniqueId());
|
||||
if (invite == null) {
|
||||
return this.getBlankBorder();
|
||||
}
|
||||
User inviter = User.getInstance(invite.getInviter());
|
||||
String name = inviter.getName();
|
||||
builder.icon(inviter.getName());
|
||||
builder.name(user.getTranslation("commands.island.team.gui.buttons.invitation"));
|
||||
createInviteDescription(builder, invite.getType(), name, template.actions());
|
||||
createInviteClickHandler(builder, invite, template.actions());
|
||||
} else {
|
||||
return this.getBlankBorder();
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private void createInviteClickHandler(PanelItemBuilder builder, TeamInvite invite,
|
||||
@NonNull List<ActionRecords> list) {
|
||||
Type type = invite.getType();
|
||||
builder.clickHandler((panel, user, clickType, clickSlot) -> {
|
||||
if (list.stream().noneMatch(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(parent.getAcceptCommand().getPermission())) {
|
||||
plugin.log("Invite accepted: " + user.getName() + " accepted " + type);
|
||||
// Accept
|
||||
switch (type) {
|
||||
case COOP -> parent.getAcceptCommand().acceptCoopInvite(user, invite);
|
||||
case TRUST -> parent.getAcceptCommand().acceptTrustInvite(user, invite);
|
||||
default -> parent.getAcceptCommand().acceptTeamInvite(user, invite);
|
||||
}
|
||||
user.closeInventory();
|
||||
}
|
||||
if (clickType.equals(ClickType.SHIFT_RIGHT)
|
||||
&& user.hasPermission(parent.getRejectCommand().getPermission())) {
|
||||
// Reject
|
||||
plugin.log("Invite rejected: " + user.getName() + " rejected " + type + " invite.");
|
||||
parent.getRejectCommand().execute(user, "", List.of());
|
||||
user.closeInventory();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void createInviteDescription(PanelItemBuilder builder, Type type, String name,
|
||||
@NonNull List<ActionRecords> list) {
|
||||
builder.description(switch (type) {
|
||||
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(list.stream()
|
||||
.map(ar -> user.getTranslation(TIPS + ar.clickType().name() + NAME) + " "
|
||||
+ user.getTranslation(ar.tooltip()))
|
||||
.toList());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 is = plugin.getIslands().getPrimaryIsland(parent.getWorld(), user.getUniqueId());
|
||||
if (is == 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 is = plugin.getIslands().getPrimaryIsland(parent.getWorld(), user.getUniqueId());
|
||||
if (is == null) {
|
||||
return this.getBlankBackground();
|
||||
}
|
||||
int minimumRank = RanksManager.getInstance().getRankUpValue(RanksManager.VISITOR_RANK); // Get the rank above Visitor.
|
||||
Optional<User> opMember = is.getMemberSet(minimumRank).stream().map(User::getInstance)
|
||||
.filter((User usr) -> rankView == RanksManager.OWNER_RANK || is.getRank(usr) == rankView) // If rankView is owner then show all ranks
|
||||
.sorted(Comparator.comparingInt((User usr) -> is.getRank(usr)).reversed()) // Show owner on left, then descending ranks
|
||||
.skip(slot.slot()) // Get the head for this slot
|
||||
.limit(1L).findFirst(); // Get just one head
|
||||
if (opMember.isEmpty()) {
|
||||
return this.getBlankBackground();
|
||||
}
|
||||
User member = opMember.get();
|
||||
int rank = is.getRank(member);
|
||||
String rankRef = RanksManager.getInstance().getRank(rank);
|
||||
@NonNull
|
||||
List<ActionRecords> actions = template.actions();
|
||||
// Make button description depending on viewer
|
||||
List<String> desc = new ArrayList<>();
|
||||
int userRank = Objects.requireNonNull(is).getRank(user);
|
||||
// Add the tooltip for kicking
|
||||
if (user.hasPermission(parent.getKickCommand().getPermission())
|
||||
&& userRank >= is.getRankCommand(parent.getLabel() + " kick") && !user.equals(member)) {
|
||||
actions.stream().filter(ar -> ar.actionType().equalsIgnoreCase("kick"))
|
||||
.map(ar -> user.getTranslation(TIPS + ar.clickType().name() + NAME)
|
||||
+ " " + user.getTranslation(ar.tooltip()))
|
||||
.findFirst().ifPresent(desc::add);
|
||||
}
|
||||
// Set Owner
|
||||
if (user.hasPermission(parent.getSetOwnerCommand().getPermission()) && !user.equals(member)
|
||||
&& userRank >= RanksManager.OWNER_RANK && rank >= RanksManager.MEMBER_RANK) {
|
||||
// Add the tooltip for setowner
|
||||
actions.stream().filter(ar -> ar.actionType().equalsIgnoreCase("setowner"))
|
||||
.map(ar -> user.getTranslation(TIPS + ar.clickType().name() + NAME)
|
||||
+ " " + user.getTranslation(ar.tooltip()))
|
||||
.findFirst().ifPresent(desc::add);
|
||||
}
|
||||
// Leave
|
||||
if (user.hasPermission(parent.getLeaveCommand().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(TIPS + ar.clickType().name() + NAME)
|
||||
+ " " + user.getTranslation(ar.tooltip()))
|
||||
.findFirst().ifPresent(desc::add);
|
||||
}
|
||||
if (member.isOnline()) {
|
||||
desc.add(0, user.getTranslation(rankRef));
|
||||
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(rankRef));
|
||||
return new PanelItemBuilder().icon(member.getName())
|
||||
.name(offlinePlayerStatus(Bukkit.getOfflinePlayer(member.getUniqueId()))).description(desc)
|
||||
.clickHandler(
|
||||
(panel, user, clickType, i) -> clickListener(panel, user, clickType, i, member, actions))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Click listener
|
||||
* @param panel panel
|
||||
* @param clickingUser clicking user
|
||||
* @param clickType click type
|
||||
* @param i slot
|
||||
* @param target target user
|
||||
* @param actions actions
|
||||
* @return true if the inventory item should not be removed - always true
|
||||
*/
|
||||
private boolean clickListener(Panel panel, User clickingUser, ClickType clickType, int i, User target,
|
||||
List<ActionRecords> actions) {
|
||||
if (actions.stream().noneMatch(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" -> kickPlayer(clickingUser, target, rank);
|
||||
case "SETOWNER" -> setOwner(clickingUser, target);
|
||||
case "LEAVE" -> leave(clickingUser, target);
|
||||
default -> {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void leave(User clickingUser, User target) {
|
||||
if (clickingUser.hasPermission(parent.getLeaveCommand().getPermission()) && target.equals(clickingUser)
|
||||
&& !clickingUser.getUniqueId().equals(island.getOwner())) {
|
||||
plugin.log("Leave: " + clickingUser.getName() + " trying to leave island at " + island.getCenter());
|
||||
clickingUser.closeInventory();
|
||||
if (parent.getLeaveCommand().leave(clickingUser)) {
|
||||
plugin.log("Leave: success");
|
||||
} else {
|
||||
plugin.log("Leave: failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setOwner(User clickingUser, User target) {
|
||||
// Make the player the leader of the island
|
||||
if (clickingUser.hasPermission(parent.getSetOwnerCommand().getPermission()) && !target.equals(clickingUser)
|
||||
&& clickingUser.getUniqueId().equals(island.getOwner())
|
||||
&& island.getRank(target) >= RanksManager.MEMBER_RANK) {
|
||||
plugin.log("Set Owner: " + clickingUser.getName() + " trying to make " + target.getName()
|
||||
+ " owner of island at " + island.getCenter());
|
||||
clickingUser.closeInventory();
|
||||
if (parent.getSetOwnerCommand().setOwner(clickingUser, target.getUniqueId())) {
|
||||
plugin.log("Set Owner: success");
|
||||
} else {
|
||||
plugin.log("Set Owner: failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void kickPlayer(User clickingUser, User target, int rank) {
|
||||
// Kick the player, or uncoop, or untrust
|
||||
if (clickingUser.hasPermission(parent.getKickCommand().getPermission()) && !target.equals(clickingUser)
|
||||
&& rank >= island.getRankCommand(parent.getLabel() + " kick")) {
|
||||
plugin.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);
|
||||
plugin.log("Kick: success");
|
||||
} else {
|
||||
plugin.log("Kick: failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 -> parent.getUncoopCommand().unCoopCmd(user, member.getUniqueId());
|
||||
case RanksManager.TRUSTED_RANK -> parent.getUnTrustCommand().unTrustCmd(user, member.getUniqueId());
|
||||
default -> {
|
||||
if (parent.getKickCommand().canExecute(user, parent.getKickCommand().getLabel(),
|
||||
List.of(member.getName()))) {
|
||||
yield parent.getKickCommand().kick(clicker, member.getUniqueId());
|
||||
} else {
|
||||
yield false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
private List<String> showMembers() {
|
||||
List<String> message = new ArrayList<>();
|
||||
// Gather online members
|
||||
long onlineMemberCount = island.getMemberSet(RanksManager.MEMBER_RANK).stream()
|
||||
.filter(uuid -> Util.getOnlinePlayerList(user).contains(Bukkit.getOfflinePlayer(uuid).getName()))
|
||||
.count();
|
||||
|
||||
// Show header:
|
||||
message.add(user.getTranslation("commands.island.team.info.header", "[max]",
|
||||
String.valueOf(plugin.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();
|
||||
|
||||
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
|
||||
message.add(user.getTranslation("commands.island.team.info.rank-layout.owner", TextVariables.RANK,
|
||||
user.getTranslation(RanksManager.OWNER_RANK_REF)));
|
||||
} else {
|
||||
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())));
|
||||
}
|
||||
message.addAll(displayOnOffline(rank, island, onlineMembers));
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
private List<String> displayOnOffline(int rank, Island island, List<UUID> onlineMembers) {
|
||||
List<String> message = new ArrayList<>();
|
||||
for (UUID member : island.getMemberSet(rank, false)) {
|
||||
message.add(getMemberStatus(member, onlineMembers.contains(member)));
|
||||
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
private String getMemberStatus(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(offlineMember);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates text to describe the status of the player
|
||||
* @param offlineMember member of the team
|
||||
* @return string
|
||||
*/
|
||||
private String offlinePlayerStatus(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;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,18 +1,18 @@
|
|||
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;
|
||||
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
|
||||
import world.bentobox.bentobox.api.commands.island.team.Invite.Type;
|
||||
import world.bentobox.bentobox.api.events.IslandBaseEvent;
|
||||
import world.bentobox.bentobox.api.events.island.IslandEvent;
|
||||
import world.bentobox.bentobox.api.events.team.TeamEvent;
|
||||
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.TeamInvite;
|
||||
import world.bentobox.bentobox.database.objects.TeamInvite.Type;
|
||||
import world.bentobox.bentobox.managers.RanksManager;
|
||||
import world.bentobox.bentobox.util.Util;
|
||||
|
||||
|
@ -50,7 +50,7 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
|
|||
user.sendMessage(INVALID_INVITE);
|
||||
return false;
|
||||
}
|
||||
Invite invite = itc.getInvite(playerUUID);
|
||||
TeamInvite invite = itc.getInvite(playerUUID);
|
||||
if (invite.getType().equals(Type.TEAM)) {
|
||||
// Check rank to of inviter
|
||||
Island island = getIslands().getIsland(getWorld(), prospectiveOwnerUUID);
|
||||
|
@ -62,7 +62,8 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
|
|||
}
|
||||
|
||||
// Check if player is already in a team
|
||||
if (getIslands().inTeam(getWorld(), playerUUID)) {
|
||||
if (getIWM().getWorldSettings(getWorld()).isDisallowTeamMemberIslands()
|
||||
&& getIslands().inTeam(getWorld(), playerUUID)) {
|
||||
user.sendMessage("commands.island.team.invite.errors.you-already-are-in-team");
|
||||
return false;
|
||||
}
|
||||
|
@ -78,21 +79,27 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
|
|||
@Override
|
||||
public boolean execute(User user, String label, List<String> args) {
|
||||
// Get the invite
|
||||
Invite invite = itc.getInvite(user.getUniqueId());
|
||||
TeamInvite invite = itc.getInvite(user.getUniqueId());
|
||||
switch (invite.getType()) {
|
||||
case COOP -> askConfirmation(user, () -> acceptCoopInvite(user, invite));
|
||||
case TRUST -> askConfirmation(user, () -> acceptTrustInvite(user, invite));
|
||||
default -> askConfirmation(user, user.getTranslation("commands.island.team.invite.accept.confirmation"),
|
||||
() -> acceptTeamInvite(user, invite));
|
||||
default -> {
|
||||
if (getIWM().getWorldSettings(getWorld()).isDisallowTeamMemberIslands()) {
|
||||
askConfirmation(user, user.getTranslation("commands.island.team.invite.accept.confirmation"),
|
||||
() -> acceptTeamInvite(user, invite));
|
||||
} else {
|
||||
acceptTeamInvite(user, invite);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void acceptTrustInvite(User user, Invite invite) {
|
||||
void acceptTrustInvite(User user, TeamInvite invite) {
|
||||
// Remove the invite
|
||||
itc.removeInvite(user.getUniqueId());
|
||||
User inviter = User.getInstance(invite.getInviter());
|
||||
Island island = invite.getIsland();
|
||||
Island island = getIslands().getIslandById(invite.getIslandID()).orElse(null);
|
||||
if (island != null) {
|
||||
if (island.getMemberSet(RanksManager.TRUSTED_RANK, false).size() > getIslands().getMaxMembers(island,
|
||||
RanksManager.TRUSTED_RANK)) {
|
||||
|
@ -114,11 +121,11 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
|
|||
}
|
||||
}
|
||||
|
||||
void acceptCoopInvite(User user, Invite invite) {
|
||||
void acceptCoopInvite(User user, TeamInvite invite) {
|
||||
// Remove the invite
|
||||
itc.removeInvite(user.getUniqueId());
|
||||
User inviter = User.getInstance(invite.getInviter());
|
||||
Island island = invite.getIsland();
|
||||
Island island = getIslands().getIslandById(invite.getIslandID()).orElse(null);
|
||||
if (island != null) {
|
||||
if (island.getMemberSet(RanksManager.COOP_RANK, false).size() > getIslands().getMaxMembers(island,
|
||||
RanksManager.COOP_RANK)) {
|
||||
|
@ -140,13 +147,13 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
|
|||
}
|
||||
}
|
||||
|
||||
void acceptTeamInvite(User user, Invite invite) {
|
||||
void acceptTeamInvite(User user, TeamInvite invite) {
|
||||
// Remove the invite
|
||||
itc.removeInvite(user.getUniqueId());
|
||||
// Get the player's island - may be null if the player has no island
|
||||
Set<Island> islands = getIslands().getIslands(getWorld(), user.getUniqueId());
|
||||
List<Island> islands = getIslands().getIslands(getWorld(), user.getUniqueId());
|
||||
// Get the team's island
|
||||
Island teamIsland = invite.getIsland();
|
||||
Island teamIsland = getIslands().getIslandById(invite.getIslandID()).orElse(null);
|
||||
if (teamIsland == null) {
|
||||
user.sendMessage(INVALID_INVITE);
|
||||
return;
|
||||
|
@ -156,17 +163,21 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
|
|||
user.sendMessage("commands.island.team.invite.errors.island-is-full");
|
||||
return;
|
||||
}
|
||||
// Remove the player's other islands
|
||||
getIslands().removePlayer(getWorld(), user.getUniqueId());
|
||||
// Remove money inventory etc. for leaving
|
||||
cleanPlayer(user);
|
||||
if (getIWM().getWorldSettings(getWorld()).isDisallowTeamMemberIslands()) {
|
||||
// Remove the player's other islands
|
||||
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, user.getUniqueId());
|
||||
// Move player to team's island
|
||||
getIslands().setPrimaryIsland(user.getUniqueId(), teamIsland);
|
||||
getIslands().homeTeleportAsync(getWorld(), user.getPlayer()).thenRun(() -> {
|
||||
// Delete the old islands
|
||||
islands.forEach(island -> getIslands().deleteIsland(island, true, user.getUniqueId()));
|
||||
|
||||
if (getIWM().getWorldSettings(getWorld()).isDisallowTeamMemberIslands()) {
|
||||
// Delete the old islands
|
||||
islands.forEach(island -> getIslands().deleteIsland(island, true, user.getUniqueId()));
|
||||
}
|
||||
// Put player back into normal mode
|
||||
user.setGameMode(getIWM().getDefaultGameMode(getWorld()));
|
||||
|
||||
|
@ -175,8 +186,9 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
|
|||
Util.runCommands(user, ownerName, getIWM().getOnJoinCommands(getWorld()), "join");
|
||||
|
||||
});
|
||||
// Reset deaths
|
||||
if (getIWM().isTeamJoinDeathReset(getWorld())) {
|
||||
if (getIWM().getWorldSettings(getWorld()).isDisallowTeamMemberIslands()
|
||||
&& getIWM().isTeamJoinDeathReset(getWorld())) {
|
||||
// Reset deaths
|
||||
getPlayers().setDeaths(getWorld(), user.getUniqueId(), 0);
|
||||
}
|
||||
user.sendMessage("commands.island.team.invite.accept.you-joined-island", TextVariables.LABEL, getTopLabel());
|
||||
|
@ -185,7 +197,6 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
|
|||
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(teamIsland).reason(TeamEvent.Reason.JOINED).involvedPlayer(user.getUniqueId())
|
||||
.build();
|
||||
|
|
|
@ -7,31 +7,16 @@ 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.database.objects.TeamInvite.Type;
|
||||
import world.bentobox.bentobox.managers.IslandsManager;
|
||||
import world.bentobox.bentobox.managers.PlayersManager;
|
||||
import world.bentobox.bentobox.managers.RanksManager;
|
||||
|
@ -43,11 +28,6 @@ public class IslandTeamInviteCommand extends CompositeCommand {
|
|||
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");
|
||||
|
@ -77,14 +57,13 @@ public class IslandTeamInviteCommand extends CompositeCommand {
|
|||
user.sendMessage("general.errors.no-island");
|
||||
return false;
|
||||
}
|
||||
Island island = islandsManager.getIsland(getWorld(), user);
|
||||
|
||||
if (args.size() != 1) {
|
||||
this.inviteCmd = true;
|
||||
build(user);
|
||||
new IslandTeamInviteGUI(itc, true, island).build(user);
|
||||
return true;
|
||||
}
|
||||
|
||||
Island island = islandsManager.getIsland(getWorld(), user);
|
||||
int rank = Objects.requireNonNull(island).getRank(user);
|
||||
|
||||
return checkRankAndInvitePlayer(user, island, rank, args.get(0));
|
||||
|
@ -126,7 +105,8 @@ public class IslandTeamInviteCommand extends CompositeCommand {
|
|||
}
|
||||
|
||||
// Player cannot invite someone already on a team
|
||||
if (getIslands().inTeam(getWorld(), invitedPlayerUUID)) {
|
||||
if (getIWM().getWorldSettings(getWorld()).isDisallowTeamMemberIslands()
|
||||
&& getIslands().inTeam(getWorld(), invitedPlayerUUID)) {
|
||||
user.sendMessage("commands.island.team.invite.errors.already-on-team");
|
||||
return false;
|
||||
}
|
||||
|
@ -186,12 +166,13 @@ 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(), island);
|
||||
itc.addInvite(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());
|
||||
invitedPlayer.sendMessage("commands.island.team.invite.to-accept-or-reject", TextVariables.LABEL, getTopLabel());
|
||||
if (getIslands().hasIsland(getWorld(), invitedPlayer.getUniqueId())) {
|
||||
if (getIWM().getWorldSettings(getWorld()).isDisallowTeamMemberIslands()
|
||||
&& getIslands().hasIsland(getWorld(), invitedPlayer.getUniqueId())) {
|
||||
invitedPlayer.sendMessage("commands.island.team.invite.you-will-lose-your-island");
|
||||
}
|
||||
return true;
|
||||
|
@ -208,204 +189,4 @@ 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,286 @@
|
|||
package world.bentobox.bentobox.api.commands.island.team;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.conversations.ConversationContext;
|
||||
import org.bukkit.conversations.ConversationFactory;
|
||||
import org.bukkit.conversations.Prompt;
|
||||
import org.bukkit.conversations.StringPrompt;
|
||||
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.localization.TextVariables;
|
||||
import world.bentobox.bentobox.api.panels.PanelItem;
|
||||
import world.bentobox.bentobox.api.panels.TemplatedPanel;
|
||||
import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder;
|
||||
import world.bentobox.bentobox.api.panels.builders.TemplatedPanelBuilder;
|
||||
import world.bentobox.bentobox.api.panels.reader.ItemTemplateRecord;
|
||||
import world.bentobox.bentobox.api.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;
|
||||
|
||||
public class IslandTeamInviteGUI {
|
||||
|
||||
private final IslandTeamInviteCommand itic;
|
||||
private final IslandTeamCommand itc;
|
||||
private @Nullable TemplateItem border;
|
||||
private @Nullable TemplateItem background;
|
||||
private User user;
|
||||
private long page = 0; // This number by 35
|
||||
private final boolean inviteCmd;
|
||||
private static final long PER_PAGE = 35;
|
||||
private String searchName = "";
|
||||
private final BentoBox plugin;
|
||||
private final Island island;
|
||||
|
||||
public IslandTeamInviteGUI(IslandTeamCommand itc, boolean invitedCmd, Island island) {
|
||||
this.island = island;
|
||||
this.plugin = itc.getPlugin();
|
||||
this.inviteCmd = invitedCmd;
|
||||
itic = itc.getInviteCommand();
|
||||
this.itc = itc;
|
||||
// Panels
|
||||
if (!new File(plugin.getDataFolder() + File.separator + "panels", "team_invite_panel.yml")
|
||||
.exists()) {
|
||||
plugin.saveResource("panels/team_invite_panel.yml", false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(plugin.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) {
|
||||
new IslandTeamGUI(plugin, itc, user, island).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())
|
||||
.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 = itc.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) {
|
||||
plugin.logError("Icon in template is missing or unknown! " + template.toString());
|
||||
}
|
||||
if (template.title() == null) {
|
||||
plugin.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 is = plugin.getIslands().getPrimaryIsland(itc.getWorld(), user.getUniqueId());
|
||||
if (is == null) {
|
||||
return this.getBlankBackground();
|
||||
}
|
||||
if (page < 0) {
|
||||
page = 0;
|
||||
}
|
||||
// Stream of all players that the user can see
|
||||
Stream<Player> visiblePlayers = itc.getWorld().getPlayers().stream().filter(user.getPlayer()::canSee);
|
||||
|
||||
// Filter players based on searchName if it's not blank, and ensure they're not the user
|
||||
Stream<Player> filteredPlayers = visiblePlayers
|
||||
.filter(player -> this.searchName.isBlank()
|
||||
|| player.getName().toLowerCase().contains(searchName.toLowerCase()))
|
||||
.filter(player -> !player.equals(user.getPlayer()));
|
||||
|
||||
// Skipping to the correct pagination slot, then finding the first player
|
||||
Optional<Player> playerOptional = filteredPlayers.skip(slot.slot() + page * PER_PAGE).findFirst();
|
||||
|
||||
// Map the player to a prospect or return a blank background if not found
|
||||
return playerOptional.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(user, clickType, player,
|
||||
template.actions()))
|
||||
.build();
|
||||
}
|
||||
|
||||
private boolean clickHandler(User user, ClickType clickType, Player player, @NonNull List<ActionRecords> list) {
|
||||
if (list.stream().noneMatch(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 (itic.canExecute(user, itic.getLabel(), List.of(player.getName()))) {
|
||||
plugin.log("Invite sent to: " + player.getName() + " by " + user.getName() + " to join island in "
|
||||
+ itc.getWorld().getName());
|
||||
itic.execute(user, itic.getLabel(), List.of(player.getName()));
|
||||
} else {
|
||||
plugin.log("Invite failed: " + player.getName() + " by " + user.getName() + " to join island in "
|
||||
+ itc.getWorld().getName());
|
||||
}
|
||||
} else if (clickType.equals(ClickType.RIGHT)) {
|
||||
user.closeInventory();
|
||||
if (this.itc.getCoopCommand().canExecute(user, itic.getLabel(), List.of(player.getName()))) {
|
||||
plugin.log("Coop: " + player.getName() + " cooped " + user.getName() + " to island in "
|
||||
+ itc.getWorld().getName());
|
||||
this.itc.getCoopCommand().execute(user, itic.getLabel(), List.of(player.getName()));
|
||||
} else {
|
||||
plugin.log(
|
||||
"Coop failed: " + player.getName() + "'s coop to " + user.getName() + " failed for island in "
|
||||
+ itc.getWorld().getName());
|
||||
}
|
||||
} else if (clickType.equals(ClickType.SHIFT_LEFT)) {
|
||||
user.closeInventory();
|
||||
if (this.itc.getTrustCommand().canExecute(user, itic.getLabel(), List.of(player.getName()))) {
|
||||
plugin.log("Trust: " + player.getName() + " trusted " + user.getName() + " to island in "
|
||||
+ itc.getWorld().getName());
|
||||
this.itc.getTrustCommand().execute(user, itic.getLabel(), List.of(player.getName()));
|
||||
} else {
|
||||
plugin.log("Trust failed: " + player.getName() + "'s trust failed for " + user.getName()
|
||||
+ " for island in "
|
||||
+ itc.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();
|
||||
}
|
||||
|
||||
class InviteNamePrompt extends StringPrompt {
|
||||
|
||||
@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) {
|
||||
if (itic.canExecute(user, itic.getLabel(), List.of(input))
|
||||
&& itic.execute(user, itic.getLabel(), List.of(input))) {
|
||||
return Prompt.END_OF_CONVERSATION;
|
||||
}
|
||||
// Set the search item to what was entered
|
||||
searchName = 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(), () -> build(user), 20L);
|
||||
return Prompt.END_OF_CONVERSATION;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -63,7 +63,7 @@ public class IslandTeamKickCommand extends ConfirmableCommand {
|
|||
user.sendMessage("commands.island.team.kick.cannot-kick");
|
||||
return false;
|
||||
}
|
||||
if (!getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()).getMemberSet().contains(targetUUID)) {
|
||||
if (!getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()).inTeam(targetUUID)) {
|
||||
user.sendMessage("general.errors.not-in-team");
|
||||
return false;
|
||||
}
|
||||
|
@ -90,22 +90,25 @@ public class IslandTeamKickCommand extends ConfirmableCommand {
|
|||
}
|
||||
}
|
||||
|
||||
protected void kick(User user, UUID targetUUID) {
|
||||
protected boolean kick(User user, UUID targetUUID) {
|
||||
if (targetUUID == null) {
|
||||
return false;
|
||||
}
|
||||
User target = User.getInstance(targetUUID);
|
||||
Island oldIsland = Objects.requireNonNull(getIslands().getIsland(getWorld(), targetUUID)); // Should never be
|
||||
Island oldIsland = Objects.requireNonNull(getIslands().getIsland(getWorld(), user)); // Should never be
|
||||
// null because of
|
||||
// checks above
|
||||
// Fire event
|
||||
IslandBaseEvent event = TeamEvent.builder().island(oldIsland).reason(TeamEvent.Reason.KICK)
|
||||
.involvedPlayer(targetUUID).build();
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
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);
|
||||
getIslands().removePlayer(oldIsland, targetUUID);
|
||||
// Clean the target player
|
||||
getPlayers().cleanLeavingPlayer(getWorld(), target, true, oldIsland);
|
||||
|
||||
|
@ -121,6 +124,7 @@ public class IslandTeamKickCommand extends ConfirmableCommand {
|
|||
getParent().getSubCommand("invite").ifPresent(c -> c.setCooldown(oldIsland.getUniqueId(),
|
||||
targetUUID.toString(), getSettings().getInviteCooldown() * 60));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,6 +15,9 @@ import world.bentobox.bentobox.database.objects.Island;
|
|||
import world.bentobox.bentobox.managers.RanksManager;
|
||||
import world.bentobox.bentobox.util.Util;
|
||||
|
||||
/**
|
||||
* Handle promotion and demotion
|
||||
*/
|
||||
public class IslandTeamPromoteCommand extends CompositeCommand {
|
||||
|
||||
private User target;
|
||||
|
@ -45,7 +48,7 @@ public class IslandTeamPromoteCommand extends CompositeCommand {
|
|||
showHelp(this, user);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the user has a team
|
||||
if (!getIslands().inTeam(getWorld(), user.getUniqueId())) {
|
||||
user.sendMessage("general.errors.no-team");
|
||||
return false;
|
||||
|
@ -65,6 +68,11 @@ public class IslandTeamPromoteCommand extends CompositeCommand {
|
|||
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
|
||||
return false;
|
||||
}
|
||||
// Check that target is a member of this island
|
||||
if (!island.inTeam(target.getUniqueId())) {
|
||||
user.sendMessage("commands.island.team.promote.errors.must-be-member");
|
||||
return false;
|
||||
}
|
||||
// Check if the user is not trying to promote/ demote himself
|
||||
if (target.equals(user)) {
|
||||
if (this.getLabel().equals("promote")) {
|
||||
|
@ -100,7 +108,8 @@ public class IslandTeamPromoteCommand extends CompositeCommand {
|
|||
if (this.getLabel().equals("promote")) {
|
||||
int nextRank = RanksManager.getInstance().getRankUpValue(currentRank);
|
||||
// Stop short of owner
|
||||
if (nextRank != RanksManager.OWNER_RANK && nextRank > currentRank) {
|
||||
if (nextRank < RanksManager.OWNER_RANK && currentRank >= RanksManager.MEMBER_RANK
|
||||
&& nextRank > currentRank) {
|
||||
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());
|
||||
|
|
|
@ -42,7 +42,7 @@ public class IslandTeamSetownerCommand extends CompositeCommand {
|
|||
}
|
||||
// Can use if in a team
|
||||
Island is = getIslands().getPrimaryIsland(getWorld(), user.getUniqueId());
|
||||
if (is == null || !is.getMemberSet().contains(user.getUniqueId())) {
|
||||
if (is == null || !is.inTeam(user.getUniqueId())) {
|
||||
user.sendMessage("general.errors.no-team");
|
||||
return false;
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ public class IslandTeamSetownerCommand extends CompositeCommand {
|
|||
user.sendMessage("commands.island.team.setowner.errors.cant-transfer-to-yourself");
|
||||
return false;
|
||||
}
|
||||
if (!is.getMemberSet().contains(targetUUID)) {
|
||||
if (!is.inTeam(targetUUID)) {
|
||||
user.sendMessage("commands.island.team.setowner.errors.target-is-not-member");
|
||||
return false;
|
||||
}
|
||||
|
@ -91,7 +91,6 @@ public class IslandTeamSetownerCommand extends CompositeCommand {
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,10 +8,10 @@ import java.util.UUID;
|
|||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||
import world.bentobox.bentobox.api.commands.island.team.Invite.Type;
|
||||
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.TeamInvite.Type;
|
||||
import world.bentobox.bentobox.managers.RanksManager;
|
||||
import world.bentobox.bentobox.util.Util;
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ public class IslandTeamUncoopCommand extends CompositeCommand {
|
|||
user.sendMessage("commands.island.team.uncoop.cannot-uncoop-yourself");
|
||||
return false;
|
||||
}
|
||||
if (getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()).getMemberSet().contains(targetUUID)) {
|
||||
if (getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()).inTeam(targetUUID)) {
|
||||
user.sendMessage("commands.island.team.uncoop.cannot-uncoop-member");
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ public class IslandTeamUntrustCommand extends CompositeCommand {
|
|||
user.sendMessage("commands.island.team.untrust.cannot-untrust-yourself");
|
||||
return false;
|
||||
}
|
||||
if (getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()).getMemberSet().contains(targetUUID)) {
|
||||
if (getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()).inTeam(targetUUID)) {
|
||||
user.sendMessage("commands.island.team.untrust.cannot-untrust-member");
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -644,4 +644,13 @@ public interface WorldSettings extends ConfigObject {
|
|||
default int getConcurrentIslands() {
|
||||
return BentoBox.getInstance().getSettings().getIslandNumber();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove islands when players join a team and not allow players to have other islands if they are in a team.
|
||||
* @return true or false
|
||||
* @since 2.3.0
|
||||
*/
|
||||
default boolean isDisallowTeamMemberIslands() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -388,21 +388,24 @@ public class Flag implements Comparable<Flag> {
|
|||
if (!user.isOp() && invisible) {
|
||||
return null;
|
||||
}
|
||||
// Start the flag conversion
|
||||
PanelItemBuilder pib = new PanelItemBuilder()
|
||||
.icon(ItemParser.parse(user.getTranslationOrNothing(this.getIconReference()), new ItemStack(icon)))
|
||||
.name(user.getTranslation("protection.panel.flag-item.name-layout", TextVariables.NAME, user.getTranslation(getNameReference())))
|
||||
.name(user.getTranslation("protection.panel.flag-item.name-layout", TextVariables.NAME,
|
||||
user.getTranslation(getNameReference())))
|
||||
.clickHandler(clickHandler)
|
||||
.invisible(invisible);
|
||||
if (hasSubPanel()) {
|
||||
pib.description(user.getTranslation("protection.panel.flag-item.menu-layout", TextVariables.DESCRIPTION, user.getTranslation(getDescriptionReference())));
|
||||
return pib.build();
|
||||
}
|
||||
|
||||
return switch (getType()) {
|
||||
case PROTECTION -> createProtectionFlag(plugin, user, island, pib).build();
|
||||
case SETTING -> createSettingFlag(user, island, pib).build();
|
||||
case WORLD_SETTING -> createWorldSettingFlag(user, world, pib).build();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
private PanelItemBuilder createWorldSettingFlag(User user, World world, PanelItemBuilder pib) {
|
||||
|
@ -429,19 +432,24 @@ public class Flag implements Comparable<Flag> {
|
|||
|
||||
private PanelItemBuilder createProtectionFlag(BentoBox plugin, User user, Island island, PanelItemBuilder pib) {
|
||||
if (island != null) {
|
||||
int y = island.getFlag(this);
|
||||
// Protection flag
|
||||
|
||||
pib.description(user.getTranslation("protection.panel.flag-item.description-layout",
|
||||
TextVariables.DESCRIPTION, user.getTranslation(getDescriptionReference())));
|
||||
|
||||
RanksManager.getInstance().getRanks().forEach((reference, score) -> {
|
||||
if (score > RanksManager.BANNED_RANK && score < island.getFlag(this)) {
|
||||
|
||||
if (score > RanksManager.BANNED_RANK && score < y) {
|
||||
pib.description(user.getTranslation("protection.panel.flag-item.blocked-rank") + user.getTranslation(reference));
|
||||
} else if (score <= RanksManager.OWNER_RANK && score > island.getFlag(this)) {
|
||||
} else if (score <= RanksManager.OWNER_RANK && score > y) {
|
||||
pib.description(user.getTranslation("protection.panel.flag-item.allowed-rank") + user.getTranslation(reference));
|
||||
} else if (score == island.getFlag(this)) {
|
||||
} else if (score == y) {
|
||||
pib.description(user.getTranslation("protection.panel.flag-item.minimal-rank") + user.getTranslation(reference));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return pib;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import java.util.Objects;
|
|||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
|
@ -59,6 +60,11 @@ public class CycleClick implements PanelItem.ClickHandler {
|
|||
|
||||
@Override
|
||||
public boolean onClick(Panel panel, User user2, ClickType click, int slot) {
|
||||
if (panel.getWorld().isEmpty()) {
|
||||
plugin.logError("Panel " + panel.getName()
|
||||
+ " has no world associated with it. Please report this bug to the author.");
|
||||
return true;
|
||||
}
|
||||
// This click listener is used with TabbedPanel and SettingsTabs only
|
||||
TabbedPanel tp = (TabbedPanel)panel;
|
||||
SettingsTab st = (SettingsTab)tp.getActiveTab();
|
||||
|
@ -67,7 +73,7 @@ public class CycleClick implements PanelItem.ClickHandler {
|
|||
this.user = user2;
|
||||
changeOccurred = false;
|
||||
// Permission prefix
|
||||
String prefix = plugin.getIWM().getPermissionPrefix(Util.getWorld(user.getWorld()));
|
||||
String prefix = plugin.getIWM().getPermissionPrefix(Util.getWorld(panel.getWorld().get()));
|
||||
String reqPerm = prefix + "settings." + id;
|
||||
String allPerms = prefix + "settings.*";
|
||||
if (!user.hasPermission(reqPerm) && !user.hasPermission(allPerms)
|
||||
|
@ -91,7 +97,7 @@ public class CycleClick implements PanelItem.ClickHandler {
|
|||
rightClick(flag, currentRank);
|
||||
|
||||
} else if (click.equals(ClickType.SHIFT_LEFT) && user2.isOp()) {
|
||||
leftShiftClick(flag);
|
||||
leftShiftClick(flag, panel.getWorld().get());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
@ -149,16 +155,16 @@ public class CycleClick implements PanelItem.ClickHandler {
|
|||
|
||||
}
|
||||
|
||||
private void leftShiftClick(Flag flag) {
|
||||
if (!plugin.getIWM().getHiddenFlags(user.getWorld()).contains(flag.getID())) {
|
||||
plugin.getIWM().getHiddenFlags(user.getWorld()).add(flag.getID());
|
||||
private void leftShiftClick(Flag flag, World world) {
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -48,7 +48,6 @@ public class PanelItem {
|
|||
meta.addItemFlags(ItemFlag.HIDE_DESTROYS);
|
||||
meta.addItemFlags(ItemFlag.HIDE_PLACED_ON);
|
||||
meta.addItemFlags(ItemFlag.HIDE_ENCHANTS);
|
||||
meta.addItemFlags(ItemFlag.HIDE_POTION_EFFECTS);
|
||||
icon.setItemMeta(meta);
|
||||
}
|
||||
|
||||
|
@ -89,7 +88,6 @@ public class PanelItem {
|
|||
this.name = name;
|
||||
if (meta != null) {
|
||||
meta.setDisplayName(name);
|
||||
meta.setLocalizedName(name); //Localized name cannot be overridden by the player using an anvils
|
||||
icon.setItemMeta(meta);
|
||||
}
|
||||
}
|
||||
|
@ -135,9 +133,9 @@ public class PanelItem {
|
|||
}
|
||||
if (meta != null) {
|
||||
if (glow) {
|
||||
meta.addEnchant(Enchantment.ARROW_DAMAGE, 0, glow);
|
||||
meta.addEnchant(Enchantment.LURE, 0, glow);
|
||||
} else {
|
||||
meta.removeEnchant(Enchantment.ARROW_DAMAGE);
|
||||
meta.removeEnchant(Enchantment.LURE);
|
||||
}
|
||||
icon.setItemMeta(meta);
|
||||
|
||||
|
|
|
@ -74,6 +74,7 @@ public class TabbedPanel extends Panel implements PanelListener {
|
|||
* @param page - the page of the tab to show (if multi paged)
|
||||
*/
|
||||
public void openPanel(int activeTab, int page) {
|
||||
|
||||
if (!tpb.getTabs().containsKey(activeTab)) {
|
||||
// Request to open a non-existent tab
|
||||
throw new InvalidParameterException("Attempt to open a non-existent tab in a tabbed panel. Missing tab #" + activeTab);
|
||||
|
@ -88,21 +89,17 @@ public class TabbedPanel extends Panel implements PanelListener {
|
|||
TreeMap<Integer, PanelItem> items = new TreeMap<>();
|
||||
// Get the tab
|
||||
Tab tab = tpb.getTabs().get(activeTab);
|
||||
|
||||
// Remove any tabs that have no items, if required
|
||||
if (tpb.isHideIfEmpty()) {
|
||||
tpb.getTabs().values().removeIf(t -> !t.equals(tab) && t.getPanelItems().stream().noneMatch(Objects::nonNull));
|
||||
}
|
||||
|
||||
// Set up the tabbed header
|
||||
setupHeader(tab, items);
|
||||
|
||||
// Show the active tab
|
||||
if (tpb.getTabs().containsKey(activeTab)) {
|
||||
List<PanelItem> panelItems = tab.getPanelItems();
|
||||
// Adds the flag items
|
||||
panelItems.stream().filter(Objects::nonNull).skip(page * ITEMS_PER_PAGE).limit(page * ITEMS_PER_PAGE + ITEMS_PER_PAGE).forEach(i -> items.put(items.lastKey() + 1, i));
|
||||
|
||||
// set up the footer
|
||||
setupFooter(items);
|
||||
// Add forward and backward icons
|
||||
|
@ -182,6 +179,7 @@ public class TabbedPanel extends Panel implements PanelListener {
|
|||
// Reset the closed flag
|
||||
closed = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -14,6 +14,7 @@ import java.util.UUID;
|
|||
import org.apache.commons.lang.math.NumberUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Color;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
|
@ -33,6 +34,8 @@ import org.bukkit.util.Vector;
|
|||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import com.google.common.base.Enums;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.api.addons.Addon;
|
||||
import world.bentobox.bentobox.api.events.OfflineMessageEvent;
|
||||
|
@ -62,19 +65,23 @@ public class User implements MetaDataAble {
|
|||
private static final Map<Particle, Class<?>> VALIDATION_CHECK;
|
||||
static {
|
||||
Map<Particle, Class<?>> v = new EnumMap<>(Particle.class);
|
||||
v.put(Particle.REDSTONE, Particle.DustOptions.class);
|
||||
v.put(Particle.ITEM_CRACK, ItemStack.class);
|
||||
v.put(Particle.BLOCK_CRACK, BlockData.class);
|
||||
v.put(Particle.BLOCK_DUST, BlockData.class);
|
||||
v.put(Enums.getIfPresent(Particle.class, "DUST")
|
||||
.or(Enums.getIfPresent(Particle.class, "REDSTONE").or(Particle.FLAME)), Particle.DustOptions.class);
|
||||
if (Enums.getIfPresent(Particle.class, "ITEM").isPresent()) {
|
||||
// 1.20.6 Particles
|
||||
v.put(Particle.ITEM, ItemStack.class);
|
||||
v.put(Particle.ITEM_COBWEB, ItemStack.class);
|
||||
v.put(Particle.BLOCK, BlockData.class);
|
||||
v.put(Particle.DUST_PILLAR, BlockData.class);
|
||||
v.put(Particle.ENTITY_EFFECT, Color.class);
|
||||
}
|
||||
v.put(Particle.FALLING_DUST, BlockData.class);
|
||||
v.put(Particle.BLOCK_MARKER, BlockData.class);
|
||||
v.put(Particle.DUST_COLOR_TRANSITION, DustTransition.class);
|
||||
v.put(Particle.VIBRATION, Vibration.class);
|
||||
v.put(Particle.SCULK_CHARGE, Float.class);
|
||||
v.put(Particle.SHRIEK, Integer.class);
|
||||
v.put(Particle.LEGACY_BLOCK_CRACK, BlockData.class);
|
||||
v.put(Particle.LEGACY_BLOCK_DUST, BlockData.class);
|
||||
v.put(Particle.LEGACY_FALLING_DUST, BlockData.class);
|
||||
|
||||
VALIDATION_CHECK = Collections.unmodifiableMap(v);
|
||||
}
|
||||
|
||||
|
@ -711,8 +718,7 @@ public class User implements MetaDataAble {
|
|||
* 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 dustOptions Particle.DustOptions for the particle to display.
|
||||
* @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.
|
||||
|
@ -730,7 +736,8 @@ public class User implements MetaDataAble {
|
|||
// 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 (particle.equals(Enums.getIfPresent(Particle.class, "DUST")
|
||||
.or(Enums.getIfPresent(Particle.class, "REDSTONE").or(Particle.FLAME)))) {
|
||||
player.spawnParticle(particle, x, y, z, 1, 0, 0, 0, 1, dustOptions);
|
||||
} else if (dustOptions != null) {
|
||||
player.spawnParticle(particle, x, y, z, 1, dustOptions);
|
||||
|
@ -747,8 +754,7 @@ public class User implements MetaDataAble {
|
|||
* 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 dustOptions Particle.DustOptions for the particle to display.
|
||||
* @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.
|
||||
|
@ -762,8 +768,7 @@ public class User implements MetaDataAble {
|
|||
* 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 dustOptions Particle.DustOptions for the particle to display.
|
||||
* @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.
|
||||
|
|
|
@ -8,6 +8,7 @@ import java.util.LinkedHashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
|
@ -43,6 +44,7 @@ import world.bentobox.bentobox.api.user.User;
|
|||
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBlock;
|
||||
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintCreatureSpawner;
|
||||
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity;
|
||||
import world.bentobox.bentobox.hooks.MythicMobsHook;
|
||||
|
||||
/**
|
||||
* The clipboard provides the holding spot for an active blueprint that is being
|
||||
|
@ -67,6 +69,7 @@ public class BlueprintClipboard {
|
|||
private final Map<Vector, BlueprintBlock> bpAttachable = new LinkedHashMap<>();
|
||||
private final Map<Vector, BlueprintBlock> bpBlocks = new LinkedHashMap<>();
|
||||
private final BentoBox plugin = BentoBox.getInstance();
|
||||
private Optional<MythicMobsHook> mmh;
|
||||
|
||||
/**
|
||||
* Create a clipboard for blueprint
|
||||
|
@ -74,9 +77,16 @@ public class BlueprintClipboard {
|
|||
*/
|
||||
public BlueprintClipboard(@NonNull Blueprint blueprint) {
|
||||
this.blueprint = blueprint;
|
||||
// MythicMobs
|
||||
mmh = plugin.getHooks().getHook("MythicMobs").filter(MythicMobsHook.class::isInstance)
|
||||
.map(MythicMobsHook.class::cast);
|
||||
}
|
||||
|
||||
public BlueprintClipboard() { }
|
||||
public BlueprintClipboard() {
|
||||
// MythicMobs
|
||||
mmh = plugin.getHooks().getHook("MythicMobs").filter(MythicMobsHook.class::isInstance)
|
||||
.map(MythicMobsHook.class::cast);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the blocks between pos1 and pos2 into the clipboard for a user.
|
||||
|
@ -285,6 +295,7 @@ public class BlueprintClipboard {
|
|||
List<BlueprintEntity> bpEnts = new ArrayList<>();
|
||||
for (LivingEntity entity: entities) {
|
||||
BlueprintEntity bpe = new BlueprintEntity();
|
||||
|
||||
bpe.setType(entity.getType());
|
||||
bpe.setCustomName(entity.getCustomName());
|
||||
if (entity instanceof Villager villager) {
|
||||
|
@ -317,6 +328,10 @@ public class BlueprintClipboard {
|
|||
if (entity instanceof Horse horse) {
|
||||
bpe.setStyle(horse.getStyle());
|
||||
}
|
||||
|
||||
mmh.filter(mm -> mm.isMythicMob(entity)).map(mm -> mm.getMythicMob(entity))
|
||||
.ifPresent(bpe::setMythicMobsRecord);
|
||||
|
||||
bpEnts.add(bpe);
|
||||
}
|
||||
return bpEnts;
|
||||
|
|
|
@ -119,6 +119,9 @@ public class BlueprintPaster {
|
|||
Vector off = bp.getBedrock() != null ? bp.getBedrock() : new Vector(0,0,0);
|
||||
// Calculate location for pasting
|
||||
this.location = island.getProtectionCenter().toVector().subtract(off).toLocation(world);
|
||||
// Ensure the y coordinate is within the world limits
|
||||
int y = Math.min(world.getMaxHeight() - 1, Math.max(world.getMinHeight(), location.getBlockY()));
|
||||
location.setY(y);
|
||||
}
|
||||
|
||||
private record Bits(Map<Vector, BlueprintBlock> blocks,
|
||||
|
@ -237,25 +240,7 @@ public class BlueprintPaster {
|
|||
private void pasteBlocks(Bits bits, int count, Optional<User> owner, int pasteSpeed, boolean useNMS) {
|
||||
Iterator<Entry<Vector, BlueprintBlock>> it = pasteState.equals(PasteState.BLOCKS) ? bits.it : bits.it2;
|
||||
if (it.hasNext()) {
|
||||
Map<Location, BlueprintBlock> blockMap = new HashMap<>();
|
||||
// Paste blocks
|
||||
while (count < pasteSpeed) {
|
||||
if (!it.hasNext()) {
|
||||
break;
|
||||
}
|
||||
Entry<Vector, BlueprintBlock> entry = it.next();
|
||||
Location pasteTo = location.clone().add(entry.getKey());
|
||||
// pos1 and pos2 update
|
||||
updatePos(pasteTo);
|
||||
|
||||
BlueprintBlock block = entry.getValue();
|
||||
blockMap.put(pasteTo, block);
|
||||
count++;
|
||||
}
|
||||
if (!blockMap.isEmpty()) {
|
||||
currentTask = useNMS ? paster.pasteBlocks(island, world, blockMap)
|
||||
: fallback.pasteBlocks(island, world, blockMap);
|
||||
}
|
||||
pasteBlocksNow(it, count, pasteSpeed, useNMS);
|
||||
} else {
|
||||
if (pasteState.equals(PasteState.BLOCKS)) {
|
||||
// Blocks done
|
||||
|
@ -272,6 +257,29 @@ public class BlueprintPaster {
|
|||
|
||||
}
|
||||
|
||||
private void pasteBlocksNow(Iterator<Entry<Vector, BlueprintBlock>> it, int count, int pasteSpeed, boolean useNMS) {
|
||||
Map<Location, BlueprintBlock> blockMap = new HashMap<>();
|
||||
// Paste blocks
|
||||
while (count < pasteSpeed) {
|
||||
if (!it.hasNext()) {
|
||||
break;
|
||||
}
|
||||
Entry<Vector, BlueprintBlock> entry = it.next();
|
||||
Location pasteTo = location.clone().add(entry.getKey());
|
||||
// pos1 and pos2 update
|
||||
updatePos(pasteTo);
|
||||
|
||||
BlueprintBlock block = entry.getValue();
|
||||
blockMap.put(pasteTo, block);
|
||||
count++;
|
||||
}
|
||||
if (!blockMap.isEmpty()) {
|
||||
currentTask = useNMS ? paster.pasteBlocks(island, world, blockMap)
|
||||
: fallback.pasteBlocks(island, world, blockMap);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void loadChunk() {
|
||||
long timer = System.currentTimeMillis();
|
||||
pasteState = PasteState.CHUNK_LOADING;
|
||||
|
|
|
@ -61,6 +61,11 @@ public class BlueprintBundle implements DataObject {
|
|||
@Expose
|
||||
private int slot = 0;
|
||||
|
||||
/**
|
||||
* Number of times this bundle can be used by a single player. 0 = unlimited
|
||||
*/
|
||||
@Expose
|
||||
private int times = 0;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -188,4 +193,18 @@ public class BlueprintBundle implements DataObject {
|
|||
this.slot = slot;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the times
|
||||
*/
|
||||
public int getTimes() {
|
||||
return times;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param times the times to set
|
||||
*/
|
||||
public void setTimes(int times) {
|
||||
this.times = times;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,6 +24,19 @@ import com.google.gson.annotations.Expose;
|
|||
*/
|
||||
public class BlueprintEntity {
|
||||
|
||||
public record MythicMobRecord(String type, String displayName, double level, float power, String stance) {
|
||||
}
|
||||
|
||||
// GSON can serialize records, but the record class needs to be know in advance. So this breaks out the record entries
|
||||
@Expose
|
||||
String MMtype;
|
||||
@Expose
|
||||
Double MMLevel;
|
||||
@Expose
|
||||
String MMStance;
|
||||
@Expose
|
||||
Float MMpower;
|
||||
|
||||
@Expose
|
||||
private DyeColor color;
|
||||
@Expose
|
||||
|
@ -51,7 +64,6 @@ public class BlueprintEntity {
|
|||
@Expose
|
||||
private Villager.Type villagerType;
|
||||
|
||||
|
||||
/**
|
||||
* @since 1.8.0
|
||||
*/
|
||||
|
@ -85,7 +97,6 @@ public class BlueprintEntity {
|
|||
if (style != null && e instanceof Horse horse) {
|
||||
horse.setStyle(style);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -271,4 +282,26 @@ public class BlueprintEntity {
|
|||
this.domestication = domestication;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the mythicMobsRecord
|
||||
*/
|
||||
public MythicMobRecord getMythicMobsRecord() {
|
||||
if (this.MMtype == null || this.MMLevel == null || this.MMpower == null || this.MMStance == null) {
|
||||
return null;
|
||||
}
|
||||
return new MythicMobRecord(this.MMtype, this.getCustomName(), this.MMLevel, this.MMpower, this.MMStance);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mmr the mythicMobsRecord to set
|
||||
* @since 2.1.0
|
||||
*/
|
||||
public void setMythicMobsRecord(MythicMobRecord mmr) {
|
||||
this.setCustomName(mmr.displayName());
|
||||
this.MMtype = mmr.type();
|
||||
this.MMLevel = mmr.level();
|
||||
this.MMStance = mmr.stance();
|
||||
this.MMpower = mmr.power();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ public class BentoBoxAboutCommand extends CompositeCommand {
|
|||
@Override
|
||||
public boolean execute(User user, String label, List<String> args) {
|
||||
user.sendRawMessage("About " + BentoBox.getInstance().getDescription().getName() + " v" + BentoBox.getInstance().getDescription().getVersion() + ":");
|
||||
user.sendRawMessage("Copyright (c) 2017 - 2023 Tastybento, Poslovitch and the BentoBoxWorld contributors");
|
||||
user.sendRawMessage("Copyright (c) 2017 - 2024 Tastybento, Poslovitch and the BentoBoxWorld contributors");
|
||||
user.sendRawMessage("See https://www.eclipse.org/legal/epl-2.0/ for license information.");
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import java.util.Objects;
|
|||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
|
@ -41,6 +42,7 @@ import world.bentobox.bentobox.api.user.User;
|
|||
import world.bentobox.bentobox.database.objects.adapters.Adapter;
|
||||
import world.bentobox.bentobox.database.objects.adapters.LogEntryListAdapter;
|
||||
import world.bentobox.bentobox.lists.Flags;
|
||||
import world.bentobox.bentobox.managers.IslandsManager;
|
||||
import world.bentobox.bentobox.managers.RanksManager;
|
||||
import world.bentobox.bentobox.util.Pair;
|
||||
import world.bentobox.bentobox.util.Util;
|
||||
|
@ -56,7 +58,7 @@ import world.bentobox.bentobox.util.Util;
|
|||
public class Island implements DataObject, MetaDataAble {
|
||||
|
||||
@Expose
|
||||
private boolean primary;
|
||||
private Set<UUID> primaries = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Set to true if this data object has been changed since being loaded from the
|
||||
|
@ -243,7 +245,6 @@ public class Island implements DataObject, MetaDataAble {
|
|||
range = BentoBox.getInstance().getIWM().getIslandDistance(world);
|
||||
this.protectionRange = protectionRange;
|
||||
this.maxEverProtectionRange = protectionRange;
|
||||
this.setChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -290,6 +291,7 @@ public class Island implements DataObject, MetaDataAble {
|
|||
this.updatedDate = island.getUpdatedDate();
|
||||
this.world = island.getWorld();
|
||||
this.bonusRanges.addAll(island.getBonusRanges());
|
||||
this.primaries.addAll(island.getPrimaries());
|
||||
this.setChanged();
|
||||
}
|
||||
|
||||
|
@ -304,8 +306,10 @@ public class Island implements DataObject, MetaDataAble {
|
|||
* @param playerUUID - the player's UUID
|
||||
*/
|
||||
public void addMember(@NonNull UUID playerUUID) {
|
||||
setRank(playerUUID, RanksManager.MEMBER_RANK);
|
||||
setChanged();
|
||||
if (getRank(playerUUID) != RanksManager.MEMBER_RANK) {
|
||||
setRank(playerUUID, RanksManager.MEMBER_RANK);
|
||||
setChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -320,9 +324,12 @@ public class Island implements DataObject, MetaDataAble {
|
|||
* @return {@code true}
|
||||
*/
|
||||
public boolean ban(@NonNull UUID issuer, @NonNull UUID target) {
|
||||
setRank(target, RanksManager.BANNED_RANK);
|
||||
log(new LogEntry.Builder("BAN").data("player", target.toString()).data("issuer", issuer.toString()).build());
|
||||
setChanged();
|
||||
if (getRank(target) != RanksManager.BANNED_RANK) {
|
||||
setRank(target, RanksManager.BANNED_RANK);
|
||||
log(new LogEntry.Builder("BAN").data("player", target.toString()).data("issuer", issuer.toString())
|
||||
.build());
|
||||
setChanged();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1005,25 +1012,30 @@ public class Island implements DataObject, MetaDataAble {
|
|||
* @param playerUUID - uuid of player
|
||||
*/
|
||||
public void removeMember(UUID playerUUID) {
|
||||
members.remove(playerUUID);
|
||||
setChanged();
|
||||
if (members.remove(playerUUID) != null) {
|
||||
setChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param center the center to set
|
||||
*/
|
||||
public void setCenter(@NonNull Location center) {
|
||||
this.world = center.getWorld();
|
||||
this.center = center;
|
||||
setChanged();
|
||||
if (this.center == null || !center.getWorld().equals(this.center.getWorld()) || !center.equals(this.center)) {
|
||||
this.world = center.getWorld();
|
||||
this.center = center;
|
||||
setChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param createdDate - the createdDate to sets
|
||||
*/
|
||||
public void setCreatedDate(long createdDate) {
|
||||
this.createdDate = createdDate;
|
||||
setChanged();
|
||||
if (this.createdDate != createdDate) {
|
||||
this.createdDate = createdDate;
|
||||
setChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1032,27 +1044,30 @@ public class Island implements DataObject, MetaDataAble {
|
|||
*
|
||||
* @param flag - flag
|
||||
* @param value - Use RanksManager settings, e.g. RanksManager.MEMBER
|
||||
* @return this island
|
||||
*/
|
||||
public void setFlag(Flag flag, int value) {
|
||||
public Island setFlag(Flag flag, int value) {
|
||||
setFlag(flag, value, true);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Island Guard flag rank Also specify whether subflags are affected by
|
||||
* this method call
|
||||
* Set the Island Guard flag rank and set any subflags
|
||||
*
|
||||
* @param flag - flag
|
||||
* @param value - Use RanksManager settings, e.g. RanksManager.MEMBER
|
||||
* @param doSubflags - whether to set subflags
|
||||
*/
|
||||
public void setFlag(Flag flag, int value, boolean doSubflags) {
|
||||
flags.put(flag.getID(), value);
|
||||
if (flags.containsKey(flag.getID()) && flags.get(flag.getID()) != value) {
|
||||
flags.put(flag.getID(), value);
|
||||
setChanged();
|
||||
}
|
||||
// Subflag support
|
||||
if (doSubflags && flag.hasSubflags()) {
|
||||
// Ensure that a subflag isn't a subflag of itself or else we're in trouble!
|
||||
flag.getSubflags().forEach(subflag -> setFlag(subflag, value, true));
|
||||
}
|
||||
setChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1066,9 +1081,10 @@ public class Island implements DataObject, MetaDataAble {
|
|||
/**
|
||||
* Resets the flags to their default as set in config.yml for this island. If
|
||||
* flags are missing from the config, the default hard-coded value is used and
|
||||
* set
|
||||
* set.
|
||||
* @return this island
|
||||
*/
|
||||
public void setFlagsDefaults() {
|
||||
public Island setFlagsDefaults() {
|
||||
BentoBox plugin = BentoBox.getInstance();
|
||||
Map<String, Integer> result = new HashMap<>();
|
||||
plugin.getFlagsManager().getFlags().stream().filter(f -> f.getType().equals(Flag.Type.PROTECTION))
|
||||
|
@ -1077,8 +1093,8 @@ public class Island implements DataObject, MetaDataAble {
|
|||
plugin.getFlagsManager().getFlags().stream().filter(f -> f.getType().equals(Flag.Type.SETTING))
|
||||
.forEach(f -> result.put(f.getID(),
|
||||
plugin.getIWM().getDefaultIslandSettings(world).getOrDefault(f, f.getDefaultRank())));
|
||||
this.setFlags(result);
|
||||
setChanged();
|
||||
setFlags(result);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1097,8 +1113,10 @@ public class Island implements DataObject, MetaDataAble {
|
|||
* @param name The display name to set.
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = (name != null && !name.equals("")) ? name : null;
|
||||
setChanged();
|
||||
if (name == null || !name.equals(this.name)) {
|
||||
this.name = (name != null && !name.equals("")) ? name : null;
|
||||
setChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1130,9 +1148,11 @@ public class Island implements DataObject, MetaDataAble {
|
|||
* @param protectionRange the protectionRange to set
|
||||
*/
|
||||
public void setProtectionRange(int protectionRange) {
|
||||
this.protectionRange = protectionRange;
|
||||
this.updateMaxEverProtectionRange();
|
||||
setChanged();
|
||||
if (this.protectionRange != protectionRange) {
|
||||
this.protectionRange = protectionRange;
|
||||
this.updateMaxEverProtectionRange();
|
||||
setChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1164,8 +1184,10 @@ public class Island implements DataObject, MetaDataAble {
|
|||
* @param purgeProtected - if the island is protected from the Purge
|
||||
*/
|
||||
public void setPurgeProtected(boolean purgeProtected) {
|
||||
this.purgeProtected = purgeProtected;
|
||||
setChanged();
|
||||
if (this.purgeProtected != purgeProtected) {
|
||||
this.purgeProtected = purgeProtected;
|
||||
setChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1179,8 +1201,10 @@ public class Island implements DataObject, MetaDataAble {
|
|||
* @see #setProtectionRange(int)
|
||||
*/
|
||||
public void setRange(int range) {
|
||||
this.range = range;
|
||||
setChanged();
|
||||
if (this.range != range) {
|
||||
this.range = range;
|
||||
setChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1191,7 +1215,6 @@ public class Island implements DataObject, MetaDataAble {
|
|||
*/
|
||||
public void setRank(User user, int rank) {
|
||||
setRank(user.getUniqueId(), rank);
|
||||
setChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1199,17 +1222,36 @@ public class Island implements DataObject, MetaDataAble {
|
|||
* the {@link world.bentobox.bentobox.api.events.island.IslandRankChangeEvent}.
|
||||
*
|
||||
* @param uuid UUID of the player
|
||||
* @param rank rank value
|
||||
* @param newRank rank value
|
||||
* @since 1.1
|
||||
*/
|
||||
public void setRank(@Nullable UUID uuid, int rank) {
|
||||
public void setRank(@Nullable UUID uuid, int newRank) {
|
||||
// Early return if the UUID is null, to avoid unnecessary processing.
|
||||
if (uuid == null) {
|
||||
return; // Defensive code
|
||||
return;
|
||||
}
|
||||
|
||||
// Use an AtomicBoolean to track if the member's rank has been changed.
|
||||
AtomicBoolean isRankChanged = new AtomicBoolean(false);
|
||||
|
||||
// Attempt to update the member's rank, if necessary.
|
||||
members.compute(uuid, (key, existingRank) -> {
|
||||
// If the member does not exist or their rank is different, update the rank.
|
||||
if (existingRank == null || existingRank != newRank) {
|
||||
isRankChanged.set(true);
|
||||
return newRank; // Update the rank.
|
||||
}
|
||||
// No change needed; return the existing rank.
|
||||
return existingRank;
|
||||
});
|
||||
|
||||
// If the rank was changed, notify the change and log the update.
|
||||
if (isRankChanged.get()) {
|
||||
setChanged(); // Notify that a change has occurred.
|
||||
}
|
||||
members.put(uuid, rank);
|
||||
setChanged();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param ranks the ranks to set
|
||||
*/
|
||||
|
@ -1266,7 +1308,6 @@ public class Island implements DataObject, MetaDataAble {
|
|||
@Override
|
||||
public void setUniqueId(String uniqueId) {
|
||||
this.uniqueId = uniqueId;
|
||||
setChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1274,7 +1315,6 @@ public class Island implements DataObject, MetaDataAble {
|
|||
*/
|
||||
public void setUpdatedDate(long updatedDate) {
|
||||
this.updatedDate = updatedDate;
|
||||
setChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1347,8 +1387,13 @@ public class Island implements DataObject, MetaDataAble {
|
|||
* @param l - location
|
||||
*/
|
||||
public void setSpawnPoint(Environment islandType, Location l) {
|
||||
spawnPoint.put(islandType, l);
|
||||
setChanged();
|
||||
spawnPoint.compute(islandType, (key, value) -> {
|
||||
if (value == null || !value.equals(l)) {
|
||||
setChanged(); // Call setChanged only if the value is updated.
|
||||
return l;
|
||||
}
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1368,8 +1413,9 @@ public class Island implements DataObject, MetaDataAble {
|
|||
* @param rank rank value
|
||||
*/
|
||||
public void removeRank(Integer rank) {
|
||||
members.values().removeIf(rank::equals);
|
||||
setChanged();
|
||||
if (members.values().removeIf(rank::equals)) {
|
||||
setChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1455,7 +1501,6 @@ public class Island implements DataObject, MetaDataAble {
|
|||
*/
|
||||
public void setGameMode(String gameMode) {
|
||||
this.gameMode = gameMode;
|
||||
setChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1518,8 +1563,9 @@ public class Island implements DataObject, MetaDataAble {
|
|||
if (cooldowns.containsKey(flag.getID()) && cooldowns.get(flag.getID()) > System.currentTimeMillis()) {
|
||||
return true;
|
||||
}
|
||||
cooldowns.remove(flag.getID());
|
||||
setChanged();
|
||||
if (cooldowns.remove(flag.getID()) != null) {
|
||||
setChanged();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1603,8 +1649,13 @@ public class Island implements DataObject, MetaDataAble {
|
|||
public void setRankCommand(String command, int rank) {
|
||||
if (this.commandRanks == null)
|
||||
this.commandRanks = new HashMap<>();
|
||||
this.commandRanks.put(command, rank);
|
||||
setChanged();
|
||||
commandRanks.compute(command, (key, value) -> {
|
||||
if (value == null || !value.equals(rank)) {
|
||||
setChanged(); // Call setChanged only if the value is updated.
|
||||
return rank;
|
||||
}
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1624,8 +1675,10 @@ public class Island implements DataObject, MetaDataAble {
|
|||
* @since 1.6.0
|
||||
*/
|
||||
public void setReserved(boolean reserved) {
|
||||
this.reserved = reserved;
|
||||
setChanged();
|
||||
if (this.reserved != reserved) {
|
||||
this.reserved = reserved;
|
||||
setChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1658,17 +1711,19 @@ public class Island implements DataObject, MetaDataAble {
|
|||
}
|
||||
|
||||
/**
|
||||
* Indicates the fields have been changed. Used to optimize saving on shutdown.
|
||||
* Indicates the fields have been changed. Used to optimize saving on shutdown and notify other servers
|
||||
*/
|
||||
public void setChanged() {
|
||||
this.setUpdatedDate(System.currentTimeMillis());
|
||||
this.changed = true;
|
||||
IslandsManager.updateIsland(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param changed the changed to set
|
||||
* Resets the changed if the island has been saved
|
||||
*/
|
||||
public void setChanged(boolean changed) {
|
||||
this.changed = changed;
|
||||
public void clearChanged() {
|
||||
this.changed = false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1692,6 +1747,9 @@ public class Island implements DataObject, MetaDataAble {
|
|||
* @since 1.16.0
|
||||
*/
|
||||
public void setProtectionCenter(Location location) throws IOException {
|
||||
if (this.location.equals(location)) {
|
||||
return; // nothing to do
|
||||
}
|
||||
if (!this.inIslandSpace(location)) {
|
||||
throw new IOException("Location must be in island space");
|
||||
}
|
||||
|
@ -1741,6 +1799,9 @@ public class Island implements DataObject, MetaDataAble {
|
|||
* @since 1.16.0
|
||||
*/
|
||||
public void addHome(String name, Location location) {
|
||||
if (getHomes().containsKey(name) && getHomes().get(name).equals(location)) {
|
||||
return; // nothing to do
|
||||
}
|
||||
if (location != null) {
|
||||
Vector v = location.toVector();
|
||||
if (!this.getBoundingBox().contains(v)) {
|
||||
|
@ -1763,8 +1824,11 @@ public class Island implements DataObject, MetaDataAble {
|
|||
* @since 1.16.0
|
||||
*/
|
||||
public boolean removeHome(String name) {
|
||||
setChanged();
|
||||
return getHomes().remove(name.toLowerCase()) != null;
|
||||
if (getHomes().remove(name.toLowerCase()) != null) {
|
||||
setChanged();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1774,8 +1838,11 @@ public class Island implements DataObject, MetaDataAble {
|
|||
* @since 1.20.0
|
||||
*/
|
||||
public boolean removeHomes() {
|
||||
setChanged();
|
||||
return getHomes().keySet().removeIf(k -> !k.isEmpty());
|
||||
if (getHomes().keySet().removeIf(k -> !k.isEmpty())) {
|
||||
setChanged();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1814,8 +1881,10 @@ public class Island implements DataObject, MetaDataAble {
|
|||
* @since 1.16.0
|
||||
*/
|
||||
public void setMaxHomes(@Nullable Integer maxHomes) {
|
||||
this.maxHomes = maxHomes;
|
||||
setChanged();
|
||||
if (this.maxHomes != maxHomes) {
|
||||
this.maxHomes = maxHomes;
|
||||
setChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1834,8 +1903,10 @@ public class Island implements DataObject, MetaDataAble {
|
|||
* @since 1.16.0
|
||||
*/
|
||||
public void setMaxMembers(Map<Integer, Integer> maxMembers) {
|
||||
this.maxMembers = maxMembers;
|
||||
setChanged();
|
||||
if (this.maxMembers != maxMembers) {
|
||||
this.maxMembers = maxMembers;
|
||||
setChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1860,7 +1931,13 @@ public class Island implements DataObject, MetaDataAble {
|
|||
* @since 1.16.0
|
||||
*/
|
||||
public void setMaxMembers(int rank, Integer maxMembers) {
|
||||
getMaxMembers().put(rank, maxMembers);
|
||||
getMaxMembers().compute(rank, (key, value) -> {
|
||||
if (value == null || !value.equals(maxMembers)) {
|
||||
setChanged(); // Call setChanged only if the value is updated.
|
||||
return maxMembers;
|
||||
}
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1923,8 +2000,9 @@ public class Island implements DataObject, MetaDataAble {
|
|||
* @param id id to identify this bonus
|
||||
*/
|
||||
public void clearBonusRange(String id) {
|
||||
this.getBonusRanges().removeIf(r -> r.getUniqueId().equals(id));
|
||||
setChanged();
|
||||
if (this.getBonusRanges().removeIf(r -> r.getUniqueId().equals(id))) {
|
||||
setChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1936,18 +2014,50 @@ public class Island implements DataObject, MetaDataAble {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param userID user UUID
|
||||
* @return the primary
|
||||
*/
|
||||
public boolean isPrimary() {
|
||||
return primary;
|
||||
public boolean isPrimary(UUID userID) {
|
||||
return getPrimaries().contains(userID);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param primary the primary to set
|
||||
* Set this island to be the primary for this user
|
||||
* @param userID user UUID
|
||||
*/
|
||||
public void setPrimary(boolean primary) {
|
||||
this.primary = primary;
|
||||
setChanged();
|
||||
public void setPrimary(UUID userID) {
|
||||
if (getPrimaries().add(userID)) {
|
||||
setChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the primary island
|
||||
* @param userID user UUID
|
||||
*/
|
||||
public void removePrimary(UUID userID) {
|
||||
if (getPrimaries().remove(userID)) {
|
||||
setChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a player is in this island's team
|
||||
* @param playerUUID player's UUID
|
||||
* @return true if in team
|
||||
* @since 2.3.0
|
||||
*/
|
||||
public boolean inTeam(UUID playerUUID) {
|
||||
return this.getMemberSet().contains(playerUUID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this island has a team
|
||||
* @return true if this island has a team
|
||||
* @since 2.3.0
|
||||
*/
|
||||
public boolean hasTeam() {
|
||||
return this.getMemberSet().size() > 1;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1967,4 +2077,41 @@ public class Island implements DataObject, MetaDataAble {
|
|||
+ commandRanks + ", reserved=" + reserved + ", metaData=" + metaData + ", homes=" + homes
|
||||
+ ", maxHomes=" + maxHomes + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the primaries
|
||||
*/
|
||||
public Set<UUID> getPrimaries() {
|
||||
if (primaries == null) {
|
||||
primaries = new HashSet<>();
|
||||
}
|
||||
return primaries;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param primaries the primaries to set
|
||||
*/
|
||||
public void setPrimaries(Set<UUID> primaries) {
|
||||
this.primaries = primaries;
|
||||
setChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(uniqueId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
Island other = (Island) obj;
|
||||
return Objects.equals(uniqueId, other.uniqueId);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -6,13 +6,10 @@ import java.util.Map;
|
|||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
|
||||
|
@ -30,8 +27,6 @@ import world.bentobox.bentobox.util.Util;
|
|||
*/
|
||||
@Table(name = "Players")
|
||||
public class Players implements DataObject, MetaDataAble {
|
||||
@Expose
|
||||
private Map<Location, Integer> homeLocations = new HashMap<>();
|
||||
@Expose
|
||||
private String uniqueId;
|
||||
@Expose
|
||||
|
@ -77,7 +72,6 @@ public class Players implements DataObject, MetaDataAble {
|
|||
*/
|
||||
public Players(BentoBox plugin, UUID uniqueId) {
|
||||
this.uniqueId = uniqueId.toString();
|
||||
homeLocations = new HashMap<>();
|
||||
locale = "";
|
||||
// Try to get player's name
|
||||
this.playerName = Bukkit.getOfflinePlayer(uniqueId).getName();
|
||||
|
@ -86,72 +80,6 @@ public class Players implements DataObject, MetaDataAble {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default home location.
|
||||
* @param world - world to check
|
||||
* @return Location - home location in world
|
||||
* @deprecated Homes are stored in the Island object now
|
||||
*/
|
||||
@Deprecated(since="1.18.0", forRemoval=true)
|
||||
@Nullable
|
||||
public Location getHomeLocation(World world) {
|
||||
return getHomeLocation(world, 1); // Default
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the home location by number for world
|
||||
* @param world - includes world and any related nether or end worlds
|
||||
* @param number - a number
|
||||
* @return Location of this home or null if not available
|
||||
* @deprecated Homes are stored in the island object now
|
||||
*/
|
||||
@Deprecated(since="1.18.0", forRemoval=true)
|
||||
@Nullable
|
||||
public Location getHomeLocation(World world, int number) {
|
||||
// Remove any lost worlds/locations
|
||||
homeLocations.keySet().removeIf(l -> l == null || l.getWorld() == null);
|
||||
return homeLocations.entrySet().stream()
|
||||
.filter(en -> Util.sameWorld(en.getKey().getWorld(), world) && en.getValue() == number)
|
||||
.map(Map.Entry::getKey)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param world - world
|
||||
* @return Map of home locations
|
||||
* @deprecated Homes are stored in the island object now
|
||||
*/
|
||||
@Deprecated(since="1.18.0", forRemoval=true)
|
||||
public Map<Location, Integer> getHomeLocations(World world) {
|
||||
// Remove any lost worlds/locations
|
||||
homeLocations.keySet().removeIf(l -> l == null || l.getWorld() == null);
|
||||
return homeLocations.entrySet().stream().filter(e -> Util.sameWorld(e.getKey().getWorld(),world))
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the homeLocations
|
||||
* @deprecated Homes are stored in the Island object now
|
||||
*/
|
||||
@Deprecated(since="1.18.0", forRemoval=true)
|
||||
public Map<Location, Integer> getHomeLocations() {
|
||||
// Remove any lost worlds/locations
|
||||
homeLocations.keySet().removeIf(l -> l == null || l.getWorld() == null);
|
||||
return homeLocations;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param homeLocations the homeLocations to set
|
||||
* @deprecated Homes are stored in the Island object now
|
||||
*/
|
||||
@Deprecated(since="1.18.0", forRemoval=true)
|
||||
public void setHomeLocations(Map<Location, Integer> homeLocations) {
|
||||
this.homeLocations = homeLocations;
|
||||
// Remove any lost worlds/locations
|
||||
homeLocations.keySet().removeIf(l -> l == null || l.getWorld() == null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param playerName the playerName to set
|
||||
*/
|
||||
|
@ -202,30 +130,6 @@ public class Players implements DataObject, MetaDataAble {
|
|||
this.resets.put(world.getName(), resets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the home location of the player in a String format
|
||||
*
|
||||
* @param l a Bukkit location
|
||||
* @deprecated Home locations are stored in islands
|
||||
*/
|
||||
@Deprecated(since="1.18.0", forRemoval=true)
|
||||
public void setHomeLocation(final Location l) {
|
||||
setHomeLocation(l, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the numbered home location of the player. Numbering starts at 1.
|
||||
* @param location - the location
|
||||
* @param number - a number
|
||||
* @deprecated Home locations are no longer stored for players. They are stored in islands.
|
||||
*/
|
||||
@Deprecated(since="1.18.0", forRemoval=true)
|
||||
public void setHomeLocation(Location location, int number) {
|
||||
// Remove any home locations in the same world with the same number
|
||||
homeLocations.entrySet().removeIf(e -> e.getKey() == null || (Util.sameWorld(location.getWorld(), e.getKey().getWorld()) && e.getValue().equals(number)));
|
||||
homeLocations.put(location, number);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the uuid for this player object
|
||||
* @param uuid - UUID
|
||||
|
@ -234,16 +138,6 @@ public class Players implements DataObject, MetaDataAble {
|
|||
uniqueId = uuid.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all home Locations in world
|
||||
* @param world - world
|
||||
* @deprecated Home locations are no longer stored for players. Use {@link world.bentobox.bentobox.managers.IslandsManager}
|
||||
*/
|
||||
@Deprecated(since="1.18.0", forRemoval=true)
|
||||
public void clearHomeLocations(World world) {
|
||||
homeLocations.keySet().removeIf(l -> l == null || l.getWorld() == null || Util.sameWorld(l.getWorld(), world));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the locale
|
||||
*/
|
||||
|
@ -350,24 +244,6 @@ public class Players implements DataObject, MetaDataAble {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the display mode for the Flags in the Settings Panel.
|
||||
* @return the display mode for the Flags in the Settings Panel.
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public Flag.Mode getFlagsDisplayMode() {
|
||||
return flagsDisplayMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the display mode for the Flags in the Settings Panel.
|
||||
* @param flagsDisplayMode the display mode for the Flags in the Settings Panel.
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public void setFlagsDisplayMode(Flag.Mode flagsDisplayMode) {
|
||||
this.flagsDisplayMode = flagsDisplayMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the metaData
|
||||
* @since 1.15.5
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
package world.bentobox.bentobox.database.objects;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
|
||||
/**
|
||||
* Data object for team invites
|
||||
*/
|
||||
@Table(name = "TeamInvites")
|
||||
public class TeamInvite implements DataObject {
|
||||
|
||||
/**
|
||||
* Type of invitation
|
||||
*
|
||||
*/
|
||||
public enum Type {
|
||||
COOP,
|
||||
TEAM,
|
||||
TRUST
|
||||
}
|
||||
|
||||
@Expose
|
||||
private Type type;
|
||||
@Expose
|
||||
private UUID inviter;
|
||||
@Expose
|
||||
private String islandID;
|
||||
|
||||
@Expose
|
||||
private String uniqueId;
|
||||
|
||||
/**
|
||||
* @param type - invitation type, e.g., coop, team, trust
|
||||
* @param inviter - UUID of inviter
|
||||
* @param invitee - UUID of invitee
|
||||
* @param islandID - the unique ID of the island this invite is for
|
||||
*/
|
||||
public TeamInvite(Type type, UUID inviter, UUID invitee, String islandID) {
|
||||
this.type = type;
|
||||
this.uniqueId = invitee.toString();
|
||||
this.inviter = inviter;
|
||||
this.islandID = islandID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUniqueId() {
|
||||
// Inviter
|
||||
return this.uniqueId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUniqueId(String uniqueId) {
|
||||
this.uniqueId = uniqueId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the type
|
||||
*/
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the invitee
|
||||
*/
|
||||
public UUID getInvitee() {
|
||||
return UUID.fromString(uniqueId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the inviter
|
||||
*/
|
||||
public UUID getInviter() {
|
||||
return inviter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the islandID
|
||||
*/
|
||||
public String getIslandID() {
|
||||
return islandID;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(inviter, uniqueId, type);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (!(obj instanceof TeamInvite other)) {
|
||||
return false;
|
||||
}
|
||||
return Objects.equals(inviter, other.inviter) && Objects.equals(uniqueId, other.getUniqueId())
|
||||
&& type == other.type;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,11 @@
|
|||
package world.bentobox.bentobox.hooks;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.EntityType;
|
||||
|
@ -8,6 +13,7 @@ import org.bukkit.entity.Player;
|
|||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.entity.EntityExplodeEvent;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import dev.lone.itemsadder.api.CustomBlock;
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
|
@ -22,6 +28,19 @@ 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
|
||||
*/
|
||||
/*
|
||||
* add some methods under CustomBlock#Advanced class.
|
||||
|
||||
public static void deleteAllCustomBlocksInChunk(Chunk chunk)
|
||||
|
||||
@Nullable
|
||||
public List<Location> getAllBlocksLocationsList(Chunk chunk)
|
||||
|
||||
@Nullable
|
||||
public Map<String, Location> getAllBlocksLocations(Chunk chunk)
|
||||
|
||||
public void runActionOnBlocks(Chunk chunk, BiConsumer<String, Location> action)
|
||||
*/
|
||||
public class ItemsAdderHook extends Hook {
|
||||
|
||||
/**
|
||||
|
@ -78,6 +97,24 @@ public class ItemsAdderHook extends Hook {
|
|||
// CustomBlock.remove(location);
|
||||
}
|
||||
|
||||
public static void deleteAllCustomBlocksInChunk(Chunk chunk) {
|
||||
CustomBlock.Advanced.deleteAllCustomBlocksInChunk(chunk);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public List<Location> getAllBlocksLocationsList(Chunk chunk) {
|
||||
return CustomBlock.Advanced.getAllBlocksLocationsList(chunk);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Map<Location, String> getAllBlocksLocations(Chunk chunk) {
|
||||
return CustomBlock.Advanced.getAllBlocksLocations(chunk);
|
||||
}
|
||||
|
||||
public void runActionOnBlocks(Chunk chunk, BiConsumer<String, Location> action) {
|
||||
CustomBlock.Advanced.runActionOnBlocks(chunk, action);
|
||||
}
|
||||
|
||||
class BlockInteractListener extends FlagListener {
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package world.bentobox.bentobox.hooks;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.bukkit.DyeColor;
|
||||
import org.bukkit.Material;
|
||||
|
@ -264,29 +266,48 @@ public class LangUtilsHook extends Hook {
|
|||
if (hooked) {
|
||||
return LanguageHelper.getPotionName(potionType, getUserLocale(user));
|
||||
}
|
||||
return generalPotionName(potionType);
|
||||
}
|
||||
|
||||
private static String generalPotionName(PotionType potionType) {
|
||||
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";
|
||||
default -> "Unknown Potion";
|
||||
case LONG_FIRE_RESISTANCE -> "Potion of Long Fire Resistance";
|
||||
case LONG_INVISIBILITY -> "Potion of Long Invisibility";
|
||||
case LONG_NIGHT_VISION -> "Potion of Long Night Vision";
|
||||
case LONG_POISON -> "Potion of Long Poison";
|
||||
case LONG_REGENERATION -> "Potion of Long Regeneration";
|
||||
case LONG_SLOWNESS -> "Potion of Long Slowness";
|
||||
case LONG_SLOW_FALLING -> "Potion of Long Slow Falling";
|
||||
case LONG_STRENGTH -> "Potion of Long Strength";
|
||||
case LONG_SWIFTNESS -> "Potion of Long Swiftness";
|
||||
case LONG_TURTLE_MASTER -> "Potion of Long Turtle Master";
|
||||
case LONG_WATER_BREATHING -> "Potion of Long Water Breathing";
|
||||
case LONG_WEAKNESS -> "Potion of Long Weakness";
|
||||
case STRONG_HARMING -> "Potion of Strong Harming";
|
||||
case STRONG_HEALING -> "Potion of Strong Healing";
|
||||
case STRONG_LEAPING -> "Potion of Strong Leaping";
|
||||
case STRONG_POISON -> "Potion of Strong Poison";
|
||||
case STRONG_REGENERATION -> "Potion of Strong Regeneration";
|
||||
case STRONG_SLOWNESS -> "Potion of Strong Slowness";
|
||||
case STRONG_STRENGTH -> "Potion of Strong Strength";
|
||||
case STRONG_SWIFTNESS -> "Potion of Swiftness";
|
||||
case STRONG_TURTLE_MASTER -> "Potion of Strong Turtle Master";
|
||||
default -> "Potion of " + Util.prettifyText(potionType.name());
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -302,30 +323,7 @@ public class LangUtilsHook extends Hook {
|
|||
if (hooked) {
|
||||
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";
|
||||
default -> "Unknown Splash Potion";
|
||||
};
|
||||
return "Splash" + generalPotionName(potionType);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -339,30 +337,7 @@ public class LangUtilsHook extends Hook {
|
|||
if (hooked) {
|
||||
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";
|
||||
default -> "Unknown Lingering Potion";
|
||||
};
|
||||
return "Lingering" + generalPotionName(potionType);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -376,28 +351,7 @@ public class LangUtilsHook extends Hook {
|
|||
if (hooked) {
|
||||
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";
|
||||
default -> "Unknown Arrow";
|
||||
};
|
||||
return generalPotionName(potionType).replaceAll("Potion", "Arrow");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -413,11 +367,12 @@ public class LangUtilsHook extends Hook {
|
|||
if (hooked) {
|
||||
return LanguageHelper.getPotionBaseEffectName(potionType, getUserLocale(user));
|
||||
}
|
||||
PotionEffectType effectType = potionType.getEffectType();
|
||||
if (effectType == null) {
|
||||
List<PotionEffect> effects = potionType.getPotionEffects();
|
||||
if (effects.isEmpty()) {
|
||||
return "No Effects";
|
||||
}
|
||||
return Util.prettifyText(effectType.getName());
|
||||
return effects.stream().map(effect -> Util.prettifyText(effect.getType().getKey().getKey()))
|
||||
.collect(Collectors.joining(", "));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -430,7 +385,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.getKey().getKey());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,420 @@
|
|||
package world.bentobox.bentobox.hooks;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import com.github.puregero.multilib.DataStorageImpl;
|
||||
import com.github.puregero.multilib.MultiLib;
|
||||
|
||||
import world.bentobox.bentobox.api.hooks.Hook;
|
||||
|
||||
/**
|
||||
* Hook for Multipaper
|
||||
*/
|
||||
public class MultipaperHook extends Hook {
|
||||
|
||||
public MultipaperHook() {
|
||||
super("multipaper", Material.PAPER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hook() {
|
||||
return MultiLib.isMultiPaper();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this is a Multipaper server
|
||||
*/
|
||||
@Override
|
||||
public boolean isPluginAvailable() {
|
||||
return MultiLib.isMultiPaper();
|
||||
}
|
||||
|
||||
/**
|
||||
* Always null because it is not a plugin
|
||||
* @return null
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
public Plugin getPlugin() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the chunk is running on an external server or not.
|
||||
*
|
||||
* @return True if the chunk is an external chunk, or false if the chunk
|
||||
* is running on this server or if it's unloaded.
|
||||
*/
|
||||
public static boolean isChunkExternal(World world, int cx, int cz) {
|
||||
return MultiLib.isChunkExternal(world, cx, cz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the chunk is running on an external server or not.
|
||||
*
|
||||
* @return True if the chunk is an external chunk, or false if the chunk
|
||||
* is running on this server or if it's unloaded.
|
||||
*/
|
||||
public static boolean isChunkExternal(Location location) {
|
||||
return MultiLib.isChunkExternal(location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the chunk is running on an external server or not.
|
||||
*
|
||||
* @return True if the chunk is an external chunk, or false if the chunk
|
||||
* is running on this server or if it's unloaded.
|
||||
*/
|
||||
public static boolean isChunkExternal(Entity entity) {
|
||||
return MultiLib.isChunkExternal(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the chunk is running on an external server or not.
|
||||
*
|
||||
* @return True if the chunk is an external chunk, or false if the chunk
|
||||
* is running on this server or if it's unloaded.
|
||||
*/
|
||||
public static boolean isChunkExternal(Block block) {
|
||||
return MultiLib.isChunkExternal(block);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the chunk is running on an external server or not.
|
||||
*
|
||||
* @return True if the chunk is an external chunk, or false if the chunk
|
||||
* is running on this server or if it's unloaded.
|
||||
*/
|
||||
public static boolean isChunkExternal(Chunk chunk) {
|
||||
return MultiLib.isChunkExternal(chunk);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the chunk is running on this server or not.
|
||||
*
|
||||
* @return True if the chunk is a local chunk, or false if the chunk
|
||||
* is running on an external server or if it's unloaded.
|
||||
*/
|
||||
public static boolean isChunkLocal(World world, int cx, int cz) {
|
||||
return MultiLib.isChunkLocal(world, cx, cz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the chunk is running on this server or not.
|
||||
*
|
||||
* @return True if the chunk is a local chunk, or false if the chunk
|
||||
* is running on an external server or if it's unloaded.
|
||||
*/
|
||||
public static boolean isChunkLocal(Location location) {
|
||||
return MultiLib.isChunkLocal(location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the chunk is running on this server or not.
|
||||
*
|
||||
* @return True if the chunk is a local chunk, or false if the chunk
|
||||
* is running on an external server or if it's unloaded.
|
||||
*/
|
||||
public static boolean isChunkLocal(Entity entity) {
|
||||
return MultiLib.isChunkLocal(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the chunk is running on this server or not.
|
||||
*
|
||||
* @return True if the chunk is a local chunk, or false if the chunk
|
||||
* is running on an external server or if it's unloaded.
|
||||
*/
|
||||
public static boolean isChunkLocal(Block block) {
|
||||
return MultiLib.isChunkLocal(block);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the chunk is running on this server or not.
|
||||
*
|
||||
* @return True if the chunk is a local chunk, or false if the chunk
|
||||
* is running on an external server or if it's unloaded.
|
||||
*/
|
||||
public static boolean isChunkLocal(Chunk chunk) {
|
||||
return MultiLib.isChunkLocal(chunk);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the player is on an external server or not.
|
||||
*
|
||||
* @return True if the player is on an external server.
|
||||
*/
|
||||
public static boolean isExternalPlayer(Player player) {
|
||||
return MultiLib.isExternalPlayer(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the player is on this server or not.
|
||||
*
|
||||
* @return True if the player is on this server.
|
||||
*/
|
||||
public static boolean isLocalPlayer(Player player) {
|
||||
return MultiLib.isLocalPlayer(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bungeecord name of this server.
|
||||
*
|
||||
* @return the bungeecord name of this server
|
||||
*/
|
||||
@NonNull
|
||||
public static String getLocalServerName() {
|
||||
return MultiLib.getLocalServerName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bungeecord name of the server that this player is on.
|
||||
*
|
||||
* @return The bungeecord name of the server the player is on for external
|
||||
* players, or null for local players.
|
||||
*/
|
||||
@Nullable
|
||||
public static String getExternalServerName(Player player) {
|
||||
return MultiLib.getExternalServerName(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns cross-server data that is stored under the specified key. Note
|
||||
* that all plugins share the same set of keys. This data is
|
||||
* non-persistent, it will be lost when the player disconnects.
|
||||
*
|
||||
* @param key The key the data is stored under.
|
||||
* @return The data stored under the key, or null if the key isn't set.
|
||||
*/
|
||||
public static String getData(Player player, String key) {
|
||||
return MultiLib.getData(player, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store cross-server data under the specified key. Note that all plugins
|
||||
* share the same set of keys. This data is non-persistent, it will be
|
||||
* lost when the player disconnects.
|
||||
*
|
||||
* @param key The key to store the data under.
|
||||
* @param value The data to store under the key.
|
||||
*/
|
||||
public static void setData(Player player, String key, String value) {
|
||||
MultiLib.setData(player, key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns cross-server data that is stored under the specified key. Note
|
||||
* that all plugins share the same set of keys. This data is persistent,
|
||||
* it will be saved even if the player disconnects. This persistent data is
|
||||
* saved onto the player's .dat file.
|
||||
*
|
||||
* @param key The key the data is stored under.
|
||||
* @return The data stored under the key, or null if the key isn't set.
|
||||
*/
|
||||
public static String getPersistentData(Player player, String key) {
|
||||
return MultiLib.getPersistentData(player, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store cross-server data under the specified key. Note that all plugins
|
||||
* share the same set of keys. This data is persistent, it will be saved
|
||||
* even if the player disconnects. This persistent data is saved onto the
|
||||
* player's .dat file.
|
||||
*
|
||||
* @param key The key to store the data under.
|
||||
* @param value The data to store under the key.
|
||||
*/
|
||||
public static void setPersistentData(Player player, String key, String value) {
|
||||
MultiLib.setPersistentData(player, key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen to notifications sent by other servers.
|
||||
*
|
||||
* @param plugin The plugin listening to these notifications
|
||||
* @param channel The notification channel to listen to
|
||||
* @param callback A handler for any data received
|
||||
*/
|
||||
public static void on(Plugin plugin, String channel, Consumer<byte[]> callback) {
|
||||
MultiLib.on(plugin, channel, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen to notifications sent by other servers.
|
||||
*
|
||||
* @param plugin The plugin listening to these notifications
|
||||
* @param channel The notification channel to listen to
|
||||
* @param callback A handler for any data received
|
||||
*/
|
||||
public static void onString(Plugin plugin, String channel, Consumer<String> callback) {
|
||||
MultiLib.onString(plugin, channel, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen to notifications sent by other servers.
|
||||
*
|
||||
* @param plugin The plugin listening to these notifications
|
||||
* @param channel The notification channel to listen to
|
||||
* @param callbackWithReply A handler for any data received, and a method to reply to the server on a specified channel
|
||||
*/
|
||||
public static void on(Plugin plugin, String channel,
|
||||
BiConsumer<byte[], BiConsumer<String, byte[]>> callbackWithReply) {
|
||||
MultiLib.on(plugin, channel, callbackWithReply);
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen to notifications sent by other servers.
|
||||
*
|
||||
* @param plugin The plugin listening to these notifications
|
||||
* @param channel The notification channel to listen to
|
||||
* @param callbackWithReply A handler for any data received, and a method to reply to the server on a specified channel
|
||||
*/
|
||||
public static void onString(Plugin plugin, String channel,
|
||||
BiConsumer<String, BiConsumer<String, String>> callbackWithReply) {
|
||||
MultiLib.onString(plugin, channel, callbackWithReply);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify all other servers.
|
||||
*
|
||||
* @param channel The notification channel to notify on
|
||||
* @param data The data to notify other servers with
|
||||
*/
|
||||
public static void notify(String channel, byte[] data) {
|
||||
MultiLib.notify(channel, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify all other servers.
|
||||
*
|
||||
* @param channel The notification channel to notify on
|
||||
* @param data The data to notify other servers with
|
||||
*/
|
||||
public static void notify(String channel, String data) {
|
||||
MultiLib.notify(channel, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify other servers with the specified chunk loaded
|
||||
*
|
||||
* @param chunk The chunk that's loaded
|
||||
* @param channel The notification channel to notify on
|
||||
* @param data The data to notify other servers with
|
||||
*/
|
||||
public static void notify(Chunk chunk, String channel, byte[] data) {
|
||||
MultiLib.notify(chunk, channel, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify other servers with the specified chunk loaded
|
||||
*
|
||||
* @param chunk The chunk that's loaded
|
||||
* @param channel The notification channel to notify on
|
||||
* @param data The data to notify other servers with
|
||||
*/
|
||||
public static void notify(Chunk chunk, String channel, String data) {
|
||||
MultiLib.notify(chunk, channel, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the owning server of the specified chunk.
|
||||
* This chunk must be loaded on this server.
|
||||
* This will notify this server if this server is the owning server.
|
||||
*
|
||||
* @param chunk The loaded chunk with an owning server
|
||||
* @param channel The notification channel to notify on
|
||||
* @param data The data to notify other servers with
|
||||
*/
|
||||
public static void notifyOwningServer(Chunk chunk, String channel, byte[] data) {
|
||||
MultiLib.notifyOwningServer(chunk, channel, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the owning server of the specified chunk.
|
||||
* This chunk must be loaded on this server.
|
||||
* This will notify this server if this server is the owning server.
|
||||
*
|
||||
* @param chunk The loaded chunk with an owning server
|
||||
* @param channel The notification channel to notify on
|
||||
* @param data The data to notify other servers with
|
||||
*/
|
||||
public static void notifyOwningServer(Chunk chunk, String channel, String data) {
|
||||
MultiLib.notifyOwningServer(chunk, channel, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the owning server of the specified player.
|
||||
* This will notify this server if this server is the owning server.
|
||||
*
|
||||
* @param player The player with an owning server
|
||||
* @param channel The notification channel to notify on
|
||||
* @param data The data to notify other servers with
|
||||
*/
|
||||
public static void notifyOwningServer(Player player, String channel, byte[] data) {
|
||||
MultiLib.notifyOwningServer(player, channel, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the owning server of the specified player.
|
||||
* This will notify this server if this server is the owning server.
|
||||
*
|
||||
* @param player The player with an owning server
|
||||
* @param channel The notification channel to notify on
|
||||
* @param data The data to notify other servers with
|
||||
*/
|
||||
public static void notifyOwningServer(Player player, String channel, String data) {
|
||||
MultiLib.notifyOwningServer(player, channel, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Says a message (or runs a command) on other servers excluding this one.
|
||||
*
|
||||
* @param message The chat message to say
|
||||
*/
|
||||
public static void chatOnOtherServers(Player player, String message) {
|
||||
MultiLib.chatOnOtherServers(player, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all online players across all server instances.
|
||||
*
|
||||
* @return a view of all online players
|
||||
*/
|
||||
public static Collection<? extends Player> getAllOnlinePlayers() {
|
||||
return MultiLib.getAllOnlinePlayers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns players logged into your single local server instance.
|
||||
*
|
||||
* @return a view of players online on your local instance
|
||||
*/
|
||||
public static Collection<? extends Player> getLocalOnlinePlayers() {
|
||||
return MultiLib.getLocalOnlinePlayers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the multipaper key-value data storage. Accessing this data is
|
||||
* asynchronous. This storage medium is hosted on the Master instance,
|
||||
* or a yaml file when using Bukkit.
|
||||
*
|
||||
* @return the multipaper data storage
|
||||
*/
|
||||
public static DataStorageImpl getDataStorage() {
|
||||
return MultiLib.getDataStorage();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package world.bentobox.bentobox.hooks;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Entity;
|
||||
|
||||
import io.lumine.mythic.bukkit.BukkitAdapter;
|
||||
import io.lumine.mythic.bukkit.MythicBukkit;
|
||||
import io.lumine.mythic.core.mobs.ActiveMob;
|
||||
import world.bentobox.bentobox.api.hooks.Hook;
|
||||
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity.MythicMobRecord;
|
||||
|
||||
/**
|
||||
* Provides implementation and interfacing to interact with MythicMobs.
|
||||
*
|
||||
* @author tastybento
|
||||
* @since 2.2.0
|
||||
*/
|
||||
public class MythicMobsHook extends Hook {
|
||||
|
||||
public MythicMobsHook() {
|
||||
super("MythicMobs", Material.CREEPER_HEAD);
|
||||
}
|
||||
|
||||
public boolean isMythicMob(Entity bukkitEntity) {
|
||||
return MythicBukkit.inst().getMobManager().isMythicMob(bukkitEntity);
|
||||
}
|
||||
|
||||
public MythicMobRecord getMythicMob(Entity bukkitEntity) {
|
||||
ActiveMob mm = MythicBukkit.inst().getMobManager().getActiveMob(bukkitEntity.getUniqueId()).orElse(null);
|
||||
if (mm != null) {
|
||||
return new MythicMobRecord(mm.getMobType(), mm.getDisplayName(), mm.getLevel(),
|
||||
mm.getPower(),
|
||||
mm.getStance());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean hook() {
|
||||
return true; // The hook process shouldn't fail
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFailureCause() {
|
||||
return null; // The hook process shouldn't fail
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawn a MythicMob
|
||||
* @param mmr MythicMobRecord
|
||||
* @param spawnLocation location
|
||||
* @return true if spawn is successful
|
||||
*/
|
||||
public boolean spawnMythicMob(MythicMobRecord mmr, Location spawnLocation) {
|
||||
if (!this.isPluginAvailable()) {
|
||||
return false;
|
||||
}
|
||||
return MythicBukkit.inst().getMobManager().getMythicMob(mmr.type()).map(mob -> {
|
||||
// A delay is required before spawning, I assume because the blocks are pasted using NMS
|
||||
Bukkit.getScheduler().runTaskLater(getPlugin(), () -> {
|
||||
// spawns mob
|
||||
ActiveMob activeMob = mob.spawn(BukkitAdapter.adapt(spawnLocation), mmr.level());
|
||||
activeMob.setDisplayName(mmr.displayName());
|
||||
activeMob.setPower(mmr.power());
|
||||
activeMob.setStance(mmr.stance());
|
||||
}, 40L);
|
||||
return true;
|
||||
}).orElse(false);
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ package world.bentobox.bentobox.listeners;
|
|||
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
|
@ -61,7 +62,7 @@ public class JoinLeaveListener implements Listener {
|
|||
|
||||
// Make sure the player is loaded into the cache or create the player if they
|
||||
// don't exist
|
||||
players.addPlayer(playerUUID);
|
||||
players.getPlayer(playerUUID);
|
||||
|
||||
// Reset island resets if required
|
||||
plugin.getIWM().getOverWorlds().stream()
|
||||
|
@ -74,11 +75,14 @@ public class JoinLeaveListener implements Listener {
|
|||
// Set the player's name (it may have changed), but only if it isn't empty
|
||||
if (!user.getName().isEmpty()) {
|
||||
players.setPlayerName(user);
|
||||
players.save(playerUUID);
|
||||
} else {
|
||||
plugin.logWarning("Player that just logged in has no name! " + playerUUID);
|
||||
}
|
||||
|
||||
// Set the primary island to the player's location if this is their island
|
||||
plugin.getIslands().getIslandAt(user.getLocation()).filter(i -> user.getUniqueId().equals(i.getOwner()))
|
||||
.ifPresent(i -> plugin.getIslands().setPrimaryIsland(playerUUID, i));
|
||||
|
||||
// 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())
|
||||
|
@ -106,7 +110,7 @@ public class JoinLeaveListener implements Listener {
|
|||
private void firstTime(User user) {
|
||||
// Make sure the player is loaded into the cache or create the player if they
|
||||
// don't exist
|
||||
players.addPlayer(user.getUniqueId());
|
||||
players.getPlayer(user.getUniqueId());
|
||||
|
||||
plugin.getIWM().getOverWorlds().stream().filter(w -> plugin.getIWM().isCreateIslandOnFirstLoginEnabled(w))
|
||||
.forEach(w -> {
|
||||
|
@ -177,13 +181,15 @@ public class JoinLeaveListener implements Listener {
|
|||
user.getPlayer().getInventory().clear();
|
||||
}
|
||||
|
||||
playerData.getPendingKicks().remove(world.getName());
|
||||
players.save(user.getUniqueId());
|
||||
Set<String> kicks = playerData.getPendingKicks();
|
||||
kicks.remove(world.getName());
|
||||
playerData.setPendingKicks(kicks);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void updateIslandRange(User user) {
|
||||
plugin.getIslands().getIslands().stream()
|
||||
plugin.getIslands().getIslands(user.getUniqueId()).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
|
||||
|
@ -214,12 +220,11 @@ public class JoinLeaveListener implements Listener {
|
|||
// Remove any coops if all the island players have left
|
||||
// Go through all the islands this player is a member of, check if all members
|
||||
// have left, remove coops
|
||||
|
||||
plugin.getIslands().getIslands().stream()
|
||||
plugin.getIslands().getIslands(event.getPlayer().getUniqueId()).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()))) {
|
||||
.noneMatch(p -> island.inTeam(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)
|
||||
|
@ -232,7 +237,6 @@ public class JoinLeaveListener implements Listener {
|
|||
});
|
||||
// Remove any coop associations from the player logging out
|
||||
plugin.getIslands().clearRank(RanksManager.COOP_RANK, event.getPlayer().getUniqueId());
|
||||
players.save(event.getPlayer().getUniqueId());
|
||||
User.removePlayer(event.getPlayer());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,6 +61,7 @@ public class PanelListenerManager implements Listener {
|
|||
// Refresh
|
||||
l.refreshPanel();
|
||||
});
|
||||
|
||||
} else {
|
||||
// Wrong name - delete this panel
|
||||
openPanels.remove(user.getUniqueId());
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
package world.bentobox.bentobox.listeners;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.world.ChunkLoadEvent;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.api.addons.GameModeAddon;
|
||||
import world.bentobox.bentobox.api.events.BentoBoxReadyEvent;
|
||||
import world.bentobox.bentobox.util.Util;
|
||||
|
||||
/**
|
||||
* Updates chunks in seed worlds if they have been generated in the main world
|
||||
* @author tastybento
|
||||
*/
|
||||
public class SeedWorldMakerListener implements Listener {
|
||||
|
||||
private final BentoBox plugin;
|
||||
|
||||
/**
|
||||
* Whether BentoBox is ready or not.
|
||||
* This helps to avoid hanging out the server on startup as a lot of {@link ChunkLoadEvent} are called at this time.
|
||||
* @since 1.1
|
||||
*/
|
||||
private boolean ready;
|
||||
|
||||
|
||||
public SeedWorldMakerListener(BentoBox bentoBox) {
|
||||
this.plugin = bentoBox;
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||
public void onBentoBoxReady(BentoBoxReadyEvent e) {
|
||||
ready = true;
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||
public void onChunkLoad(ChunkLoadEvent e) {
|
||||
if (!ready || !e.getChunk().isGenerated()) {
|
||||
return;
|
||||
}
|
||||
World world = e.getWorld();
|
||||
plugin.getIWM().getAddon(world).filter(GameModeAddon::isUsesNewChunkGeneration).ifPresent(gma -> {
|
||||
World seed = Bukkit.getWorld(world.getName() + "/bentobox");
|
||||
int x = e.getChunk().getX();
|
||||
int z = e.getChunk().getZ();
|
||||
if (seed != null && !seed.getChunkAt(x, z, false).isGenerated()) {
|
||||
Util.getChunkAtAsync(seed, x, z, true);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -60,9 +60,6 @@ public class CommandCycleClick implements ClickHandler {
|
|||
}
|
||||
// Apply change to panel
|
||||
panel.getInventory().setItem(slot, commandRankClickListener.getPanelItem(command, user, world).getItem());
|
||||
// Save island
|
||||
plugin.getIslands().save(island);
|
||||
|
||||
} else {
|
||||
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_METAL_HIT, 1F, 1F);
|
||||
}
|
||||
|
|
|
@ -80,16 +80,20 @@ public class BreakBlocksListener extends FlagListener {
|
|||
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;
|
||||
// Right click handling
|
||||
if (e.getAction() == Action.RIGHT_CLICK_BLOCK) {
|
||||
Material clickedType = e.getClickedBlock().getType();
|
||||
switch (clickedType) {
|
||||
case CAVE_VINES, CAVE_VINES_PLANT -> {
|
||||
if (((CaveVinesPlant) e.getClickedBlock().getBlockData()).isBerries()) {
|
||||
this.checkIsland(e, p, l, Flags.HARVEST);
|
||||
}
|
||||
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);
|
||||
}
|
||||
case SWEET_BERRY_BUSH -> this.checkIsland(e, p, l, Flags.HARVEST);
|
||||
case ROOTED_DIRT -> this.checkIsland(e, p, l, Flags.BREAK_BLOCKS);
|
||||
default -> { // Do nothing
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Only handle hitting things
|
||||
|
|
|
@ -17,6 +17,7 @@ import org.bukkit.inventory.EquipmentSlot;
|
|||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import com.google.common.base.Enums;
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import world.bentobox.bentobox.api.flags.FlagListener;
|
||||
import world.bentobox.bentobox.lists.Flags;
|
||||
|
@ -28,7 +29,6 @@ import world.bentobox.bentobox.lists.Flags;
|
|||
*
|
||||
*/
|
||||
public class BreedingListener extends FlagListener {
|
||||
|
||||
/**
|
||||
* A list of items that cause breeding if a player has them in their hand and they click an animal
|
||||
* This list may need to be extended with future versions of Minecraft.
|
||||
|
@ -41,7 +41,10 @@ public class BreedingListener extends FlagListener {
|
|||
bi.put(EntityType.HORSE, Arrays.asList(Material.GOLDEN_APPLE, Material.GOLDEN_CARROT));
|
||||
bi.put(EntityType.DONKEY, Arrays.asList(Material.GOLDEN_APPLE, Material.GOLDEN_CARROT));
|
||||
bi.put(EntityType.COW, Collections.singletonList(Material.WHEAT));
|
||||
bi.put(EntityType.MUSHROOM_COW, Collections.singletonList(Material.WHEAT));
|
||||
Optional<EntityType> mc = Enums.getIfPresent(EntityType.class, "MUSHROOM_COW");
|
||||
if (mc.isPresent()) {
|
||||
bi.put(mc.get(), Collections.singletonList(Material.WHEAT));
|
||||
}
|
||||
bi.put(EntityType.SHEEP, Collections.singletonList(Material.WHEAT));
|
||||
bi.put(EntityType.PIG, Arrays.asList(Material.CARROT, Material.POTATO, Material.BEETROOT));
|
||||
bi.put(EntityType.CHICKEN, Arrays.asList(Material.WHEAT_SEEDS, Material.PUMPKIN_SEEDS, Material.MELON_SEEDS, Material.BEETROOT_SEEDS));
|
||||
|
|
|
@ -28,7 +28,6 @@ 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;
|
||||
|
@ -185,9 +184,6 @@ public class InventoryListener extends FlagListener
|
|||
} 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;
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.bukkit.event.player.PlayerUnleashEntityEvent;
|
|||
|
||||
import world.bentobox.bentobox.api.flags.FlagListener;
|
||||
import world.bentobox.bentobox.lists.Flags;
|
||||
import world.bentobox.bentobox.util.Util;
|
||||
|
||||
/**
|
||||
* @author tastybento
|
||||
|
@ -42,7 +43,8 @@ public class LeashListener extends FlagListener {
|
|||
*/
|
||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||
public void onPlayerLeashHitch(final HangingPlaceEvent e) {
|
||||
if (e.getEntity().getType().equals(EntityType.LEASH_HITCH)) {
|
||||
EntityType LEASH_HITCH = Util.findFirstMatchingEnum(EntityType.class, "LEASH_HITCH", "LEASH_KNOT");
|
||||
if (e.getEntity().getType().equals(LEASH_HITCH)) {
|
||||
checkIsland(e, e.getPlayer(), e.getEntity().getLocation(), Flags.LEASH);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ import world.bentobox.bentobox.lists.Flags;
|
|||
|
||||
|
||||
/**
|
||||
* Listener for {@link Flags#CROP_TRAMPLE, Flags#PRESSURE_PLATE, Flags#TURTLE_EGGS, Flags#BUTTON}
|
||||
* Listener for {@link Flags#CROP_TRAMPLE}, {@link Flags#PRESSURE_PLATE}, {@link Flags#TURTLE_EGGS}, {@link Flags#BUTTON}
|
||||
* @author tastybento
|
||||
*
|
||||
*/
|
||||
|
|
|
@ -20,6 +20,7 @@ import world.bentobox.bentobox.api.flags.FlagListener;
|
|||
import world.bentobox.bentobox.lists.Flags;
|
||||
|
||||
/**
|
||||
* Provides protection for placing blocks.
|
||||
* @author tastybento
|
||||
*/
|
||||
public class PlaceBlocksListener extends FlagListener
|
||||
|
|
|
@ -4,6 +4,7 @@ import org.bukkit.event.EventHandler;
|
|||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.player.PlayerShearEntityEvent;
|
||||
|
||||
import io.papermc.paper.event.block.PlayerShearBlockEvent;
|
||||
import world.bentobox.bentobox.api.flags.FlagListener;
|
||||
import world.bentobox.bentobox.lists.Flags;
|
||||
|
||||
|
@ -20,4 +21,10 @@ public class ShearingListener extends FlagListener {
|
|||
checkIsland(e, e.getPlayer(), e.getEntity().getLocation(), Flags.SHEARING);
|
||||
}
|
||||
|
||||
// Block shearing - paper only
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void onShearBlock(final PlayerShearBlockEvent e) {
|
||||
checkIsland(e, e.getPlayer(), e.getBlock().getLocation(), Flags.SHEARING);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,6 +17,9 @@ import org.bukkit.event.entity.EntityDamageEvent;
|
|||
import org.bukkit.event.entity.EntityExplodeEvent;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
|
||||
import com.google.common.base.Enums;
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import world.bentobox.bentobox.api.flags.FlagListener;
|
||||
import world.bentobox.bentobox.lists.Flags;
|
||||
|
||||
|
@ -25,13 +28,26 @@ import world.bentobox.bentobox.lists.Flags;
|
|||
* @author tastybento
|
||||
*/
|
||||
public class TNTListener extends FlagListener {
|
||||
|
||||
/**
|
||||
* Contains {@link EntityType}s that generates an explosion.
|
||||
* @since 1.5.0
|
||||
*/
|
||||
private static final List<EntityType> TNT_TYPES = List.of(EntityType.PRIMED_TNT, EntityType.MINECART_TNT);
|
||||
private static final List<EntityType> TNT_TYPES = List.of(
|
||||
findFirstMatchingEnum(EntityType.class, "PRIMED_TNT", "TNT"),
|
||||
findFirstMatchingEnum(EntityType.class, "MINECART_TNT", "TNT_MINECART"));
|
||||
|
||||
private static <T extends Enum<T>> T findFirstMatchingEnum(Class<T> enumClass, String... values) {
|
||||
if (enumClass == null || values == null) {
|
||||
return null;
|
||||
}
|
||||
for (String value : values) {
|
||||
Optional<T> enumConstant = Enums.getIfPresent(enumClass, value.toUpperCase());
|
||||
if (enumConstant.isPresent()) {
|
||||
return enumConstant.get();
|
||||
}
|
||||
}
|
||||
return null; // Return null if no match is found
|
||||
}
|
||||
/**
|
||||
* Contains {@link Material}s that can be used to prime a TNT.
|
||||
* @since 1.5.0
|
||||
|
|
|
@ -53,8 +53,9 @@ public class PVPListener extends FlagListener {
|
|||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||
public void onEntityDamage(EntityDamageByEntityEvent e) {
|
||||
if (e.getEntity() instanceof Player player && getPlugin().getIWM().inWorld(e.getEntity().getWorld())) {
|
||||
// Allow self damage or NPC attack because Citizens handles its own PVP
|
||||
if (e.getEntity().equals(e.getDamager()) || e.getEntity().hasMetadata("NPC")) {
|
||||
// Allow self damage or NPC attack or attack by NPC because Citizens handles its own PVP
|
||||
if (e.getEntity().equals(e.getDamager()) || e.getEntity().hasMetadata("NPC")
|
||||
|| e.getDamager().hasMetadata("NPC")) {
|
||||
return;
|
||||
}
|
||||
// Is PVP allowed here?
|
||||
|
@ -224,9 +225,7 @@ public class PVPListener extends FlagListener {
|
|||
// Only care about PVP Flags
|
||||
if (Flags.PVP_OVERWORLD.equals(flag) || Flags.PVP_NETHER.equals(flag) || Flags.PVP_END.equals(flag)) {
|
||||
String message = "protection.flags." + flag.getID() + "." + (e.isSetTo() ? "enabled" : "disabled");
|
||||
// Send the message to visitors
|
||||
e.getIsland().getVisitors().forEach(visitor -> User.getInstance(visitor).sendMessage(message));
|
||||
// Send the message to players on the island
|
||||
// Send the message to all players on the island
|
||||
e.getIsland().getPlayersOnIsland().forEach(player -> User.getInstance(player).sendMessage(message));
|
||||
}
|
||||
}
|
||||
|
@ -268,7 +267,7 @@ public class PVPListener extends FlagListener {
|
|||
|
||||
private void alertUser(@NonNull Player player, Flag flag) {
|
||||
String message = "protection.flags." + flag.getID() + ".enabled";
|
||||
User.getInstance(player).sendMessage(message);
|
||||
User.getInstance(player).notify(message);
|
||||
player.playSound(player.getLocation(), Sound.ENTITY_ARROW_HIT_PLAYER,2F, 1F);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package world.bentobox.bentobox.listeners.flags.worldsettings;
|
||||
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Creeper;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
|
@ -8,6 +11,7 @@ import org.bukkit.event.EventPriority;
|
|||
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||
import org.bukkit.event.entity.EntityDamageEvent;
|
||||
import org.bukkit.event.entity.EntityExplodeEvent;
|
||||
import org.bukkit.event.player.PlayerInteractEntityEvent;
|
||||
|
||||
import world.bentobox.bentobox.api.flags.FlagListener;
|
||||
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||
|
@ -68,4 +72,33 @@ public class CreeperListener extends FlagListener {
|
|||
e.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prevent creepers from igniting if they are not allowed to grief
|
||||
* @param e - event
|
||||
* @since 2.4.0
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||
public void onPlayerInteractEntity(PlayerInteractEntityEvent e)
|
||||
{
|
||||
Player player = e.getPlayer();
|
||||
Location location = e.getRightClicked().getLocation();
|
||||
|
||||
if (!Flags.CREEPER_GRIEFING.isSetForWorld(location.getWorld()) &&
|
||||
e.getRightClicked() instanceof Creeper &&
|
||||
!this.getIslandsManager().locationIsOnIsland(player, location))
|
||||
{
|
||||
Material mainHand = player.getInventory().getItemInMainHand().getType();
|
||||
|
||||
if (Material.FIRE_CHARGE.equals(mainHand) ||
|
||||
Material.FLINT_AND_STEEL.equals(mainHand))
|
||||
{
|
||||
// Creeper igniting
|
||||
User user = User.getInstance(player);
|
||||
user.notify("protection.protected", TextVariables.DESCRIPTION, user.getTranslation(Flags.CREEPER_GRIEFING.getHintReference()));
|
||||
e.setCancelled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -107,7 +107,7 @@ public class EnterExitListener extends FlagListener {
|
|||
// Leave messages are always specific to this world
|
||||
String islandMessage = user.getTranslation(island.getWorld(), ISLAND_MESSAGE, TextVariables.NAME, getPlugin().getPlayers().getName(island.getOwner()));
|
||||
// Send specific message if the player is member of this island
|
||||
if (island.getMemberSet().contains(user.getUniqueId())) {
|
||||
if (island.inTeam(user.getUniqueId())) {
|
||||
user.notify(island.getWorld(), "protection.flags.ENTER_EXIT_MESSAGES.now-leaving-your-island", TextVariables.NAME, (island.getName() != null) ? island.getName() : islandMessage);
|
||||
} else {
|
||||
user.notify(island.getWorld(), "protection.flags.ENTER_EXIT_MESSAGES.now-leaving", TextVariables.NAME, (island.getName() != null) ? island.getName() : islandMessage);
|
||||
|
@ -135,7 +135,7 @@ public class EnterExitListener extends FlagListener {
|
|||
// Enter messages are always specific to this world
|
||||
String islandMessage = user.getTranslation(island.getWorld(), ISLAND_MESSAGE, TextVariables.NAME, getPlugin().getPlayers().getName(island.getOwner()));
|
||||
// Send specific message if the player is member of this island
|
||||
if (island.getMemberSet().contains(user.getUniqueId())) {
|
||||
if (island.inTeam(user.getUniqueId())) {
|
||||
user.notify(island.getWorld(), "protection.flags.ENTER_EXIT_MESSAGES.now-entering-your-island", TextVariables.NAME, (island.getName() != null) ? island.getName() : islandMessage);
|
||||
} else {
|
||||
user.notify(island.getWorld(), "protection.flags.ENTER_EXIT_MESSAGES.now-entering", TextVariables.NAME, (island.getName() != null) ? island.getName() : islandMessage);
|
||||
|
|
|
@ -17,6 +17,7 @@ import org.bukkit.event.block.Action;
|
|||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.inventory.EquipmentSlot;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.util.RayTraceResult;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.api.flags.FlagListener;
|
||||
|
@ -68,12 +69,20 @@ public class ObsidianScoopingListener extends FlagListener {
|
|||
return lookForLava(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param e PlayerInteractEvent
|
||||
* @return false if obsidian not scooped, true if scooped
|
||||
*/
|
||||
private boolean lookForLava(PlayerInteractEvent e) {
|
||||
Player player = e.getPlayer();
|
||||
ItemStack bucket = e.getItem();
|
||||
|
||||
// Get block player is looking at
|
||||
Block b = e.getPlayer().rayTraceBlocks(5, FluidCollisionMode.ALWAYS).getHitBlock();
|
||||
RayTraceResult rtBlocks = e.getPlayer().rayTraceBlocks(5, FluidCollisionMode.ALWAYS);
|
||||
if (rtBlocks == null) {
|
||||
return false;
|
||||
}
|
||||
Block b = rtBlocks.getHitBlock();
|
||||
if (!b.getType().equals(Material.OBSIDIAN)) {
|
||||
// This should not be needed but might catch some attempts
|
||||
return false;
|
||||
|
|
|
@ -31,7 +31,7 @@ public class PetTeleportListener extends FlagListener {
|
|||
// Get where the pet is going
|
||||
e.setCancelled(getIslands().getProtectedIslandAt(e.getTo())
|
||||
// Not home island
|
||||
.map(i -> !i.getMemberSet().contains(t.getOwner().getUniqueId()))
|
||||
.map(i -> !i.inTeam(t.getOwner().getUniqueId()))
|
||||
// Not any island
|
||||
.orElse(true));
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ public class VisitorsStartingRaidListener extends FlagListener
|
|||
|
||||
Optional<Island> island = this.getIslands().getProtectedIslandAt(event.getPlayer().getLocation());
|
||||
|
||||
if (island.isPresent() && !island.get().getMemberSet().contains(event.getPlayer().getUniqueId()))
|
||||
if (island.isPresent() && !island.get().inTeam(event.getPlayer().getUniqueId()))
|
||||
{
|
||||
event.setCancelled(true);
|
||||
this.report(User.getInstance(event.getPlayer()),
|
||||
|
|
|
@ -674,7 +674,7 @@ public final class Flags {
|
|||
/**
|
||||
* Crop Planting
|
||||
* Controls who gets to plant crops on tilled soil.
|
||||
* Listener is {@link PlaceBlockListener}
|
||||
* Listener is {@link world.bentobox.bentobox.listeners.flags.protection.PlaceBlocksListener}
|
||||
* @since 1.23.0
|
||||
*/
|
||||
public static final Flag CROP_PLANTING = new Flag.Builder("CROP_PLANTING", Material.PUMPKIN_SEEDS).mode(Flag.Mode.BASIC).type(Type.PROTECTION).build();
|
||||
|
|
|
@ -17,6 +17,9 @@ import world.bentobox.bentobox.database.objects.Island;
|
|||
import world.bentobox.bentobox.managers.RanksManager;
|
||||
import world.bentobox.bentobox.util.Util;
|
||||
|
||||
/**
|
||||
* Common Game Mode Placeholders
|
||||
*/
|
||||
public enum GameModePlaceholder {
|
||||
|
||||
/* World-related */
|
||||
|
|
|
@ -214,8 +214,11 @@ public class BlueprintClipboardManager {
|
|||
}
|
||||
|
||||
private void unzipFiles(final ZipInputStream zipInputStream, final Path unzipFilePath) throws IOException {
|
||||
if (!unzipFilePath.toFile().getCanonicalPath().startsWith(blueprintFolder.getCanonicalPath())) {
|
||||
throw new IOException("Entry is outside of the target directory");
|
||||
// Prevent directory traversal attacks by normalizing the path
|
||||
if (!unzipFilePath.startsWith(blueprintFolder.getCanonicalFile().toPath().normalize())) {
|
||||
throw new IOException(
|
||||
"Blueprint file is trying to write outside of the target directory! Blocked attempt to write to "
|
||||
+ unzipFilePath.toString());
|
||||
}
|
||||
try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(unzipFilePath.toFile().getCanonicalPath()))) {
|
||||
byte[] bytesIn = new byte[1024];
|
||||
|
|
|
@ -40,6 +40,7 @@ import world.bentobox.bentobox.BentoBox;
|
|||
import world.bentobox.bentobox.api.addons.Addon;
|
||||
import world.bentobox.bentobox.api.addons.GameModeAddon;
|
||||
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||
import world.bentobox.bentobox.api.metadata.MetaDataValue;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.bentobox.blueprints.Blueprint;
|
||||
import world.bentobox.bentobox.blueprints.BlueprintPaster;
|
||||
|
@ -484,6 +485,8 @@ public class BlueprintsManager {
|
|||
b2 ->
|
||||
pasteEnd(addon, bb, island).thenAccept(message -> sendMessage(island)).thenAccept(b3 -> Bukkit.getScheduler().runTask(plugin, task))));
|
||||
}
|
||||
// Set the bundle name
|
||||
island.putMetaData("bundle", new MetaDataValue(name));
|
||||
return true;
|
||||
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ import java.util.concurrent.CompletableFuture;
|
|||
import java.util.stream.Collectors;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Tag;
|
||||
|
@ -37,6 +36,10 @@ import org.bukkit.util.Vector;
|
|||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import com.github.puregero.multilib.MultiLib;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
import io.papermc.lib.PaperLib;
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.api.events.IslandBaseEvent;
|
||||
|
@ -44,9 +47,9 @@ import world.bentobox.bentobox.api.events.island.IslandEvent;
|
|||
import world.bentobox.bentobox.api.events.island.IslandEvent.Reason;
|
||||
import world.bentobox.bentobox.api.flags.Flag;
|
||||
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||
import world.bentobox.bentobox.api.logs.LogEntry;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.bentobox.database.Database;
|
||||
import world.bentobox.bentobox.database.json.BentoboxTypeAdapterFactory;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
import world.bentobox.bentobox.database.objects.IslandDeletion;
|
||||
import world.bentobox.bentobox.lists.Flags;
|
||||
|
@ -65,29 +68,18 @@ public class IslandsManager {
|
|||
|
||||
private final BentoBox plugin;
|
||||
|
||||
/**
|
||||
* One island can be spawn, this is the one - otherwise, this value is null
|
||||
*/
|
||||
@NonNull
|
||||
private final Map<@NonNull World, @Nullable Island> spawn;
|
||||
private Map<World, Island> spawns = new HashMap<>();
|
||||
|
||||
private Map<World, Location> last = new HashMap<>();
|
||||
|
||||
@NonNull
|
||||
private Database<Island> handler;
|
||||
|
||||
/**
|
||||
* The last locations where an island were put. This is not stored persistently
|
||||
* and resets when the server starts
|
||||
*/
|
||||
private final Map<World, Location> last;
|
||||
private static Database<Island> handler;
|
||||
|
||||
/**
|
||||
* Island Cache
|
||||
*/
|
||||
@NonNull
|
||||
private IslandCache islandCache;
|
||||
// Quarantined islands
|
||||
@NonNull
|
||||
private final Map<UUID, List<Island>> quarantineCache;
|
||||
// Deleted islands
|
||||
@NonNull
|
||||
private final List<String> deletedIslands;
|
||||
|
@ -105,24 +97,57 @@ public class IslandsManager {
|
|||
this.plugin = plugin;
|
||||
// Set up the database handler to store and retrieve Island classes
|
||||
handler = new Database<>(plugin, Island.class);
|
||||
islandCache = new IslandCache();
|
||||
quarantineCache = new HashMap<>();
|
||||
spawn = new HashMap<>();
|
||||
last = new HashMap<>();
|
||||
islandCache = new IslandCache(handler);
|
||||
// This list should always be empty unless database deletion failed
|
||||
// In that case a purge utility may be required in the future
|
||||
deletedIslands = new ArrayList<>();
|
||||
// Mid-teleport players going home
|
||||
goingHome = new HashSet<>();
|
||||
// Set handler in Island
|
||||
|
||||
// Listen for Island Updates
|
||||
MultiLib.onString(plugin, "bentobox-updateIsland", id -> {
|
||||
Island island = handler.loadObject(id);
|
||||
if (island != null) {
|
||||
islandCache.updateIsland(island);
|
||||
}
|
||||
});
|
||||
|
||||
// Delete island blocks
|
||||
MultiLib.onString(plugin, "bentobox-deleteIsland", id -> {
|
||||
IslandDeletion idd = getGson().fromJson(id, IslandDeletion.class);
|
||||
plugin.getIslandDeletionManager().getIslandChunkDeletionManager().add(idd);
|
||||
});
|
||||
// List for new islands
|
||||
MultiLib.onString(plugin, "bentobox-newIsland", id -> {
|
||||
Island island = handler.loadObject(id);
|
||||
if (island != null) {
|
||||
islandCache.addIsland(island);
|
||||
}
|
||||
});
|
||||
// Set or clear spawn
|
||||
MultiLib.onString(plugin, "bentobox-setspawn", sp -> {
|
||||
String[] split = sp.split(",");
|
||||
if (split.length == 1) {
|
||||
World world = Bukkit.getWorld(split[0]);
|
||||
this.clearSpawn(world);
|
||||
} else if (split.length == 2) {
|
||||
World world = Bukkit.getWorld(split[0]);
|
||||
if (world != null) {
|
||||
getIslandById(split[1]).ifPresent(i -> this.setSpawn(i));
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Used only for testing. Sets the database to a mock database.
|
||||
*
|
||||
* @param handler - handler
|
||||
* @param h - handler
|
||||
*/
|
||||
public void setHandler(@NonNull Database<Island> handler) {
|
||||
this.handler = handler;
|
||||
public void setHandler(@NonNull Database<Island> h) {
|
||||
handler = h;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -227,14 +252,13 @@ public class IslandsManager {
|
|||
.orElse("");
|
||||
island.setGameMode(gmName);
|
||||
island.setUniqueId(gmName + island.getUniqueId());
|
||||
while (handler.objectExists(island.getUniqueId())) {
|
||||
// This should never happen, so although this is a potential infinite loop I'm
|
||||
// going to leave it here because
|
||||
// it will be bad if this does occur and the server should crash.
|
||||
plugin.logWarning("Duplicate island UUID occurred");
|
||||
island.setUniqueId(gmName + UUID.randomUUID());
|
||||
}
|
||||
if (islandCache.addIsland(island)) {
|
||||
// Save to database and notify other servers
|
||||
handler.saveObjectAsync(island).thenAccept(b -> {
|
||||
if (b.equals(Boolean.TRUE)) {
|
||||
MultiLib.notify("bentobox-newIsland", island.getUniqueId());
|
||||
}
|
||||
});
|
||||
return island;
|
||||
}
|
||||
return null;
|
||||
|
@ -257,27 +281,40 @@ public class IslandsManager {
|
|||
// Set the owner of the island to no one.
|
||||
island.setOwner(null);
|
||||
island.setFlag(Flags.LOCK, RanksManager.VISITOR_RANK);
|
||||
island.setDeleted(true);
|
||||
if (removeBlocks) {
|
||||
// Remove island from the cache
|
||||
islandCache.deleteIslandFromCache(island);
|
||||
// Log the deletion (it shouldn't matter but may be useful)
|
||||
island.log(new LogEntry.Builder("DELETED").build());
|
||||
// Set the delete flag which will prevent it from being loaded even if database
|
||||
// deletion fails
|
||||
island.setDeleted(true);
|
||||
// Save the island
|
||||
handler.saveObjectAsync(island);
|
||||
// Delete the island
|
||||
handler.deleteObject(island);
|
||||
// Remove players from island
|
||||
removePlayersFromIsland(island);
|
||||
if (!plugin.getSettings().isKeepPreviousIslandOnReset()) {
|
||||
// Remove blocks from world
|
||||
plugin.getIslandDeletionManager().getIslandChunkDeletionManager().add(new IslandDeletion(island));
|
||||
IslandDeletion id = new IslandDeletion(island);
|
||||
plugin.getIslandDeletionManager().getIslandChunkDeletionManager().add(id);
|
||||
// Tell other servers
|
||||
MultiLib.notify("bentobox-deleteIsland", getGson().toJson(id));
|
||||
}
|
||||
// Delete the island from the database
|
||||
handler.deleteObject(island);
|
||||
}
|
||||
}
|
||||
|
||||
private Gson getGson() {
|
||||
|
||||
// Build the Gson
|
||||
|
||||
// excludeFieldsWithoutExposeAnnotation - this means that every field to be stored should use @Expose
|
||||
// enableComplexMapKeySerialization - forces GSON to use TypeAdapters even for Map keys
|
||||
GsonBuilder builder = new GsonBuilder().excludeFieldsWithoutExposeAnnotation()
|
||||
.enableComplexMapKeySerialization().setPrettyPrinting();
|
||||
// Register adapter factory
|
||||
builder.registerTypeAdapterFactory(new BentoboxTypeAdapterFactory(plugin));
|
||||
// Allow characters like < or > without escaping them
|
||||
builder.disableHtmlEscaping();
|
||||
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of islands made on this server. Used by stats.
|
||||
*
|
||||
|
@ -321,7 +358,7 @@ public class IslandsManager {
|
|||
* @return List of islands or empty list if none found for user
|
||||
*/
|
||||
@NonNull
|
||||
public Set<Island> getIslands(@NonNull World world, @NonNull User user) {
|
||||
public List<Island> getIslands(@NonNull World world, @NonNull User user) {
|
||||
return getIslands(world, user.getUniqueId());
|
||||
}
|
||||
|
||||
|
@ -333,15 +370,26 @@ public class IslandsManager {
|
|||
* @return List of islands or empty list if none found for user
|
||||
*/
|
||||
@NonNull
|
||||
public Set<Island> getIslands(@NonNull World world, UUID uniqueId) {
|
||||
public List<Island> getIslands(@NonNull World world, UUID uniqueId) {
|
||||
return islandCache.getIslands(world, uniqueId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the islands for this player in any world where this player has any presence
|
||||
*
|
||||
* @param uniqueId user's UUID
|
||||
* @return List of islands or empty list if none found for user
|
||||
*/
|
||||
@NonNull
|
||||
public List<Island> getIslands(UUID uniqueId) {
|
||||
return islandCache.getIslands(uniqueId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the islands for this player in this world that this player owns.
|
||||
*
|
||||
* @param world world to check
|
||||
* @param uniqueId user's UUID
|
||||
* @param user user
|
||||
* @return List of islands or empty list if none found for user
|
||||
* @since 2.1.0
|
||||
*/
|
||||
|
@ -378,7 +426,7 @@ public class IslandsManager {
|
|||
*/
|
||||
@Nullable
|
||||
public Island getIsland(@NonNull World world, @NonNull UUID uuid) {
|
||||
return islandCache.get(world, uuid);
|
||||
return islandCache.getIsland(world, uuid);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -403,7 +451,7 @@ public class IslandsManager {
|
|||
*/
|
||||
@NonNull
|
||||
public Collection<Island> getIslands() {
|
||||
return islandCache.getIslands();
|
||||
return handler.loadObjects().stream().toList();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -417,7 +465,7 @@ public class IslandsManager {
|
|||
*/
|
||||
@NonNull
|
||||
public Collection<Island> getIslands(@NonNull World world) {
|
||||
return islandCache.getIslands(world);
|
||||
return handler.loadObjects().stream().filter(i -> world.equals(i.getWorld())).toList();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -462,7 +510,7 @@ public class IslandsManager {
|
|||
* Get the last location where an island was created
|
||||
*
|
||||
* @param world - world
|
||||
* @return location
|
||||
* @return location or null if none found
|
||||
*/
|
||||
public Location getLast(@NonNull World world) {
|
||||
return last.get(world);
|
||||
|
@ -486,7 +534,7 @@ public class IslandsManager {
|
|||
if (island.getOwner() == null) {
|
||||
// No owner, no rank settings
|
||||
island.setMaxMembers(null);
|
||||
this.save(island);
|
||||
updateIsland(island);
|
||||
return 0;
|
||||
}
|
||||
// Island max is either the world default or specified amount for this island
|
||||
|
@ -507,8 +555,11 @@ public class IslandsManager {
|
|||
islandMax = owner.getPermissionValue(plugin.getIWM().getPermissionPrefix(island.getWorld()) + perm,
|
||||
islandMax);
|
||||
}
|
||||
island.setMaxMembers(rank, islandMax == worldDefault ? null : islandMax);
|
||||
this.save(island);
|
||||
Integer change = islandMax == worldDefault ? null : islandMax;
|
||||
if (island.getMaxMembers().get(rank) != change) {
|
||||
island.setMaxMembers(rank, change);
|
||||
updateIsland(island);
|
||||
}
|
||||
return islandMax;
|
||||
}
|
||||
|
||||
|
@ -546,13 +597,16 @@ public class IslandsManager {
|
|||
}
|
||||
// If the island maxHomes is just the same as the world default, then set to
|
||||
// null
|
||||
island.setMaxHomes(islandMax == plugin.getIWM().getMaxHomes(island.getWorld()) ? null : islandMax);
|
||||
this.save(island);
|
||||
Integer change = islandMax == plugin.getIWM().getMaxHomes(island.getWorld()) ? null : islandMax;
|
||||
if (island.getMaxHomes() != change) {
|
||||
island.setMaxHomes(change);
|
||||
updateIsland(island);
|
||||
}
|
||||
return islandMax;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum numbber of homes allowed on this island
|
||||
* Set the maximum number of homes allowed on this island
|
||||
*
|
||||
* @param island - island
|
||||
* @param maxHomes - max number of homes allowed, or null if the world default
|
||||
|
@ -735,9 +789,9 @@ public class IslandsManager {
|
|||
* @since 1.16.0
|
||||
*/
|
||||
public boolean setHomeLocation(@Nullable Island island, Location location, String name) {
|
||||
if (island != null) {
|
||||
if (island != null && (island.getHome(name) == null || !island.getHome(name).equals(location))) {
|
||||
island.addHome(name, location);
|
||||
this.save(island);
|
||||
updateIsland(island);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -890,7 +944,7 @@ public class IslandsManager {
|
|||
*/
|
||||
@NonNull
|
||||
public Optional<Island> getSpawn(@NonNull World world) {
|
||||
return Optional.ofNullable(spawn.get(world));
|
||||
return Optional.ofNullable(spawns.get(world));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -901,7 +955,7 @@ public class IslandsManager {
|
|||
*/
|
||||
@Nullable
|
||||
public Location getSpawnPoint(@NonNull World world) {
|
||||
return spawn.containsKey(world) ? spawn.get(world).getSpawnPoint(world.getEnvironment()) : null;
|
||||
return getSpawn(world).map(i -> i.getSpawnPoint(world.getEnvironment())).orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1132,7 +1186,7 @@ public class IslandsManager {
|
|||
* @return true if they are, false if they are not, or spawn does not exist
|
||||
*/
|
||||
public boolean isAtSpawn(Location playerLoc) {
|
||||
return spawn.containsKey(playerLoc.getWorld()) && spawn.get(playerLoc.getWorld()).onIsland(playerLoc);
|
||||
return getSpawn(playerLoc.getWorld()).map(i -> i.onIsland(playerLoc)).orElse(false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1144,19 +1198,14 @@ public class IslandsManager {
|
|||
* @param spawn the Island to set as spawn. Must not be null.
|
||||
*/
|
||||
public void setSpawn(@NonNull Island spawn) {
|
||||
// Checking if there is already a spawn set for this world
|
||||
if (this.spawn.containsKey(spawn.getWorld()) && this.spawn.get(spawn.getWorld()) != null) {
|
||||
Island oldSpawn = this.spawn.get(spawn.getWorld());
|
||||
if (oldSpawn.equals(spawn)) {
|
||||
return; // The spawn is already the current spawn - no need to update anything.
|
||||
} else {
|
||||
oldSpawn.setSpawn(false);
|
||||
}
|
||||
if (spawn.getWorld() != null) {
|
||||
spawns.put(Util.getWorld(spawn.getWorld()), spawn);
|
||||
// Tell other servers
|
||||
MultiLib.notify("bentobox-setspawn", spawn.getWorld().getUID().toString() + "," + spawn.getUniqueId());
|
||||
}
|
||||
this.spawn.put(spawn.getWorld(), spawn);
|
||||
spawn.setSpawn(true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clears the spawn island for this world
|
||||
*
|
||||
|
@ -1164,11 +1213,9 @@ public class IslandsManager {
|
|||
* @since 1.8.0
|
||||
*/
|
||||
public void clearSpawn(World world) {
|
||||
Island spawnIsland = spawn.get(Util.getWorld(world));
|
||||
if (spawnIsland != null) {
|
||||
spawnIsland.setSpawn(false);
|
||||
}
|
||||
this.spawn.remove(world);
|
||||
spawns.remove(world);
|
||||
// Tell other servers
|
||||
MultiLib.notify("bentobox-setspawn", world.getUID().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1192,7 +1239,6 @@ public class IslandsManager {
|
|||
*/
|
||||
public void load() throws IOException {
|
||||
islandCache.clear();
|
||||
quarantineCache.clear();
|
||||
List<Island> toQuarantine = new ArrayList<>();
|
||||
int owned = 0;
|
||||
int unowned = 0;
|
||||
|
@ -1206,9 +1252,6 @@ public class IslandsManager {
|
|||
if (island.isDeleted()) {
|
||||
// These will be deleted later
|
||||
deletedIslands.add(island.getUniqueId());
|
||||
} else if (island.isDoNotLoad() && island.getWorld() != null && island.getCenter() != null) {
|
||||
// Add to quarantine cache
|
||||
quarantineCache.computeIfAbsent(island.getOwner(), k -> new ArrayList<>()).add(island);
|
||||
} // Check island distance and if incorrect stop BentoBox
|
||||
else if (island.getWorld() != null && plugin.getIWM().inWorld(island.getWorld())
|
||||
&& island.getRange() != plugin.getIWM().getIslandDistance(island.getWorld())) {
|
||||
|
@ -1219,18 +1262,9 @@ public class IslandsManager {
|
|||
} else {
|
||||
// Fix island center if it is off
|
||||
fixIslandCenter(island);
|
||||
if (!islandCache.addIsland(island)) {
|
||||
// Quarantine the offending island
|
||||
toQuarantine.add(island);
|
||||
// Add to quarantine cache
|
||||
island.setDoNotLoad(true);
|
||||
quarantineCache.computeIfAbsent(island.getOwner(), k -> new ArrayList<>()).add(island);
|
||||
if (island.isUnowned()) {
|
||||
unowned++;
|
||||
} else {
|
||||
owned++;
|
||||
}
|
||||
} else if (island.isSpawn()) {
|
||||
islandCache.addIsland(island);
|
||||
|
||||
if (island.isSpawn()) {
|
||||
// Success, set spawn if this is the spawn island.
|
||||
this.setSpawn(island);
|
||||
} else {
|
||||
|
@ -1319,7 +1353,7 @@ public class IslandsManager {
|
|||
return false;
|
||||
}
|
||||
// Get the player's island
|
||||
return getIslandAt(loc).filter(i -> i.onIsland(loc)).map(i -> i.getMemberSet().contains(player.getUniqueId()))
|
||||
return getIslandAt(loc).filter(i -> i.onIsland(loc)).map(i -> i.inTeam(player.getUniqueId()))
|
||||
.orElse(false);
|
||||
}
|
||||
|
||||
|
@ -1389,20 +1423,15 @@ public class IslandsManager {
|
|||
.filter(p -> p.getGameMode().equals(plugin.getIWM().getDefaultGameMode(island.getWorld())))
|
||||
.filter(p -> island.onIsland(p.getLocation())).forEach(p -> {
|
||||
// Teleport island players to their island home
|
||||
if (!island.getMemberSet().contains(p.getUniqueId())
|
||||
if (!island.inTeam(p.getUniqueId())
|
||||
&& (hasIsland(w, p.getUniqueId()) || inTeam(w, p.getUniqueId()))) {
|
||||
homeTeleportAsync(w, p);
|
||||
} else {
|
||||
// Move player to spawn
|
||||
if (spawn.containsKey(w)) {
|
||||
// go to island spawn
|
||||
Location sp = spawn.get(w).getSpawnPoint(w.getEnvironment());
|
||||
if (sp != null) {
|
||||
PaperLib.teleportAsync(p, sp);
|
||||
} else {
|
||||
plugin.logWarning("Spawn exists but its location is null!");
|
||||
}
|
||||
}
|
||||
getSpawn(w).map(i -> i.getSpawnPoint(w.getEnvironment())).filter(Objects::nonNull)
|
||||
.ifPresentOrElse(sp -> PaperLib.teleportAsync(p, sp),
|
||||
() -> plugin.logWarning("Spawn exists but its location is null!"));
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1426,7 +1455,7 @@ public class IslandsManager {
|
|||
*/
|
||||
public void saveAll(boolean schedule) {
|
||||
if (!schedule) {
|
||||
for (Island island : islandCache.getIslands()) {
|
||||
for (Island island : islandCache.getCachedIslands()) {
|
||||
if (island.isChanged()) {
|
||||
try {
|
||||
handler.saveObjectAsync(island);
|
||||
|
@ -1439,7 +1468,7 @@ public class IslandsManager {
|
|||
}
|
||||
|
||||
isSaveTaskRunning = true;
|
||||
Queue<Island> queue = new LinkedList<>(islandCache.getIslands());
|
||||
Queue<Island> queue = new LinkedList<>(islandCache.getCachedIslands());
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
@ -1473,9 +1502,13 @@ public class IslandsManager {
|
|||
teamIsland.addMember(playerUUID);
|
||||
islandCache.addPlayer(playerUUID, teamIsland);
|
||||
// Save the island
|
||||
handler.saveObjectAsync(teamIsland);
|
||||
updateIsland(teamIsland);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the last island location
|
||||
* @param last location
|
||||
*/
|
||||
public void setLast(Location last) {
|
||||
this.last.put(last.getWorld(), last);
|
||||
}
|
||||
|
@ -1493,8 +1526,9 @@ public class IslandsManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* Checks if a player is in a team in this world. Note that the player may have
|
||||
* Checks if a player is in any team in this world. Note that the player may have
|
||||
* multiple islands in the world, any one of which may have a team.
|
||||
* Consider checking the island itself {@link Island#inTeam(UUID)}
|
||||
*
|
||||
* @param world - world
|
||||
* @param playerUUID - player's UUID
|
||||
|
@ -1502,7 +1536,7 @@ public class IslandsManager {
|
|||
*/
|
||||
public boolean inTeam(World world, @NonNull UUID playerUUID) {
|
||||
return this.islandCache.getIslands(world, playerUUID).stream()
|
||||
.anyMatch(island -> island.getMemberSet().size() > 1 && island.getMemberSet().contains(playerUUID));
|
||||
.anyMatch(island -> island.getMemberSet().size() > 1 && island.inTeam(playerUUID));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1520,17 +1554,22 @@ public class IslandsManager {
|
|||
/**
|
||||
* Sets this target as the owner for this island
|
||||
*
|
||||
* @param user previous owner
|
||||
* @param user user making the change
|
||||
* @param targetUUID new owner
|
||||
* @param island island to register
|
||||
* @param rank rank to which to set old owner.
|
||||
*/
|
||||
public void setOwner(User user, UUID targetUUID, Island island, int rank) {
|
||||
islandCache.setOwner(island, targetUUID);
|
||||
// Set old owner as sub-owner on island.
|
||||
if (rank > RanksManager.VISITOR_RANK) {
|
||||
island.setRank(user, rank);
|
||||
// Demote the old owner
|
||||
if (rank >= RanksManager.OWNER_RANK) {
|
||||
plugin.logWarning("Setowner: previous owner's rank cannot be higher than SubOwner");
|
||||
rank = RanksManager.SUB_OWNER_RANK;
|
||||
}
|
||||
if (rank > RanksManager.VISITOR_RANK && island.getOwner() != null) {
|
||||
island.setRank(island.getOwner(), rank);
|
||||
}
|
||||
// Make the new owner
|
||||
islandCache.setOwner(island, targetUUID);
|
||||
|
||||
user.sendMessage("commands.island.team.setowner.name-is-the-owner", "[name]",
|
||||
plugin.getPlayers().getName(targetUUID));
|
||||
|
@ -1602,12 +1641,16 @@ public class IslandsManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* Save the island to the database
|
||||
* Update island data in database
|
||||
*
|
||||
* @param island - island
|
||||
*/
|
||||
public void save(Island island) {
|
||||
handler.saveObjectAsync(island);
|
||||
public static void updateIsland(Island island) {
|
||||
if (handler.objectExists(island.getUniqueId())) {
|
||||
island.clearChanged();
|
||||
handler.saveObjectAsync(island)
|
||||
.thenAccept(b -> MultiLib.notify("bentobox-updateIsland", island.getUniqueId()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1622,108 +1665,6 @@ public class IslandsManager {
|
|||
return Optional.ofNullable(islandCache.getIslandById(uniqueId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to get an unmodifiable list of quarantined islands owned by uuid in this
|
||||
* world
|
||||
*
|
||||
* @param world - world
|
||||
* @param uuid - target player's UUID, or <tt>null</tt> = unowned islands
|
||||
* @return list of islands; may be empty
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@NonNull
|
||||
public List<Island> getQuarantinedIslandByUser(@NonNull World world, @Nullable UUID uuid) {
|
||||
return quarantineCache.getOrDefault(uuid, Collections.emptyList()).stream()
|
||||
.filter(i -> i.getWorld().equals(world)).toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete quarantined islands owned by uuid in this world
|
||||
*
|
||||
* @param world - world
|
||||
* @param uuid - target player's UUID, or <tt>null</tt> = unowned islands
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public void deleteQuarantinedIslandByUser(World world, @Nullable UUID uuid) {
|
||||
if (quarantineCache.containsKey(uuid)) {
|
||||
quarantineCache.get(uuid).stream().filter(i -> i.getWorld().equals(world))
|
||||
.forEach(i -> handler.deleteObject(i));
|
||||
quarantineCache.get(uuid).removeIf(i -> i.getWorld().equals(world));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the quarantineCache
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@NonNull
|
||||
public Map<UUID, List<Island>> getQuarantineCache() {
|
||||
return quarantineCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a quarantined island and delete it from the database completely. This
|
||||
* is NOT recoverable unless you have database backups.
|
||||
*
|
||||
* @param island island
|
||||
* @return {@code true} if island is quarantined and removed
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public boolean purgeQuarantinedIsland(Island island) {
|
||||
if (quarantineCache.containsKey(island.getOwner()) && quarantineCache.get(island.getOwner()).remove(island)) {
|
||||
handler.deleteObject(island);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches active island and island in trash
|
||||
*
|
||||
* @param world - game world
|
||||
* @param target - target player's UUID
|
||||
* @param island - island in trash
|
||||
* @return <tt>true</tt> if successful, otherwise <tt>false</tt>
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public boolean switchIsland(World world, UUID target, Island island) {
|
||||
// Remove trashed island from trash
|
||||
if (!quarantineCache.containsKey(island.getOwner()) || !quarantineCache.get(island.getOwner()).remove(island)) {
|
||||
plugin.logError("Could not remove island from trash");
|
||||
return false;
|
||||
}
|
||||
// Remove old island from cache if it exists
|
||||
if (this.hasIsland(world, target)) {
|
||||
Island oldIsland = islandCache.get(world, target);
|
||||
islandCache.removeIsland(oldIsland);
|
||||
|
||||
// Set old island to trash
|
||||
oldIsland.setDoNotLoad(true);
|
||||
|
||||
// Put old island into trash
|
||||
quarantineCache.computeIfAbsent(target, k -> new ArrayList<>()).add(oldIsland);
|
||||
// Save old island
|
||||
handler.saveObjectAsync(oldIsland).thenAccept(result -> {
|
||||
if (Boolean.FALSE.equals(result))
|
||||
plugin.logError("Could not save trashed island in database");
|
||||
});
|
||||
}
|
||||
// Restore island from trash
|
||||
island.setDoNotLoad(false);
|
||||
// Add new island to cache
|
||||
if (!islandCache.addIsland(island)) {
|
||||
plugin.logError("Could not add recovered island to cache");
|
||||
return false;
|
||||
}
|
||||
// Save new island
|
||||
handler.saveObjectAsync(island).thenAccept(result -> {
|
||||
if (Boolean.FALSE.equals(result)) {
|
||||
plugin.logError("Could not save recovered island to database");
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets all flags to gamemode config.yml default
|
||||
*
|
||||
|
@ -1747,121 +1688,6 @@ public class IslandsManager {
|
|||
this.saveAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the specified island custom name exists in this world.
|
||||
*
|
||||
* @param world World of the gamemode
|
||||
* @param name Name of an island
|
||||
* @return {@code true} if there is an island with the specified name in this
|
||||
* world, {@code false} otherwise.
|
||||
* @since 1.7.0
|
||||
*/
|
||||
public boolean nameExists(@NonNull World world, @NonNull String name) {
|
||||
return getIslands(world).stream().map(Island::getName).filter(Objects::nonNull)
|
||||
.anyMatch(n -> ChatColor.stripColor(n).equals(ChatColor.stripColor(name)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the admin team fix command. Attempts to fix the database for teams.
|
||||
* It will identify and correct situations where a player is listed in multiple
|
||||
* teams, or is the owner of multiple teams. It will also try to fix the current
|
||||
* cache. It is recommended to restart the server after this command is run.
|
||||
*
|
||||
* @param user - admin calling
|
||||
* @param world - game world to check
|
||||
* @return CompletableFuture boolean - true when done
|
||||
* @deprecated Not compatible with multi-islands. Will be removed.
|
||||
*/
|
||||
@Deprecated
|
||||
public CompletableFuture<Boolean> checkTeams(User user, World world) {
|
||||
CompletableFuture<Boolean> r = new CompletableFuture<>();
|
||||
user.sendMessage("commands.admin.team.fix.scanning");
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
Map<UUID, Island> owners = new HashMap<>();
|
||||
Map<UUID, Integer> freq = new HashMap<>();
|
||||
Map<UUID, List<Island>> memberships = new HashMap<>();
|
||||
handler.loadObjects().stream().filter(i -> i.getOwner() != null).filter(i -> i.getWorld() != null)
|
||||
.filter(i -> i.getWorld().equals(world)).filter(i -> !i.isDoNotLoad()).forEach(i -> {
|
||||
int count = freq.getOrDefault(i.getOwner(), 0);
|
||||
freq.put(i.getOwner(), count + 1);
|
||||
if (owners.containsKey(i.getOwner())) {
|
||||
// Player already has an island in the database
|
||||
user.sendMessage("commands.admin.team.fix.duplicate-owner", TextVariables.NAME,
|
||||
plugin.getPlayers().getName(i.getOwner()));
|
||||
Island prev = owners.get(i.getOwner());
|
||||
// Find out if this island is in the cache
|
||||
Island cachedIsland = this.getIsland(i.getWorld(), i.getOwner());
|
||||
if (cachedIsland != null && !cachedIsland.getUniqueId().equals(i.getUniqueId())) {
|
||||
islandCache.deleteIslandFromCache(i.getUniqueId());
|
||||
handler.deleteID(i.getUniqueId());
|
||||
}
|
||||
if (cachedIsland != null && !cachedIsland.getUniqueId().equals(prev.getUniqueId())) {
|
||||
islandCache.deleteIslandFromCache(prev.getUniqueId());
|
||||
handler.deleteID(prev.getUniqueId());
|
||||
}
|
||||
} else {
|
||||
owners.put(i.getOwner(), i);
|
||||
i.getMemberSet().forEach(u ->
|
||||
// Place into membership
|
||||
memberships.computeIfAbsent(u, k -> new ArrayList<>()).add(i));
|
||||
}
|
||||
});
|
||||
freq.entrySet().stream().filter(en -> en.getValue() > 1)
|
||||
.forEach(en -> user.sendMessage("commands.admin.team.fix.player-has", TextVariables.NAME,
|
||||
plugin.getPlayers().getName(en.getKey()), TextVariables.NUMBER,
|
||||
String.valueOf(en.getValue())));
|
||||
// Check for players in multiple teams
|
||||
memberships.entrySet().stream().filter(en -> en.getValue().size() > 1).forEach(en -> {
|
||||
// Get the islands
|
||||
String ownerName = plugin.getPlayers().getName(en.getKey());
|
||||
user.sendMessage("commands.admin.team.fix.duplicate-member", TextVariables.NAME, ownerName);
|
||||
int highestRank = 0;
|
||||
Island highestIsland = null;
|
||||
for (Island i : en.getValue()) {
|
||||
int rankValue = i.getRank(en.getKey());
|
||||
String rank = RanksManager.getInstance().getRank(rankValue);
|
||||
if (rankValue > highestRank || highestIsland == null) {
|
||||
highestRank = rankValue;
|
||||
highestIsland = i;
|
||||
}
|
||||
String xyz = Util.xyz(i.getCenter().toVector());
|
||||
user.sendMessage("commands.admin.team.fix.rank-on-island", TextVariables.RANK,
|
||||
user.getTranslation(rank), TextVariables.XYZ, xyz);
|
||||
user.sendRawMessage(i.getUniqueId());
|
||||
}
|
||||
// Fix island ownership in cache
|
||||
// Correct island cache
|
||||
if (highestRank == RanksManager.OWNER_RANK && highestIsland != null
|
||||
&& islandCache.getIslandById(highestIsland.getUniqueId()) != null) {
|
||||
islandCache.setOwner(islandCache.getIslandById(highestIsland.getUniqueId()), en.getKey());
|
||||
}
|
||||
// Fix all the entries that are not the highest
|
||||
for (Island island : en.getValue()) {
|
||||
if (!island.equals(highestIsland)) {
|
||||
// Get the actual island being used in the cache
|
||||
Island i = islandCache.getIslandById(island.getUniqueId());
|
||||
if (i != null) {
|
||||
// Remove membership of this island
|
||||
i.removeMember(en.getKey());
|
||||
}
|
||||
// Remove from database island
|
||||
island.removeMember(en.getKey());
|
||||
// Save to database
|
||||
handler.saveObjectAsync(island)
|
||||
.thenRun(() -> user.sendMessage("commands.admin.team.fix.fixed"));
|
||||
} else {
|
||||
// Special check for when a player is an owner and member
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
user.sendMessage("commands.admin.team.fix.done");
|
||||
r.complete(true);
|
||||
});
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is user mid home teleport?
|
||||
*
|
||||
|
@ -1893,14 +1719,14 @@ public class IslandsManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* Convenience method. See {@link IslandCache#get(World, UUID)}
|
||||
* Convenience method. See {@link IslandCache#getIsland(World, UUID)}
|
||||
*
|
||||
* @param world world
|
||||
* @param uuid player's UUID
|
||||
* @return Island of player or null if there isn't one
|
||||
*/
|
||||
public Island getPrimaryIsland(World world, UUID uuid) {
|
||||
return this.getIslandCache().get(world, uuid);
|
||||
return this.getIslandCache().getIsland(world, uuid);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,6 +3,9 @@ package world.bentobox.bentobox.managers;
|
|||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
|
@ -13,6 +16,7 @@ import org.eclipse.jdt.annotation.Nullable;
|
|||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.api.addons.Addon;
|
||||
import world.bentobox.bentobox.api.addons.GameModeAddon;
|
||||
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||
import world.bentobox.bentobox.api.placeholders.PlaceholderReplacer;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
|
@ -72,6 +76,50 @@ public class PlaceholdersManager {
|
|||
.forEach(placeholder -> registerPlaceholder(addon, placeholder.getPlaceholder(), new DefaultPlaceholder(addon, placeholder)));
|
||||
// Register team member placeholders
|
||||
registerTeamMemberPlaceholders(addon);
|
||||
// Register potential island names and member info
|
||||
registerOwnedIslandPlaceholders(addon);
|
||||
}
|
||||
|
||||
private void registerOwnedIslandPlaceholders(@NonNull GameModeAddon addon) {
|
||||
int maxIslands = plugin.getIWM().getWorldSettings(addon.getOverWorld()).getConcurrentIslands();
|
||||
IntStream.range(0, maxIslands).forEach(i -> registerPlaceholder(addon, "island_name_" + (i + 1), user -> {
|
||||
if (user == null)
|
||||
return "";
|
||||
|
||||
AtomicInteger generatedCount = new AtomicInteger(1); // To increment within lambda
|
||||
return plugin.getIslands().getIslands(addon.getOverWorld(), user).stream().map(island -> {
|
||||
IslandName islandName = getIslandName(island, user, generatedCount.get());
|
||||
if (islandName.generatated()) {
|
||||
generatedCount.getAndIncrement(); // Increment if the name was generated
|
||||
}
|
||||
return islandName.name;
|
||||
}).skip(i) // Skip to the island at index 'i'
|
||||
.findFirst() // Take the first island after skipping, effectively the (i+1)th
|
||||
.orElse(""); // Default to empty string if no island is found
|
||||
}));
|
||||
|
||||
// Island_memberlist
|
||||
IntStream.range(0, maxIslands)
|
||||
.forEach(i -> registerPlaceholder(addon, "island_memberlist_" + (i + 1), user -> user == null ? ""
|
||||
: plugin.getIslands().getIslands(addon.getOverWorld(), user).stream().skip(i).findFirst()
|
||||
.map(island -> island.getMemberSet().stream()
|
||||
.map(addon.getPlayers()::getName).collect(Collectors.joining(",")))
|
||||
.orElse("")));
|
||||
}
|
||||
|
||||
private record IslandName(String name, boolean generatated) {
|
||||
}
|
||||
|
||||
private IslandName getIslandName(Island island, User user, int index) {
|
||||
if (island.getName() != null && !island.getName().isBlank()) {
|
||||
// Name has been set
|
||||
return new IslandName(island.getName(), false);
|
||||
} else {
|
||||
// Name has not been set
|
||||
return new IslandName(user.getTranslation("protection.flags.ENTER_EXIT_MESSAGES.island", TextVariables.NAME,
|
||||
user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName()) + " " + index, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void registerTeamMemberPlaceholders(@NonNull GameModeAddon addon) {
|
||||
|
|
|
@ -4,21 +4,18 @@ import java.util.Collection;
|
|||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.Tameable;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.api.flags.Flag;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.bentobox.database.Database;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
|
@ -31,11 +28,9 @@ public class PlayersManager {
|
|||
private final BentoBox plugin;
|
||||
private Database<Players> handler;
|
||||
private final Database<Names> names;
|
||||
private final Map<UUID, Players> playerCache = new HashMap<>();
|
||||
|
||||
private final Map<UUID, Players> playerCache;
|
||||
private final Set<UUID> inTeleport;
|
||||
|
||||
private boolean isSaveTaskRunning;
|
||||
private final Set<UUID> inTeleport; // this needs databasing
|
||||
|
||||
/**
|
||||
* Provides a memory cache of online player information
|
||||
|
@ -50,7 +45,6 @@ public class PlayersManager {
|
|||
handler = new Database<>(plugin, Players.class);
|
||||
// Set up the names database
|
||||
names = new Database<>(plugin, Names.class);
|
||||
playerCache = new HashMap<>();
|
||||
inTeleport = new HashSet<>();
|
||||
}
|
||||
|
||||
|
@ -62,67 +56,7 @@ public class PlayersManager {
|
|||
this.handler = handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all players - not normally used as to load all players into memory will be wasteful
|
||||
*/
|
||||
public void load(){
|
||||
playerCache.clear();
|
||||
inTeleport.clear();
|
||||
handler.loadObjects().forEach(p -> playerCache.put(p.getPlayerUUID(), p));
|
||||
}
|
||||
|
||||
public boolean isSaveTaskRunning() {
|
||||
return isSaveTaskRunning;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save all players
|
||||
*/
|
||||
public void saveAll() {
|
||||
saveAll(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save all players
|
||||
* @param schedule true if we should let the task run over multiple ticks to reduce lag spikes
|
||||
*/
|
||||
public void saveAll(boolean schedule){
|
||||
if (!schedule) {
|
||||
for (Players player : playerCache.values()) {
|
||||
try {
|
||||
handler.saveObjectAsync(player);
|
||||
} catch (Exception e) {
|
||||
plugin.logError("Could not save player to database when running sync! " + e.getMessage());
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
isSaveTaskRunning = true;
|
||||
Queue<Players> queue = new LinkedList<>(playerCache.values());
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (int i = 0; i < plugin.getSettings().getMaxSavedPlayersPerTick(); i++) {
|
||||
Players player = queue.poll();
|
||||
if (player == null) {
|
||||
isSaveTaskRunning = false;
|
||||
cancel();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
handler.saveObjectAsync(player);
|
||||
} catch (Exception e) {
|
||||
plugin.logError("Could not save player to database when running sync! " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}.runTaskTimer(plugin, 0, 1);
|
||||
}
|
||||
|
||||
public void shutdown(){
|
||||
saveAll();
|
||||
playerCache.clear();
|
||||
handler.close();
|
||||
}
|
||||
|
||||
|
@ -133,10 +67,29 @@ public class PlayersManager {
|
|||
*/
|
||||
@Nullable
|
||||
public Players getPlayer(UUID uuid){
|
||||
if (!playerCache.containsKey(uuid)) {
|
||||
addPlayer(uuid);
|
||||
return playerCache.computeIfAbsent(uuid, this::addPlayer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a player to the database. If the UUID does not exist, a new player is created.
|
||||
*
|
||||
* @param playerUUID the player's UUID, must not be null
|
||||
* @return the loaded or newly created player
|
||||
* @throws NullPointerException if playerUUID is null
|
||||
*/
|
||||
private Players addPlayer(@NonNull UUID playerUUID) {
|
||||
Objects.requireNonNull(playerUUID, "Player UUID must not be null");
|
||||
|
||||
// If the player exists in the database, load it; otherwise, create and save a new player
|
||||
if (handler.objectExists(playerUUID.toString())) {
|
||||
Players player = handler.loadObject(playerUUID.toString());
|
||||
if (player != null) {
|
||||
return player;
|
||||
}
|
||||
}
|
||||
return playerCache.get(uuid);
|
||||
Players newPlayer = new Players(plugin, playerUUID);
|
||||
handler.saveObjectAsync(newPlayer);
|
||||
return newPlayer;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -146,37 +99,7 @@ public class PlayersManager {
|
|||
*/
|
||||
@NonNull
|
||||
public Collection<Players> getPlayers() {
|
||||
return Collections.unmodifiableCollection(playerCache.values());
|
||||
}
|
||||
|
||||
/*
|
||||
* Cache control methods
|
||||
*/
|
||||
|
||||
/**
|
||||
* Adds a player to the cache. If the UUID does not exist, a new player is made
|
||||
* @param playerUUID - the player's UUID
|
||||
*/
|
||||
public void addPlayer(UUID playerUUID) {
|
||||
if (playerUUID == null) {
|
||||
return;
|
||||
}
|
||||
if (!playerCache.containsKey(playerUUID)) {
|
||||
Players player;
|
||||
// If the player is in the database, load it, otherwise create a new player
|
||||
if (handler.objectExists(playerUUID.toString())) {
|
||||
player = handler.loadObject(playerUUID.toString());
|
||||
if (player == null) {
|
||||
player = new Players(plugin, playerUUID);
|
||||
// Corrupted database entry
|
||||
plugin.logError("Corrupted player database entry for " + playerUUID + " - unrecoverable. Recreated.");
|
||||
player.setUniqueId(playerUUID.toString());
|
||||
}
|
||||
} else {
|
||||
player = new Players(plugin, playerUUID);
|
||||
}
|
||||
playerCache.put(playerUUID, player);
|
||||
}
|
||||
return Collections.unmodifiableCollection(handler.loadObjects());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -187,7 +110,7 @@ public class PlayersManager {
|
|||
* @return true if player is known, otherwise false
|
||||
*/
|
||||
public boolean isKnown(UUID uniqueID) {
|
||||
return uniqueID != null && (playerCache.containsKey(uniqueID) || handler.objectExists(uniqueID.toString()));
|
||||
return uniqueID == null ? false : handler.objectExists(uniqueID.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -206,11 +129,8 @@ public class PlayersManager {
|
|||
// Not used
|
||||
}
|
||||
}
|
||||
// Look in the name cache, then the data base and then give up
|
||||
return playerCache.values().stream()
|
||||
.filter(p -> p.getPlayerName().equalsIgnoreCase(name)).findFirst()
|
||||
.map(p -> UUID.fromString(p.getUniqueId()))
|
||||
.orElseGet(() -> names.objectExists(name) ? names.loadObject(name).getUuid() : null);
|
||||
return names.loadObjects().stream().filter(n -> n.getUniqueId().equalsIgnoreCase(name)).findFirst()
|
||||
.map(Names::getUuid).orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -218,8 +138,9 @@ public class PlayersManager {
|
|||
* @param user - the User
|
||||
*/
|
||||
public void setPlayerName(@NonNull User user) {
|
||||
addPlayer(user.getUniqueId());
|
||||
playerCache.get(user.getUniqueId()).setPlayerName(user.getName());
|
||||
Players player = getPlayer(user.getUniqueId());
|
||||
player.setPlayerName(user.getName());
|
||||
handler.saveObject(player);
|
||||
Names newName = new Names(user.getName(), user.getUniqueId());
|
||||
// Add to names database
|
||||
names.saveObjectAsync(newName);
|
||||
|
@ -237,8 +158,8 @@ public class PlayersManager {
|
|||
if (playerUUID == null) {
|
||||
return "";
|
||||
}
|
||||
addPlayer(playerUUID);
|
||||
return playerCache.get(playerUUID).getPlayerName();
|
||||
getPlayer(playerUUID);
|
||||
return Objects.requireNonNullElse(playerCache.get(playerUUID).getPlayerName(), "");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -248,8 +169,7 @@ public class PlayersManager {
|
|||
* @return number of resets
|
||||
*/
|
||||
public int getResets(World world, UUID playerUUID) {
|
||||
addPlayer(playerUUID);
|
||||
return playerCache.get(playerUUID).getResets(world);
|
||||
return getPlayer(playerUUID).getResets(world);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -261,7 +181,7 @@ public class PlayersManager {
|
|||
* @see #getResets(World, UUID)
|
||||
*/
|
||||
public int getResetsLeft(World world, UUID playerUUID) {
|
||||
addPlayer(playerUUID);
|
||||
getPlayer(playerUUID);
|
||||
if (plugin.getIWM().getResetLimit(world) == -1) {
|
||||
return -1;
|
||||
} else {
|
||||
|
@ -277,8 +197,9 @@ public class PlayersManager {
|
|||
* @param resets number of resets to set
|
||||
*/
|
||||
public void setResets(World world, UUID playerUUID, int resets) {
|
||||
addPlayer(playerUUID);
|
||||
playerCache.get(playerUUID).setResets(world, resets);
|
||||
Players p = getPlayer(playerUUID);
|
||||
p.setResets(world, resets);
|
||||
handler.saveObject(p);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -287,11 +208,7 @@ public class PlayersManager {
|
|||
* @return name of the locale this player uses
|
||||
*/
|
||||
public String getLocale(UUID playerUUID) {
|
||||
addPlayer(playerUUID);
|
||||
if (playerUUID == null) {
|
||||
return "";
|
||||
}
|
||||
return playerCache.get(playerUUID).getLocale();
|
||||
return getPlayer(playerUUID).getLocale();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -300,8 +217,9 @@ public class PlayersManager {
|
|||
* @param localeName - locale name, e.g., en-US
|
||||
*/
|
||||
public void setLocale(UUID playerUUID, String localeName) {
|
||||
addPlayer(playerUUID);
|
||||
playerCache.get(playerUUID).setLocale(localeName);
|
||||
Players p = getPlayer(playerUUID);
|
||||
p.setLocale(localeName);
|
||||
handler.saveObject(p);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -310,8 +228,9 @@ public class PlayersManager {
|
|||
* @param playerUUID - the player's UUID
|
||||
*/
|
||||
public void addDeath(World world, UUID playerUUID) {
|
||||
addPlayer(playerUUID);
|
||||
playerCache.get(playerUUID).addDeath(world);
|
||||
Players p = getPlayer(playerUUID);
|
||||
p.addDeath(world);
|
||||
handler.saveObject(p);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -321,8 +240,9 @@ public class PlayersManager {
|
|||
* @param deaths - number of deaths
|
||||
*/
|
||||
public void setDeaths(World world, UUID playerUUID, int deaths) {
|
||||
addPlayer(playerUUID);
|
||||
playerCache.get(playerUUID).setDeaths(world, deaths);
|
||||
Players p = getPlayer(playerUUID);
|
||||
p.setDeaths(world, deaths);
|
||||
handler.saveObject(p);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -332,8 +252,7 @@ public class PlayersManager {
|
|||
* @return number of deaths
|
||||
*/
|
||||
public int getDeaths(World world, UUID playerUUID) {
|
||||
addPlayer(playerUUID);
|
||||
return playerCache.get(playerUUID) == null ? 0 : playerCache.get(playerUUID).getDeaths(world);
|
||||
return getPlayer(playerUUID).getDeaths(world);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -360,16 +279,6 @@ public class PlayersManager {
|
|||
return inTeleport.contains(uniqueId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the player to the database
|
||||
* @param playerUUID - the player's UUID
|
||||
*/
|
||||
public void save(UUID playerUUID) {
|
||||
if (playerCache.containsKey(playerUUID)) {
|
||||
handler.saveObjectAsync(playerCache.get(playerUUID));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to get the user from his name
|
||||
* @param name - name
|
||||
|
@ -395,41 +304,17 @@ public class PlayersManager {
|
|||
* @param playerUUID player's UUID
|
||||
*/
|
||||
public void addReset(World world, UUID playerUUID) {
|
||||
addPlayer(playerUUID);
|
||||
playerCache.get(playerUUID).addReset(world);
|
||||
Players p = getPlayer(playerUUID);
|
||||
p.addReset(world);
|
||||
handler.saveObject(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Flags display mode for the Settings Panel for this player.
|
||||
* @param playerUUID player's UUID
|
||||
* @param displayMode the {@link Flag.Mode} to set
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public void setFlagsDisplayMode(UUID playerUUID, Flag.Mode displayMode) {
|
||||
addPlayer(playerUUID);
|
||||
playerCache.get(playerUUID).setFlagsDisplayMode(displayMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Flags display mode for the Settings Panel for this player.
|
||||
* @param playerUUID player's UUID
|
||||
* @return the {@link Flag.Mode display mode} for the Flags in the Settings Panel.
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public Flag.Mode getFlagsDisplayMode(UUID playerUUID) {
|
||||
addPlayer(playerUUID);
|
||||
return playerCache.get(playerUUID).getFlagsDisplayMode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove player from cache. Clears players with the same name or UUID
|
||||
* Remove player from database
|
||||
* @param player player to remove
|
||||
*/
|
||||
public void removePlayer(Player player) {
|
||||
// Clear any players with the same name
|
||||
playerCache.values().removeIf(p -> player.getName().equalsIgnoreCase(p.getPlayerName()));
|
||||
// Remove if the player's UUID is the same
|
||||
playerCache.values().removeIf(p -> player.getUniqueId().toString().equals(p.getUniqueId()));
|
||||
handler.deleteID(player.getUniqueId().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -495,8 +380,6 @@ public class PlayersManager {
|
|||
// Player total XP (not displayed)
|
||||
target.getPlayer().setTotalExperience(0);
|
||||
}
|
||||
// Save player
|
||||
save(target.getUniqueId());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,15 +1,21 @@
|
|||
package world.bentobox.bentobox.managers.island;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
|
@ -17,7 +23,9 @@ import org.eclipse.jdt.annotation.Nullable;
|
|||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.api.flags.Flag;
|
||||
import world.bentobox.bentobox.database.Database;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
import world.bentobox.bentobox.managers.RanksManager;
|
||||
import world.bentobox.bentobox.util.Util;
|
||||
|
||||
/**
|
||||
|
@ -26,8 +34,6 @@ import world.bentobox.bentobox.util.Util;
|
|||
* @author tastybento
|
||||
*/
|
||||
public class IslandCache {
|
||||
@NonNull
|
||||
private final Map<@NonNull Location, @NonNull Island> islandsByLocation;
|
||||
/**
|
||||
* Map of all islands with island uniqueId as key
|
||||
*/
|
||||
|
@ -38,39 +44,73 @@ public class IslandCache {
|
|||
* UUID, value is a set of islands
|
||||
*/
|
||||
@NonNull
|
||||
private final Map<@NonNull UUID, Set<Island>> islandsByUUID;
|
||||
private final Map<@NonNull UUID, Set<String>> islandsByUUID;
|
||||
|
||||
@NonNull
|
||||
private final Map<@NonNull World, @NonNull IslandGrid> grids;
|
||||
private final @NonNull Database<Island> handler;
|
||||
|
||||
public IslandCache() {
|
||||
islandsByLocation = new HashMap<>();
|
||||
public IslandCache(@NonNull Database<Island> handler) {
|
||||
islandsById = new HashMap<>();
|
||||
islandsByUUID = new HashMap<>();
|
||||
grids = new HashMap<>();
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an island to the grid
|
||||
* Replace the island we have with this one
|
||||
* @param newIsland island
|
||||
*/
|
||||
public void updateIsland(@NonNull Island newIsland) {
|
||||
if (newIsland.isDeleted()) {
|
||||
this.deleteIslandFromCache(newIsland);
|
||||
return;
|
||||
}
|
||||
// Get the old island
|
||||
Island oldIsland = getIslandById(newIsland.getUniqueId());
|
||||
Set<UUID> newMembers = newIsland.getMembers().keySet();
|
||||
if (oldIsland != null) {
|
||||
Set<UUID> oldMembers = oldIsland.getMembers().keySet();
|
||||
// Remove any members who are not in the new island
|
||||
for (UUID oldMember : oldMembers) {
|
||||
if (!newMembers.contains(oldMember)) {
|
||||
// Member has been removed - remove island
|
||||
islandsByUUID.computeIfAbsent(oldMember, k -> new HashSet<>()).remove(oldIsland.getUniqueId());
|
||||
}
|
||||
}
|
||||
}
|
||||
// Update the members with the new island object
|
||||
for (UUID newMember : newMembers) {
|
||||
Set<String> set = islandsByUUID.computeIfAbsent(newMember, k -> new HashSet<>());
|
||||
if (oldIsland != null) {
|
||||
set.remove(oldIsland.getUniqueId());
|
||||
}
|
||||
set.add(newIsland.getUniqueId());
|
||||
islandsByUUID.put(newMember, set);
|
||||
}
|
||||
|
||||
if (setIslandById(newIsland) == null) {
|
||||
BentoBox.getInstance().logError("islandsById failed to update");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an island to the grid, used for new islands
|
||||
*
|
||||
* @param island island to add, not null
|
||||
* @return true if successfully added, false if not
|
||||
*/
|
||||
public boolean addIsland(@NonNull Island island) {
|
||||
if (island.getCenter() == null || island.getWorld() == null) {
|
||||
/*
|
||||
* Special handling - return true. The island will not be quarantined, but just
|
||||
* not loaded This can occur when a gamemode is removed temporarily from the
|
||||
* server TODO: have an option to remove these when the purge command is added
|
||||
*/
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
if (addToGrid(island)) {
|
||||
islandsByLocation.put(island.getCenter(), island);
|
||||
islandsById.put(island.getUniqueId(), island);
|
||||
// Insert a null into the map as a placeholder for cache
|
||||
islandsById.put(island.getUniqueId().intern(), null);
|
||||
// Only add islands to this map if they are owned
|
||||
if (island.isOwned()) {
|
||||
islandsByUUID.computeIfAbsent(island.getOwner(), k -> new HashSet<>()).add(island);
|
||||
islandsByUUID.computeIfAbsent(island.getOwner(), k -> new HashSet<>()).add(island.getUniqueId());
|
||||
island.getMemberSet().forEach(member -> addPlayer(member, island));
|
||||
}
|
||||
return true;
|
||||
|
@ -79,14 +119,14 @@ public class IslandCache {
|
|||
}
|
||||
|
||||
/**
|
||||
* Adds a player's UUID to the look up for islands. Does no checking
|
||||
* Adds a player's UUID to the look up for islands. Does no checking. The island for this player must have been added beforehand.
|
||||
*
|
||||
* @param uuid player's uuid
|
||||
* @param island island to associate with this uuid. Only one island can be
|
||||
* associated per world.
|
||||
*/
|
||||
public void addPlayer(@NonNull UUID uuid, @NonNull Island island) {
|
||||
islandsByUUID.computeIfAbsent(uuid, k -> new HashSet<>()).add(island);
|
||||
this.islandsByUUID.computeIfAbsent(uuid, k -> new HashSet<>()).add(island.getUniqueId());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -96,11 +136,10 @@ public class IslandCache {
|
|||
* @return true if successfully added, false if not
|
||||
*/
|
||||
private boolean addToGrid(@NonNull Island newIsland) {
|
||||
return grids.computeIfAbsent(newIsland.getWorld(), k -> new IslandGrid()).addToGrid(newIsland);
|
||||
return grids.computeIfAbsent(newIsland.getWorld(), k -> new IslandGrid(this)).addToGrid(newIsland);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
islandsByLocation.clear();
|
||||
islandsById.clear();
|
||||
islandsByUUID.clear();
|
||||
}
|
||||
|
@ -109,29 +148,19 @@ public class IslandCache {
|
|||
* Deletes an island from the cache. Does not remove blocks.
|
||||
*
|
||||
* @param island island to delete
|
||||
* @return true if successful, false if not
|
||||
*/
|
||||
public boolean deleteIslandFromCache(@NonNull Island island) {
|
||||
if (!islandsByLocation.remove(island.getCenter(), island)) {
|
||||
return false;
|
||||
}
|
||||
islandsById.remove(island.getUniqueId());
|
||||
public void deleteIslandFromCache(@NonNull Island island) {
|
||||
islandsById.remove(island.getUniqueId(), island);
|
||||
removeFromIslandsByUUID(island);
|
||||
// Remove from grid
|
||||
grids.putIfAbsent(island.getWorld(), new IslandGrid());
|
||||
return grids.get(island.getWorld()).removeFromGrid(island);
|
||||
if (grids.containsKey(island.getWorld())) {
|
||||
grids.get(island.getWorld()).removeFromGrid(island);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeFromIslandsByUUID(Island island) {
|
||||
for (Set<Island> set : islandsByUUID.values()) {
|
||||
Iterator<Island> is = set.iterator();
|
||||
while (is.hasNext()) {
|
||||
Island i = is.next();
|
||||
if (i.equals(island)) {
|
||||
is.remove();
|
||||
}
|
||||
}
|
||||
// set.removeIf(island::equals);
|
||||
for (Set<String> set : islandsByUUID.values()) {
|
||||
set.removeIf(island.getUniqueId()::equals);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -141,24 +170,11 @@ public class IslandCache {
|
|||
* @param uniqueId - island unique ID
|
||||
*/
|
||||
public void deleteIslandFromCache(@NonNull String uniqueId) {
|
||||
islandsById.remove(uniqueId);
|
||||
islandsByLocation.values().removeIf(i -> i.getUniqueId().equals(uniqueId));
|
||||
for (Set<Island> set : islandsByUUID.values()) {
|
||||
set.removeIf(i -> i.getUniqueId().equals(uniqueId));
|
||||
if (islandsById.containsKey(uniqueId)) {
|
||||
deleteIslandFromCache(getIslandById(uniqueId));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get island based on the exact center location of the island
|
||||
*
|
||||
* @param location location to search for
|
||||
* @return island or null if it does not exist
|
||||
*/
|
||||
@Nullable
|
||||
public Island get(@NonNull Location location) {
|
||||
return islandsByLocation.get(location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns island referenced by player's UUID. Returns the island the player is
|
||||
* on now, or their last known island
|
||||
|
@ -168,19 +184,19 @@ public class IslandCache {
|
|||
* @return island or null if none
|
||||
*/
|
||||
@Nullable
|
||||
public Island get(@NonNull World world, @NonNull UUID uuid) {
|
||||
Set<Island> islands = getIslands(world, uuid);
|
||||
public Island getIsland(@NonNull World world, @NonNull UUID uuid) {
|
||||
List<Island> islands = getIslands(world, uuid);
|
||||
if (islands.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
for (Island island : islands) {
|
||||
if (island.isPrimary()) {
|
||||
if (island.isPrimary(uuid)) {
|
||||
return island;
|
||||
}
|
||||
}
|
||||
// If there is no primary set, then set one - it doesn't matter which.
|
||||
Island result = islands.iterator().next();
|
||||
result.setPrimary(true);
|
||||
result.setPrimary(uuid);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -189,15 +205,17 @@ public class IslandCache {
|
|||
*
|
||||
* @param world world to check. Includes nether and end worlds.
|
||||
* @param uuid player's UUID
|
||||
* @return list of island or empty list if none
|
||||
* @return list of island or empty list if none sorted from oldest to youngest
|
||||
*/
|
||||
public Set<Island> getIslands(@NonNull World world, @NonNull UUID uuid) {
|
||||
public List<Island> getIslands(@NonNull World world, @NonNull UUID uuid) {
|
||||
World w = Util.getWorld(world);
|
||||
if (w == null) {
|
||||
return new HashSet<>();
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return islandsByUUID.computeIfAbsent(uuid, k -> new HashSet<>()).stream().filter(i -> w.equals(i.getWorld()))
|
||||
.collect(Collectors.toSet());
|
||||
return islandsByUUID.computeIfAbsent(uuid, k -> new HashSet<>()).stream().map(this::getIslandById)
|
||||
.filter(Objects::nonNull).filter(island -> w.equals(island.getWorld()))
|
||||
.sorted(Comparator.comparingLong(Island::getCreatedDate))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -207,8 +225,16 @@ public class IslandCache {
|
|||
* @param island island to make primary
|
||||
*/
|
||||
public void setPrimaryIsland(@NonNull UUID uuid, @NonNull Island island) {
|
||||
if (island.getPrimaries().contains(uuid)) {
|
||||
return;
|
||||
}
|
||||
for (Island is : getIslands(island.getWorld(), uuid)) {
|
||||
is.setPrimary(island.equals(is));
|
||||
if (is.getPrimaries().contains(uuid)) {
|
||||
is.removePrimary(uuid);
|
||||
}
|
||||
if (is.equals(island)) {
|
||||
is.setPrimary(uuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -230,18 +256,40 @@ public class IslandCache {
|
|||
|
||||
/**
|
||||
* Returns an <strong>unmodifiable collection</strong> of all the islands (even
|
||||
* those who may be unowned).
|
||||
* those who may be unowned). Gets them from the cache or from the database if not
|
||||
* loaded.
|
||||
*
|
||||
* @return unmodifiable collection containing every island.
|
||||
*/
|
||||
@NonNull
|
||||
public Collection<Island> getIslands() {
|
||||
return Collections.unmodifiableCollection(islandsByLocation.values());
|
||||
List<Island> result = new ArrayList<>();
|
||||
for (Entry<@NonNull String, @NonNull Island> entry : islandsById.entrySet()) {
|
||||
Island island = entry.getValue() != null ? entry.getValue() : handler.loadObject(entry.getKey());
|
||||
if (island != null) {
|
||||
result.add(island);
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.unmodifiableCollection(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an <strong>unmodifiable collection</strong> of all the islands (even
|
||||
* those who may be unowned) in the specified world.
|
||||
* those who may be unowned) that are cached.
|
||||
*
|
||||
* @return unmodifiable collection containing every cached island.
|
||||
*/
|
||||
@NonNull
|
||||
public Collection<Island> getCachedIslands() {
|
||||
return islandsById.entrySet().stream().filter(en -> Objects.nonNull(en.getValue())).map(Map.Entry::getValue)
|
||||
.toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an <strong>unmodifiable collection</strong> of all the islands (even
|
||||
* those that may be unowned) in the specified world.
|
||||
* Gets islands from the cache if they have been loaded, or from the database if not
|
||||
*
|
||||
* @param world World of the gamemode.
|
||||
* @return unmodifiable collection containing all the islands in the specified
|
||||
|
@ -254,9 +302,16 @@ public class IslandCache {
|
|||
if (overworld == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return islandsByLocation.entrySet().stream()
|
||||
.filter(entry -> overworld.equals(Util.getWorld(entry.getKey().getWorld()))) // shouldn't make NPEs
|
||||
.map(Map.Entry::getValue).toList();
|
||||
|
||||
List<Island> result = new ArrayList<>();
|
||||
for (Entry<@NonNull String, @NonNull Island> entry : islandsById.entrySet()) {
|
||||
Island island = entry.getValue() != null ? entry.getValue() : handler.loadObject(entry.getKey());
|
||||
if (island != null && overworld.equals(island.getWorld())) {
|
||||
result.add(island);
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.unmodifiableCollection(result);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -271,7 +326,8 @@ public class IslandCache {
|
|||
if (!islandsByUUID.containsKey(uuid)) {
|
||||
return false;
|
||||
}
|
||||
return this.islandsByUUID.get(uuid).stream().filter(i -> world.equals(i.getWorld()))
|
||||
return this.islandsByUUID.get(uuid).stream().map(this::getIslandById).filter(Objects::nonNull)
|
||||
.filter(i -> world.equals(i.getWorld()))
|
||||
.anyMatch(i -> uuid.equals(i.getOwner()));
|
||||
}
|
||||
|
||||
|
@ -281,20 +337,24 @@ public class IslandCache {
|
|||
*
|
||||
* @param world world
|
||||
* @param uuid player's UUID
|
||||
* @return list of islands player had or empty if none
|
||||
* @return set of islands player had or empty if none
|
||||
*/
|
||||
public Set<Island> removePlayer(@NonNull World world, @NonNull UUID uuid) {
|
||||
World w = Util.getWorld(world);
|
||||
Set<Island> islandSet = islandsByUUID.get(uuid);
|
||||
if (w == null || islandSet == null) {
|
||||
return Collections.emptySet(); // Return empty list if no islands map exists for the world
|
||||
World resolvedWorld = Util.getWorld(world);
|
||||
Set<String> playerIslandIds = islandsByUUID.get(uuid);
|
||||
Set<Island> removedIslands = new HashSet<>();
|
||||
|
||||
if (resolvedWorld == null || playerIslandIds == null) {
|
||||
return Collections.emptySet(); // Return empty set if no islands map exists for the world
|
||||
}
|
||||
// Go through all the islands associated with this player in this world and
|
||||
// remove the player from them.
|
||||
Iterator<Island> it = islandSet.iterator();
|
||||
while (it.hasNext()) {
|
||||
Island island = it.next();
|
||||
if (w.equals(island.getWorld())) {
|
||||
|
||||
// Iterate over the player's island IDs and process each associated island
|
||||
Iterator<String> iterator = playerIslandIds.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Island island = this.getIslandById(iterator.next());
|
||||
if (island != null && resolvedWorld.equals(island.getWorld())) {
|
||||
removedIslands.add(island);
|
||||
|
||||
if (uuid.equals(island.getOwner())) {
|
||||
// Player is the owner, so clear the whole island and clear the ownership
|
||||
island.getMembers().clear();
|
||||
|
@ -302,11 +362,13 @@ public class IslandCache {
|
|||
} else {
|
||||
island.removeMember(uuid);
|
||||
}
|
||||
// Remove this island from this set of islands associated to this player
|
||||
it.remove();
|
||||
|
||||
// Remove this island from the set of islands associated with this player
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
return islandSet;
|
||||
|
||||
return removedIslands;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -316,11 +378,12 @@ public class IslandCache {
|
|||
* @param uuid uuid of member to remove
|
||||
*/
|
||||
public void removePlayer(@NonNull Island island, @NonNull UUID uuid) {
|
||||
Set<Island> islandSet = islandsByUUID.get(uuid);
|
||||
Set<String> islandSet = islandsByUUID.get(uuid);
|
||||
if (islandSet != null) {
|
||||
islandSet.remove(island);
|
||||
islandSet.remove(island.getUniqueId());
|
||||
}
|
||||
island.removeMember(uuid);
|
||||
island.removePrimary(uuid);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -329,17 +392,17 @@ public class IslandCache {
|
|||
* @return the number of islands
|
||||
*/
|
||||
public int size() {
|
||||
return islandsByLocation.size();
|
||||
return islandsById.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of islands in the cache for this world
|
||||
* Gets the number of islands in this world
|
||||
*
|
||||
* @param world world to get the number of islands in
|
||||
* @return the number of islands
|
||||
*/
|
||||
public long size(World world) {
|
||||
return this.islandsByLocation.keySet().stream().map(Location::getWorld).filter(world::equals).count();
|
||||
return this.islandsById.values().stream().map(Island::getWorld).filter(world::equals).count();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -351,10 +414,10 @@ public class IslandCache {
|
|||
public void setOwner(@NonNull Island island, @Nullable UUID newOwnerUUID) {
|
||||
island.setOwner(newOwnerUUID);
|
||||
if (newOwnerUUID != null) {
|
||||
islandsByUUID.computeIfAbsent(newOwnerUUID, k -> new HashSet<>()).add(island);
|
||||
islandsByUUID.computeIfAbsent(newOwnerUUID, k -> new HashSet<>()).add(island.getUniqueId());
|
||||
}
|
||||
islandsByLocation.put(island.getCenter(), island);
|
||||
islandsById.put(island.getUniqueId(), island);
|
||||
island.setRank(newOwnerUUID, RanksManager.OWNER_RANK);
|
||||
setIslandById(island);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -366,42 +429,28 @@ public class IslandCache {
|
|||
*/
|
||||
@Nullable
|
||||
public Island getIslandById(@NonNull String uniqueId) {
|
||||
return islandsById.get(uniqueId);
|
||||
// Load from cache or database
|
||||
return islandsById.computeIfAbsent(uniqueId, handler::loadObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an island from the cache completely without altering the island
|
||||
* object
|
||||
*
|
||||
* @param island - island to remove
|
||||
* @since 1.3.0
|
||||
* Place the island into the cache map
|
||||
* @param island island
|
||||
* @return the previous value associated with island, or null if this is a new entry
|
||||
*/
|
||||
public void removeIsland(@NonNull Island island) {
|
||||
islandsByLocation.values().removeIf(island::equals);
|
||||
islandsById.values().removeIf(island::equals);
|
||||
islandsByUUID.values().removeIf(island::equals);
|
||||
World w = Util.getWorld(island.getWorld());
|
||||
if (w == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (grids.containsKey(w)) {
|
||||
grids.get(w).removeFromGrid(island);
|
||||
}
|
||||
Island setIslandById(Island island) {
|
||||
return islandsById.put(island.getUniqueId().intern(), island);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets all islands in this game mode to default flag settings
|
||||
* Resets all islands in this game mode to default flag settings.
|
||||
*
|
||||
* @param world - world
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public void resetAllFlags(World world) {
|
||||
World w = Util.getWorld(world);
|
||||
if (w == null) {
|
||||
return;
|
||||
}
|
||||
islandsById.values().stream().filter(i -> i.getWorld().equals(w)).forEach(Island::setFlagsDefaults);
|
||||
Bukkit.getScheduler().runTaskAsynchronously(BentoBox.getInstance(),
|
||||
() -> this.getIslands(world).stream().forEach(Island::setFlagsDefaults));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -412,13 +461,9 @@ public class IslandCache {
|
|||
* @since 1.8.0
|
||||
*/
|
||||
public void resetFlag(World world, Flag flag) {
|
||||
World w = Util.getWorld(world);
|
||||
if (w == null) {
|
||||
return;
|
||||
}
|
||||
int setting = BentoBox.getInstance().getIWM().getDefaultIslandFlags(w).getOrDefault(flag,
|
||||
int setting = BentoBox.getInstance().getIWM().getDefaultIslandFlags(world).getOrDefault(flag,
|
||||
flag.getDefaultRank());
|
||||
islandsById.values().stream().filter(i -> i.getWorld().equals(w)).forEach(i -> i.setFlag(flag, setting));
|
||||
this.getIslands(world).stream().forEach(i -> i.setFlag(flag, setting));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -431,4 +476,13 @@ public class IslandCache {
|
|||
return islandsById.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a unmodifiable list of all islands this player is involved with
|
||||
* @param uniqueId player's UUID
|
||||
* @return list of islands
|
||||
*/
|
||||
public @NonNull List<Island> getIslands(UUID uniqueId) {
|
||||
return islandsByUUID.getOrDefault(uniqueId, Collections.emptySet()).stream().map(this::getIslandById).toList();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue