mirror of
https://github.com/BentoBoxWorld/BentoBox.git
synced 2024-11-23 19:25:12 +01:00
Merge remote-tracking branch 'origin/develop' into master
# Conflicts: # pom.xml # src/main/java/world/bentobox/bentobox/blueprints/BlueprintPaster.java # src/main/java/world/bentobox/bentobox/database/objects/Island.java # src/main/java/world/bentobox/bentobox/versions/ServerCompatibility.java
This commit is contained in:
commit
1a59ca7785
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@ -13,11 +13,11 @@ jobs:
|
|||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
|
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
|
||||||
- name: Set up JDK 16
|
- name: Set up JDK 17
|
||||||
uses: actions/setup-java@v2
|
uses: actions/setup-java@v2
|
||||||
with:
|
with:
|
||||||
distribution: 'adopt'
|
distribution: 'adopt'
|
||||||
java-version: '16'
|
java-version: '17'
|
||||||
- name: Cache SonarCloud packages
|
- name: Cache SonarCloud packages
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
|
34
pom.xml
34
pom.xml
@ -63,27 +63,26 @@
|
|||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
<java.version>16</java.version>
|
<java.version>17</java.version>
|
||||||
<!-- Non-minecraft related dependencies -->
|
<!-- Non-minecraft related dependencies -->
|
||||||
<powermock.version>2.0.9</powermock.version>
|
<powermock.version>2.0.9</powermock.version>
|
||||||
<mongodb.version>3.12.8</mongodb.version>
|
<mongodb.version>3.12.8</mongodb.version>
|
||||||
<!-- More visible way to change dependency versions -->
|
<!-- More visible way to change dependency versions -->
|
||||||
<spigot.version>1.18-R0.1-SNAPSHOT</spigot.version>
|
<spigot.version>1.19.2-R0.1-SNAPSHOT</spigot.version>
|
||||||
<!-- Might differ from the last Spigot release for short periods
|
<!-- Might differ from the last Spigot release for short periods
|
||||||
of time -->
|
of time -->
|
||||||
<paper.version>1.16.5-R0.1-SNAPSHOT</paper.version>
|
<paper.version>1.19-R0.1-SNAPSHOT</paper.version>
|
||||||
<bstats.version>2.2.1</bstats.version>
|
<bstats.version>2.2.1</bstats.version>
|
||||||
<vault.version>1.7</vault.version>
|
<vault.version>1.7</vault.version>
|
||||||
<placeholderapi.version>2.10.9</placeholderapi.version>
|
<placeholderapi.version>2.10.9</placeholderapi.version>
|
||||||
<githubapi.version>d5f5e0bbd8</githubapi.version>
|
<githubapi.version>d5f5e0bbd8</githubapi.version>
|
||||||
<dynmap.version>3.0-SNAPSHOT</dynmap.version>
|
<dynmap.version>3.0-SNAPSHOT</dynmap.version>
|
||||||
<worldedit.version>7.2.5</worldedit.version>
|
|
||||||
<!-- Revision variable removes warning about dynamic version -->
|
<!-- Revision variable removes warning about dynamic version -->
|
||||||
<revision>${build.version}-SNAPSHOT</revision>
|
<revision>${build.version}-SNAPSHOT</revision>
|
||||||
<!-- Do not change unless you want different name for local builds. -->
|
<!-- Do not change unless you want different name for local builds. -->
|
||||||
<build.number>-LOCAL</build.number>
|
<build.number>-LOCAL</build.number>
|
||||||
<!-- This allows to change between versions. -->
|
<!-- This allows to change between versions. -->
|
||||||
<build.version>1.20.1</build.version>
|
<build.version>1.21.0</build.version>
|
||||||
<sonar.organization>bentobox-world</sonar.organization>
|
<sonar.organization>bentobox-world</sonar.organization>
|
||||||
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
|
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
|
||||||
</properties>
|
</properties>
|
||||||
@ -158,13 +157,9 @@
|
|||||||
<id>dynmap-repo</id>
|
<id>dynmap-repo</id>
|
||||||
<url>https://repo.mikeprimm.com/</url>
|
<url>https://repo.mikeprimm.com/</url>
|
||||||
</repository>
|
</repository>
|
||||||
<repository>
|
|
||||||
<id>worldedit-repo</id>
|
|
||||||
<url>https://maven.sk89q.com/repo/</url>
|
|
||||||
</repository>
|
|
||||||
<repository>
|
<repository>
|
||||||
<id>papermc</id>
|
<id>papermc</id>
|
||||||
<url>https://papermc.io/repo/repository/maven-public/</url>
|
<url>https://repo.papermc.io/repository/maven-public/</url>
|
||||||
</repository>
|
</repository>
|
||||||
<repository>
|
<repository>
|
||||||
<!-- This is a temporary reference as the Maven Shade plugin
|
<!-- This is a temporary reference as the Maven Shade plugin
|
||||||
@ -176,6 +171,11 @@
|
|||||||
<id>minecraft-repo</id>
|
<id>minecraft-repo</id>
|
||||||
<url>https://libraries.minecraft.net/</url>
|
<url>https://libraries.minecraft.net/</url>
|
||||||
</repository>
|
</repository>
|
||||||
|
<!-- Spigot NMS required for world regeneration :( -->
|
||||||
|
<repository>
|
||||||
|
<id>nms-repo</id>
|
||||||
|
<url>https://repo.codemc.io/repository/nms/</url>
|
||||||
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@ -188,7 +188,7 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
<!-- Paper API -->
|
<!-- Paper API -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.destroystokyo.paper</groupId>
|
<groupId>io.papermc.paper</groupId>
|
||||||
<artifactId>paper-api</artifactId>
|
<artifactId>paper-api</artifactId>
|
||||||
<version>${paper.version}</version>
|
<version>${paper.version}</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
@ -230,11 +230,13 @@
|
|||||||
<groupId>org.mongodb</groupId>
|
<groupId>org.mongodb</groupId>
|
||||||
<artifactId>mongodb-driver</artifactId>
|
<artifactId>mongodb-driver</artifactId>
|
||||||
<version>${mongodb.version}</version>
|
<version>${mongodb.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>postgresql</groupId>
|
<groupId>postgresql</groupId>
|
||||||
<artifactId>postgresql</artifactId>
|
<artifactId>postgresql</artifactId>
|
||||||
<version>9.1-901-1.jdbc4</version>
|
<version>9.1-901-1.jdbc4</version>
|
||||||
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- Vault: as their maven repo is down, we need to get it from jitpack -->
|
<!-- Vault: as their maven repo is down, we need to get it from jitpack -->
|
||||||
<!-- See https://github.com/MilkBowl/VaultAPI/issues/69 -->
|
<!-- See https://github.com/MilkBowl/VaultAPI/issues/69 -->
|
||||||
@ -258,12 +260,6 @@
|
|||||||
<version>${dynmap.version}</version>
|
<version>${dynmap.version}</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>com.sk89q.worldedit</groupId>
|
|
||||||
<artifactId>worldedit-core</artifactId>
|
|
||||||
<version>${worldedit.version}</version>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<!-- Shaded APIs -->
|
<!-- Shaded APIs -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.TheBusyBiscuit</groupId>
|
<groupId>com.github.TheBusyBiscuit</groupId>
|
||||||
@ -346,7 +342,7 @@
|
|||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
<version>3.8.1</version>
|
<version>3.8.1</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<release>16</release>
|
<release>${java.version}</release>
|
||||||
<!-- <source>${java.version}</source> <target>${java.version}</target> -->
|
<!-- <source>${java.version}</source> <target>${java.version}</target> -->
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
@ -396,7 +392,7 @@
|
|||||||
<artifactId>maven-javadoc-plugin</artifactId>
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
<version>3.3.0</version>
|
<version>3.3.0</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<source>16</source>
|
<source>${java.version}</source>
|
||||||
<show>private</show>
|
<show>private</show>
|
||||||
<failOnError>false</failOnError>
|
<failOnError>false</failOnError>
|
||||||
<additionalJOption>-Xdoclint:none</additionalJOption>
|
<additionalJOption>-Xdoclint:none</additionalJOption>
|
||||||
|
@ -19,7 +19,6 @@ import world.bentobox.bentobox.api.user.Notifier;
|
|||||||
import world.bentobox.bentobox.api.user.User;
|
import world.bentobox.bentobox.api.user.User;
|
||||||
import world.bentobox.bentobox.commands.BentoBoxCommand;
|
import world.bentobox.bentobox.commands.BentoBoxCommand;
|
||||||
import world.bentobox.bentobox.database.DatabaseSetup;
|
import world.bentobox.bentobox.database.DatabaseSetup;
|
||||||
import world.bentobox.bentobox.hooks.DynmapHook;
|
|
||||||
import world.bentobox.bentobox.hooks.MultiverseCoreHook;
|
import world.bentobox.bentobox.hooks.MultiverseCoreHook;
|
||||||
import world.bentobox.bentobox.hooks.VaultHook;
|
import world.bentobox.bentobox.hooks.VaultHook;
|
||||||
import world.bentobox.bentobox.hooks.placeholders.PlaceholderAPIHook;
|
import world.bentobox.bentobox.hooks.placeholders.PlaceholderAPIHook;
|
||||||
@ -28,7 +27,8 @@ import world.bentobox.bentobox.listeners.BlockEndDragon;
|
|||||||
import world.bentobox.bentobox.listeners.DeathListener;
|
import world.bentobox.bentobox.listeners.DeathListener;
|
||||||
import world.bentobox.bentobox.listeners.JoinLeaveListener;
|
import world.bentobox.bentobox.listeners.JoinLeaveListener;
|
||||||
import world.bentobox.bentobox.listeners.PanelListenerManager;
|
import world.bentobox.bentobox.listeners.PanelListenerManager;
|
||||||
import world.bentobox.bentobox.listeners.PortalTeleportationListener;
|
import world.bentobox.bentobox.listeners.teleports.EntityTeleportListener;
|
||||||
|
import world.bentobox.bentobox.listeners.teleports.PlayerTeleportListener;
|
||||||
import world.bentobox.bentobox.listeners.StandardSpawnProtectionListener;
|
import world.bentobox.bentobox.listeners.StandardSpawnProtectionListener;
|
||||||
import world.bentobox.bentobox.managers.AddonsManager;
|
import world.bentobox.bentobox.managers.AddonsManager;
|
||||||
import world.bentobox.bentobox.managers.BlueprintsManager;
|
import world.bentobox.bentobox.managers.BlueprintsManager;
|
||||||
@ -227,8 +227,8 @@ public class BentoBox extends JavaPlugin {
|
|||||||
hooksManager.registerHook(new MultiverseCoreHook());
|
hooksManager.registerHook(new MultiverseCoreHook());
|
||||||
islandWorldManager.registerWorldsToMultiverse();
|
islandWorldManager.registerWorldsToMultiverse();
|
||||||
|
|
||||||
// Register additional hooks
|
// TODO: re-enable after implementation
|
||||||
hooksManager.registerHook(new DynmapHook());
|
//hooksManager.registerHook(new DynmapHook());
|
||||||
// TODO: re-enable after rework
|
// TODO: re-enable after rework
|
||||||
//hooksManager.registerHook(new LangUtilsHook());
|
//hooksManager.registerHook(new LangUtilsHook());
|
||||||
|
|
||||||
@ -288,8 +288,10 @@ public class BentoBox extends JavaPlugin {
|
|||||||
manager.registerEvents(new PanelListenerManager(), this);
|
manager.registerEvents(new PanelListenerManager(), this);
|
||||||
// Standard Nether/End spawns protection
|
// Standard Nether/End spawns protection
|
||||||
manager.registerEvents(new StandardSpawnProtectionListener(this), this);
|
manager.registerEvents(new StandardSpawnProtectionListener(this), this);
|
||||||
// Nether portals
|
// Player portals
|
||||||
manager.registerEvents(new PortalTeleportationListener(this), this);
|
manager.registerEvents(new PlayerTeleportListener(this), this);
|
||||||
|
// Entity portals
|
||||||
|
manager.registerEvents(new EntityTeleportListener(this), this);
|
||||||
// End dragon blocking
|
// End dragon blocking
|
||||||
manager.registerEvents(new BlockEndDragon(this), this);
|
manager.registerEvents(new BlockEndDragon(this), this);
|
||||||
// Banned visitor commands
|
// Banned visitor commands
|
||||||
|
@ -314,6 +314,12 @@ public class Settings implements ConfigObject {
|
|||||||
@ConfigEntry(path = "island.safe-spot-search-vertical-range", since = "1.19.1")
|
@ConfigEntry(path = "island.safe-spot-search-vertical-range", since = "1.19.1")
|
||||||
private int safeSpotSearchVerticalRange = 400;
|
private int safeSpotSearchVerticalRange = 400;
|
||||||
|
|
||||||
|
@ConfigComment("By default, if the destination is not safe, the plugin will try to search for a safe spot around the destination.")
|
||||||
|
@ConfigComment("This allows to change the distance for searching this spot. Larger value will mean longer position search.")
|
||||||
|
@ConfigComment("This value is also used for valid nether portal linking between dimension.")
|
||||||
|
@ConfigEntry(path = "island.safe-spot-search-range", since = "1.21.0")
|
||||||
|
private int safeSpotSearchRange = 16;
|
||||||
|
|
||||||
/* WEB */
|
/* WEB */
|
||||||
@ConfigComment("Toggle whether BentoBox can connect to GitHub to get data about updates and addons.")
|
@ConfigComment("Toggle whether BentoBox can connect to GitHub to get data about updates and addons.")
|
||||||
@ConfigComment("Disabling this will result in the deactivation of the update checker and of some other")
|
@ConfigComment("Disabling this will result in the deactivation of the update checker and of some other")
|
||||||
@ -907,19 +913,65 @@ public class Settings implements ConfigObject {
|
|||||||
this.minPortalSearchRadius = minPortalSearchRadius;
|
this.minPortalSearchRadius = minPortalSearchRadius;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets safe spot search vertical range.
|
||||||
|
*
|
||||||
|
* @return the safe spot search vertical range
|
||||||
|
*/
|
||||||
public int getSafeSpotSearchVerticalRange() {
|
public int getSafeSpotSearchVerticalRange() {
|
||||||
return safeSpotSearchVerticalRange;
|
return safeSpotSearchVerticalRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets safe spot search vertical range.
|
||||||
|
*
|
||||||
|
* @param safeSpotSearchVerticalRange the safe spot search vertical range
|
||||||
|
*/
|
||||||
public void setSafeSpotSearchVerticalRange(int safeSpotSearchVerticalRange) {
|
public void setSafeSpotSearchVerticalRange(int safeSpotSearchVerticalRange) {
|
||||||
this.safeSpotSearchVerticalRange = safeSpotSearchVerticalRange;
|
this.safeSpotSearchVerticalRange = safeSpotSearchVerticalRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is slow deletion boolean.
|
||||||
|
*
|
||||||
|
* @return the boolean
|
||||||
|
*/
|
||||||
public boolean isSlowDeletion() {
|
public boolean isSlowDeletion() {
|
||||||
return slowDeletion;
|
return slowDeletion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets slow deletion.
|
||||||
|
*
|
||||||
|
* @param slowDeletion the slow deletion
|
||||||
|
*/
|
||||||
public void setSlowDeletion(boolean slowDeletion) {
|
public void setSlowDeletion(boolean slowDeletion) {
|
||||||
this.slowDeletion = slowDeletion;
|
this.slowDeletion = slowDeletion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets safe spot search range.
|
||||||
|
*
|
||||||
|
* @return the safe spot search range
|
||||||
|
*/
|
||||||
|
public int getSafeSpotSearchRange()
|
||||||
|
{
|
||||||
|
return safeSpotSearchRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets safe spot search range.
|
||||||
|
*
|
||||||
|
* @param safeSpotSearchRange the safe spot search range
|
||||||
|
*/
|
||||||
|
public void setSafeSpotSearchRange(int safeSpotSearchRange)
|
||||||
|
{
|
||||||
|
this.safeSpotSearchRange = safeSpotSearchRange;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -262,17 +262,44 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
|
|||||||
user.sendMessage("general.errors.use-in-game");
|
user.sendMessage("general.errors.use-in-game");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Check perms, but only if this isn't the console
|
|
||||||
if (user.isPlayer() && !user.isOp() && getPermission() != null && !getPermission().isEmpty() && !user.hasPermission(getPermission())) {
|
if (!this.runPermissionCheck(user))
|
||||||
user.sendMessage("general.errors.no-permission", TextVariables.PERMISSION, getPermission());
|
{
|
||||||
|
// Error message is displayed by permission check.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the user's addon context
|
// Set the user's addon context
|
||||||
user.setAddon(addon);
|
user.setAddon(addon);
|
||||||
// Execute and trim args
|
// Execute and trim args
|
||||||
return canExecute(user, cmdLabel, cmdArgs) && execute(user, cmdLabel, cmdArgs);
|
return canExecute(user, cmdLabel, cmdArgs) && execute(user, cmdLabel, cmdArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method checks and returns if user has access to the called command.
|
||||||
|
* It also recursively checks if user has access to the all parent commands.
|
||||||
|
* @param user User who permission must be checked.
|
||||||
|
* @return {@code true} is user can execute given command, {@code false} otherwise.
|
||||||
|
*/
|
||||||
|
private boolean runPermissionCheck(User user)
|
||||||
|
{
|
||||||
|
// Check perms, but only if this isn't the console
|
||||||
|
if (user.isPlayer() &&
|
||||||
|
!user.isOp() &&
|
||||||
|
this.getPermission() != null &&
|
||||||
|
!this.getPermission().isEmpty() &&
|
||||||
|
!user.hasPermission(this.getPermission()))
|
||||||
|
{
|
||||||
|
user.sendMessage("general.errors.no-permission", TextVariables.PERMISSION, this.getPermission());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursive permission check to find if user has access to the parent command.
|
||||||
|
return this.getParent() == null || this.getParent().runPermissionCheck(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current composite command based on the arguments
|
* Get the current composite command based on the arguments
|
||||||
* @param args - arguments
|
* @param args - arguments
|
||||||
@ -309,6 +336,14 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
|
|||||||
return plugin.getIslands();
|
return plugin.getIslands();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience method to get the island manager
|
||||||
|
* @return IslandsManager
|
||||||
|
*/
|
||||||
|
protected IslandsManager getIslandsManager() {
|
||||||
|
return plugin.getIslandsManager();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return this command's sub-level. Top level is 0.
|
* @return this command's sub-level. Top level is 0.
|
||||||
* Every time a command registers with a parent, their level will be set.
|
* Every time a command registers with a parent, their level will be set.
|
||||||
|
@ -0,0 +1,129 @@
|
|||||||
|
package world.bentobox.bentobox.api.commands.admin;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
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.Util;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This command resets players island name.
|
||||||
|
* @author BONNe
|
||||||
|
*/
|
||||||
|
public class AdminResetNameCommand extends CompositeCommand
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Default constructor.
|
||||||
|
* @param command Parent command.
|
||||||
|
*/
|
||||||
|
public AdminResetNameCommand(CompositeCommand command)
|
||||||
|
{
|
||||||
|
super(command, "resetname");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
this.setPermission("mod.resetname");
|
||||||
|
this.setOnlyPlayer(true);
|
||||||
|
this.setDescription("commands.admin.resetname.description");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param user the {@link User} who is executing this command.
|
||||||
|
* @param label the label which has been used to execute this command.
|
||||||
|
* It can be {@link CompositeCommand#getLabel()} or an alias.
|
||||||
|
* @param args the command arguments.
|
||||||
|
* @return {@code true} if name can be reset, {@code false} otherwise.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean canExecute(User user, String label, List<String> args)
|
||||||
|
{
|
||||||
|
if (args.size() == 1)
|
||||||
|
{
|
||||||
|
UUID playerUUID = Util.getUUID(args.get(0));
|
||||||
|
|
||||||
|
if (playerUUID == null)
|
||||||
|
{
|
||||||
|
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.island = this.getIslandsManager().getIsland(this.getWorld(), playerUUID);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.island = this.getIslandsManager().getIslandAt(user.getLocation()).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.island == null)
|
||||||
|
{
|
||||||
|
user.sendMessage("general.errors.player-has-no-island");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param user the {@link User} who is executing this command.
|
||||||
|
* @param label the label which has been used to execute this command.
|
||||||
|
* It can be {@link CompositeCommand#getLabel()} or an alias.
|
||||||
|
* @param args the command arguments.
|
||||||
|
* @return {@code true}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean execute(User user, String label, List<String> args)
|
||||||
|
{
|
||||||
|
if (this.island == null)
|
||||||
|
{
|
||||||
|
this.showHelp(this, user);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resets the island name
|
||||||
|
this.island.setName(null);
|
||||||
|
user.sendMessage("commands.admin.resetname.success", TextVariables.NAME, this.getPlayers().getName(this.island.getOwner()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param user the {@link User} who is executing this command.
|
||||||
|
* @param alias alias for command
|
||||||
|
* @param args command arguments
|
||||||
|
* @return Optional of possible values.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
|
||||||
|
// Return the player names
|
||||||
|
|
||||||
|
if (args.size() == 1)
|
||||||
|
{
|
||||||
|
return Optional.of(Util.getOnlinePlayerList(user));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Island which name must be changed.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
private Island island;
|
||||||
|
}
|
@ -9,11 +9,7 @@ import world.bentobox.bentobox.api.commands.admin.deaths.AdminDeathsCommand;
|
|||||||
import world.bentobox.bentobox.api.commands.admin.purge.AdminPurgeCommand;
|
import world.bentobox.bentobox.api.commands.admin.purge.AdminPurgeCommand;
|
||||||
import world.bentobox.bentobox.api.commands.admin.range.AdminRangeCommand;
|
import world.bentobox.bentobox.api.commands.admin.range.AdminRangeCommand;
|
||||||
import world.bentobox.bentobox.api.commands.admin.resets.AdminResetsCommand;
|
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.*;
|
||||||
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;
|
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||||
import world.bentobox.bentobox.api.user.User;
|
import world.bentobox.bentobox.api.user.User;
|
||||||
|
|
||||||
@ -42,7 +38,7 @@ public abstract class DefaultAdminCommand extends CompositeCommand {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void setup() {
|
public void setup() {
|
||||||
this.setPermission("admin.*");
|
this.setPermission("admin");
|
||||||
this.setOnlyPlayer(false);
|
this.setOnlyPlayer(false);
|
||||||
|
|
||||||
this.setParametersHelp("commands.admin.help.parameters");
|
this.setParametersHelp("commands.admin.help.parameters");
|
||||||
@ -56,6 +52,7 @@ public abstract class DefaultAdminCommand extends CompositeCommand {
|
|||||||
new AdminSetrankCommand(this);
|
new AdminSetrankCommand(this);
|
||||||
new AdminInfoCommand(this);
|
new AdminInfoCommand(this);
|
||||||
// Team commands
|
// Team commands
|
||||||
|
new AdminTeamCommand(this);
|
||||||
new AdminTeamAddCommand(this);
|
new AdminTeamAddCommand(this);
|
||||||
new AdminTeamKickCommand(this);
|
new AdminTeamKickCommand(this);
|
||||||
new AdminTeamDisbandCommand(this);
|
new AdminTeamDisbandCommand(this);
|
||||||
@ -94,6 +91,8 @@ public abstract class DefaultAdminCommand extends CompositeCommand {
|
|||||||
new AdminSetProtectionCenterCommand(this);
|
new AdminSetProtectionCenterCommand(this);
|
||||||
// Delete homes
|
// Delete homes
|
||||||
new AdminDeleteHomesCommand(this);
|
new AdminDeleteHomesCommand(this);
|
||||||
|
// Reset name
|
||||||
|
new AdminResetNameCommand(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -62,20 +62,30 @@ public class AdminBlueprintCommand extends ConfirmableCommand {
|
|||||||
return clipboards;
|
return clipboards;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void showClipboard(User user) {
|
|
||||||
displayClipboards.putIfAbsent(user, Bukkit.getScheduler().scheduleSyncRepeatingTask(getPlugin(), () -> {
|
|
||||||
if (!user.isPlayer() || !user.getPlayer().isOnline()) {
|
|
||||||
hideClipboard(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (clipboards.containsKey(user.getUniqueId())) {
|
/**
|
||||||
BlueprintClipboard clipboard = clipboards.get(user.getUniqueId());
|
* This method shows clipboard for requested user.
|
||||||
paintAxis(user, clipboard);
|
* @param user User who need to see clipboard.
|
||||||
}
|
*/
|
||||||
|
protected void showClipboard(User user)
|
||||||
|
{
|
||||||
|
this.displayClipboards.computeIfAbsent(user,
|
||||||
|
key -> Bukkit.getScheduler().scheduleSyncRepeatingTask(this.getPlugin(), () ->
|
||||||
|
{
|
||||||
|
if (!key.isPlayer() || !key.getPlayer().isOnline())
|
||||||
|
{
|
||||||
|
this.hideClipboard(key);
|
||||||
|
}
|
||||||
|
|
||||||
}, 20, 20));
|
if (this.clipboards.containsKey(key.getUniqueId()))
|
||||||
|
{
|
||||||
|
BlueprintClipboard clipboard = this.clipboards.get(key.getUniqueId());
|
||||||
|
this.paintAxis(key, clipboard);
|
||||||
|
}
|
||||||
|
}, 20, 20));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void paintAxis(User user, BlueprintClipboard clipboard) {
|
private void paintAxis(User user, BlueprintClipboard clipboard) {
|
||||||
if (clipboard.getPos1() == null || clipboard.getPos2() == null) {
|
if (clipboard.getPos1() == null || clipboard.getPos2() == null) {
|
||||||
return;
|
return;
|
||||||
|
@ -1,35 +1,54 @@
|
|||||||
package world.bentobox.bentobox.api.commands.admin.blueprints;
|
package world.bentobox.bentobox.api.commands.admin.blueprints;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||||
import world.bentobox.bentobox.api.user.User;
|
import world.bentobox.bentobox.api.user.User;
|
||||||
import world.bentobox.bentobox.blueprints.BlueprintClipboard;
|
import world.bentobox.bentobox.blueprints.BlueprintClipboard;
|
||||||
|
|
||||||
public class AdminBlueprintCopyCommand extends CompositeCommand {
|
|
||||||
|
|
||||||
public AdminBlueprintCopyCommand(AdminBlueprintCommand parent) {
|
public class AdminBlueprintCopyCommand extends CompositeCommand
|
||||||
|
{
|
||||||
|
public AdminBlueprintCopyCommand(AdminBlueprintCommand parent)
|
||||||
|
{
|
||||||
super(parent, "copy");
|
super(parent, "copy");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setup() {
|
public void setup()
|
||||||
|
{
|
||||||
inheritPermission();
|
inheritPermission();
|
||||||
setParametersHelp("commands.admin.blueprint.copy.parameters");
|
setParametersHelp("commands.admin.blueprint.copy.parameters");
|
||||||
setDescription("commands.admin.blueprint.copy.description");
|
setDescription("commands.admin.blueprint.copy.description");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean execute(User user, String label, List<String> args) {
|
public boolean execute(User user, String label, List<String> args)
|
||||||
if (args.size() > 1) {
|
{
|
||||||
showHelp(this, user);
|
if (args.size() > 2)
|
||||||
|
{
|
||||||
|
this.showHelp(this, user);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
AdminBlueprintCommand parent = (AdminBlueprintCommand) getParent();
|
AdminBlueprintCommand parent = (AdminBlueprintCommand) getParent();
|
||||||
|
|
||||||
BlueprintClipboard clipboard = parent.getClipboards().computeIfAbsent(user.getUniqueId(), v -> new BlueprintClipboard());
|
BlueprintClipboard clipboard =
|
||||||
boolean copyAir = (args.size() == 1 && args.get(0).equalsIgnoreCase("air"));
|
parent.getClipboards().computeIfAbsent(user.getUniqueId(), v -> new BlueprintClipboard());
|
||||||
return clipboard.copy(user, copyAir);
|
|
||||||
|
boolean copyAir = args.stream().anyMatch(key -> key.equalsIgnoreCase("air"));
|
||||||
|
boolean copyBiome = args.stream().anyMatch(key -> key.equalsIgnoreCase("biome"));
|
||||||
|
|
||||||
|
return clipboard.copy(user, copyAir, copyBiome);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<List<String>> tabComplete(User user, String alias, List<String> args)
|
||||||
|
{
|
||||||
|
return Optional.of(List.of("air", "biome"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,49 +8,63 @@ import java.util.Optional;
|
|||||||
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
|
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
|
||||||
import world.bentobox.bentobox.api.localization.TextVariables;
|
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||||
import world.bentobox.bentobox.api.user.User;
|
import world.bentobox.bentobox.api.user.User;
|
||||||
|
import world.bentobox.bentobox.util.Util;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Command that deletes a Blueprint.
|
* Command that deletes a Blueprint.
|
||||||
* @author Poslovitch
|
* @author Poslovitch
|
||||||
* @since 1.9.0
|
* @since 1.9.0
|
||||||
*/
|
*/
|
||||||
public class AdminBlueprintDeleteCommand extends ConfirmableCommand {
|
public class AdminBlueprintDeleteCommand extends ConfirmableCommand
|
||||||
|
{
|
||||||
public AdminBlueprintDeleteCommand(AdminBlueprintCommand parent) {
|
public AdminBlueprintDeleteCommand(AdminBlueprintCommand parent)
|
||||||
|
{
|
||||||
super(parent, "delete", "remove");
|
super(parent, "delete", "remove");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setup() {
|
|
||||||
inheritPermission();
|
|
||||||
setParametersHelp("commands.admin.blueprint.delete.parameters");
|
|
||||||
setDescription("commands.admin.blueprint.delete.description");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean execute(User user, String label, List<String> args) {
|
public void setup()
|
||||||
if (args.size() != 1) {
|
{
|
||||||
showHelp(this, user);
|
this.inheritPermission();
|
||||||
|
this.setParametersHelp("commands.admin.blueprint.delete.parameters");
|
||||||
|
this.setDescription("commands.admin.blueprint.delete.description");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean execute(User user, String label, List<String> args)
|
||||||
|
{
|
||||||
|
if (args.size() != 1)
|
||||||
|
{
|
||||||
|
this.showHelp(this, user);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
String blueprintName = args.get(0).toLowerCase(Locale.ENGLISH);
|
String blueprintName = Util.sanitizeInput(args.get(0));
|
||||||
|
|
||||||
// Check if blueprint exist
|
// Check if blueprint exist
|
||||||
if (getPlugin().getBlueprintsManager().getBlueprints(getAddon()).containsKey(blueprintName)) {
|
if (this.getPlugin().getBlueprintsManager().getBlueprints(this.getAddon()).containsKey(blueprintName))
|
||||||
askConfirmation(user, user.getTranslation("commands.admin.blueprint.delete.confirmation"), () -> {
|
{
|
||||||
getPlugin().getBlueprintsManager().deleteBlueprint(getAddon(), blueprintName);
|
this.askConfirmation(user, user.getTranslation("commands.admin.blueprint.delete.confirmation"),
|
||||||
user.sendMessage("commands.admin.blueprint.delete.success", TextVariables.NAME, blueprintName);
|
() -> {
|
||||||
});
|
this.getPlugin().getBlueprintsManager().deleteBlueprint(this.getAddon(), blueprintName);
|
||||||
|
user.sendMessage("commands.admin.blueprint.delete.success", TextVariables.NAME, blueprintName);
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
user.sendMessage("commands.admin.blueprint.delete.no-blueprint", TextVariables.NAME, blueprintName);
|
user.sendMessage("commands.admin.blueprint.delete.no-blueprint", TextVariables.NAME, blueprintName);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
|
public Optional<List<String>> tabComplete(User user, String alias, List<String> args)
|
||||||
return Optional.of(new LinkedList<>(getPlugin().getBlueprintsManager().getBlueprints(getAddon()).keySet()));
|
{
|
||||||
|
return Optional.of(new LinkedList<>(this.getPlugin().getBlueprintsManager().getBlueprints(this.getAddon()).keySet()));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -5,51 +5,66 @@ import java.io.FilenameFilter;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||||
import world.bentobox.bentobox.api.user.User;
|
import world.bentobox.bentobox.api.user.User;
|
||||||
import world.bentobox.bentobox.managers.BlueprintsManager;
|
import world.bentobox.bentobox.managers.BlueprintsManager;
|
||||||
|
|
||||||
public class AdminBlueprintListCommand extends CompositeCommand {
|
public class AdminBlueprintListCommand extends CompositeCommand
|
||||||
|
{
|
||||||
|
|
||||||
public AdminBlueprintListCommand(AdminBlueprintCommand parent) {
|
public AdminBlueprintListCommand(AdminBlueprintCommand parent)
|
||||||
|
{
|
||||||
super(parent, "list");
|
super(parent, "list");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setup() {
|
public void setup()
|
||||||
inheritPermission();
|
{
|
||||||
setDescription("commands.admin.blueprint.list.description");
|
this.inheritPermission();
|
||||||
|
this.setDescription("commands.admin.blueprint.list.description");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canExecute(User user, String label, List<String> args) {
|
public boolean canExecute(User user, String label, List<String> args)
|
||||||
if (!args.isEmpty()) {
|
{
|
||||||
showHelp(this, user);
|
if (!args.isEmpty())
|
||||||
|
{
|
||||||
|
this.showHelp(this, user);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean execute(User user, String label, List<String> args) {
|
public boolean execute(User user, String label, List<String> args)
|
||||||
File blueprints = new File(getAddon().getDataFolder(), BlueprintsManager.FOLDER_NAME);
|
{
|
||||||
if (!blueprints.exists()) {
|
File blueprints = new File(this.getAddon().getDataFolder(), BlueprintsManager.FOLDER_NAME);
|
||||||
|
|
||||||
|
if (!blueprints.exists())
|
||||||
|
{
|
||||||
user.sendMessage("commands.admin.blueprint.list.no-blueprints");
|
user.sendMessage("commands.admin.blueprint.list.no-blueprints");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
FilenameFilter blueprintFilter = (File dir, String name) -> name.toLowerCase(java.util.Locale.ENGLISH).endsWith(BlueprintsManager.BLUEPRINT_SUFFIX);
|
|
||||||
List<String> blueprintList = Arrays.stream(Objects.requireNonNull(blueprints.list(blueprintFilter))).map(name -> name.substring(0, name.length() - BlueprintsManager.BLUEPRINT_SUFFIX.length())).collect(Collectors.toList());
|
FilenameFilter blueprintFilter = (File dir, String name) -> name.endsWith(BlueprintsManager.BLUEPRINT_SUFFIX);
|
||||||
if (blueprintList.isEmpty()) {
|
|
||||||
|
List<String> blueprintList = Arrays.stream(Objects.requireNonNull(blueprints.list(blueprintFilter))).
|
||||||
|
map(name -> name.substring(0, name.length() - BlueprintsManager.BLUEPRINT_SUFFIX.length())).
|
||||||
|
toList();
|
||||||
|
|
||||||
|
if (blueprintList.isEmpty())
|
||||||
|
{
|
||||||
user.sendMessage("commands.admin.blueprint.list.no-blueprints");
|
user.sendMessage("commands.admin.blueprint.list.no-blueprints");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
user.sendMessage("commands.admin.blueprint.list.available-blueprints");
|
user.sendMessage("commands.admin.blueprint.list.available-blueprints");
|
||||||
blueprintList.forEach(user::sendRawMessage);
|
blueprintList.forEach(user::sendRawMessage);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ public class AdminBlueprintLoadCommand extends CompositeCommand {
|
|||||||
AdminBlueprintCommand parent = (AdminBlueprintCommand) getParent();
|
AdminBlueprintCommand parent = (AdminBlueprintCommand) getParent();
|
||||||
|
|
||||||
BlueprintClipboardManager bp = new BlueprintClipboardManager(getPlugin(), parent.getBlueprintsFolder());
|
BlueprintClipboardManager bp = new BlueprintClipboardManager(getPlugin(), parent.getBlueprintsFolder());
|
||||||
if (bp.load(user, args.get(0))) {
|
if (bp.load(user, Util.sanitizeInput(args.get(0)))) {
|
||||||
parent.getClipboards().put(user.getUniqueId(), bp.getClipboard());
|
parent.getClipboards().put(user.getUniqueId(), bp.getClipboard());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -8,66 +8,107 @@ import world.bentobox.bentobox.api.commands.ConfirmableCommand;
|
|||||||
import world.bentobox.bentobox.api.localization.TextVariables;
|
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||||
import world.bentobox.bentobox.api.user.User;
|
import world.bentobox.bentobox.api.user.User;
|
||||||
import world.bentobox.bentobox.blueprints.Blueprint;
|
import world.bentobox.bentobox.blueprints.Blueprint;
|
||||||
|
import world.bentobox.bentobox.blueprints.BlueprintClipboard;
|
||||||
import world.bentobox.bentobox.managers.BlueprintsManager;
|
import world.bentobox.bentobox.managers.BlueprintsManager;
|
||||||
|
import world.bentobox.bentobox.util.Util;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renames an existing blueprint.
|
* Renames an existing blueprint.
|
||||||
* @author Poslovitch
|
* @author Poslovitch
|
||||||
* @since 1.10.0
|
* @since 1.10.0
|
||||||
*/
|
*/
|
||||||
public class AdminBlueprintRenameCommand extends ConfirmableCommand {
|
public class AdminBlueprintRenameCommand extends ConfirmableCommand
|
||||||
|
{
|
||||||
public AdminBlueprintRenameCommand(AdminBlueprintCommand parent) {
|
public AdminBlueprintRenameCommand(AdminBlueprintCommand parent)
|
||||||
|
{
|
||||||
super(parent, "rename");
|
super(parent, "rename");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setup() {
|
|
||||||
inheritPermission();
|
|
||||||
setParametersHelp("commands.admin.blueprint.rename.parameters");
|
|
||||||
setDescription("commands.admin.blueprint.rename.description");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean execute(User user, String label, List<String> args) {
|
public void setup()
|
||||||
if (args.size() != 2) {
|
{
|
||||||
showHelp(this, user);
|
this.inheritPermission();
|
||||||
|
this.setParametersHelp("commands.admin.blueprint.rename.parameters");
|
||||||
|
this.setDescription("commands.admin.blueprint.rename.description");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canExecute(User user, String label, List<String> args)
|
||||||
|
{
|
||||||
|
if (args.size() != 2)
|
||||||
|
{
|
||||||
|
// Blueprint must have a name.
|
||||||
|
this.showHelp(this, user);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
AdminBlueprintCommand parent = (AdminBlueprintCommand) getParent();
|
String from = Util.sanitizeInput(args.get(0));
|
||||||
|
String to = Util.sanitizeInput(args.get(1));
|
||||||
|
|
||||||
// Check if the names are the same
|
// Check if name is changed.
|
||||||
String from = args.get(0).toLowerCase(Locale.ENGLISH);
|
if (from.equals(to))
|
||||||
String to = args.get(1).toLowerCase(Locale.ENGLISH);
|
{
|
||||||
|
|
||||||
if (from.equals(to)) {
|
|
||||||
user.sendMessage("commands.admin.blueprint.rename.pick-different-name");
|
user.sendMessage("commands.admin.blueprint.rename.pick-different-name");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the 'from' file exists
|
// Check if the 'from' file exists
|
||||||
|
AdminBlueprintCommand parent = (AdminBlueprintCommand) this.getParent();
|
||||||
File fromFile = new File(parent.getBlueprintsFolder(), from + BlueprintsManager.BLUEPRINT_SUFFIX);
|
File fromFile = new File(parent.getBlueprintsFolder(), from + BlueprintsManager.BLUEPRINT_SUFFIX);
|
||||||
if (!fromFile.exists()) {
|
|
||||||
|
if (!fromFile.exists())
|
||||||
|
{
|
||||||
user.sendMessage("commands.admin.blueprint.no-such-file");
|
user.sendMessage("commands.admin.blueprint.no-such-file");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the 'to' file exists
|
|
||||||
|
|
||||||
File toFile = new File(parent.getBlueprintsFolder(), to + BlueprintsManager.BLUEPRINT_SUFFIX);
|
|
||||||
if (toFile.exists()) {
|
|
||||||
// Ask for confirmation to overwrite
|
|
||||||
askConfirmation(user, user.getTranslation("commands.admin.blueprint.file-exists"), () -> rename(user, from, to));
|
|
||||||
} else {
|
|
||||||
askConfirmation(user, () -> rename(user, from, to));
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void rename(User user, String blueprintName, String newName) {
|
|
||||||
Blueprint blueprint = getPlugin().getBlueprintsManager().getBlueprints(getAddon()).get(blueprintName);
|
@Override
|
||||||
getPlugin().getBlueprintsManager().renameBlueprint(getAddon(), blueprint, newName);
|
public boolean execute(User user, String label, List<String> args)
|
||||||
user.sendMessage("commands.admin.blueprint.rename.success", "[old]", blueprintName, TextVariables.NAME, blueprint.getName());
|
{
|
||||||
|
AdminBlueprintCommand parent = (AdminBlueprintCommand) getParent();
|
||||||
|
|
||||||
|
// Check if the names are the same
|
||||||
|
String from = Util.sanitizeInput(args.get(0));
|
||||||
|
String to = Util.sanitizeInput(args.get(1));
|
||||||
|
|
||||||
|
// Check if the 'to' file exists
|
||||||
|
File toFile = new File(parent.getBlueprintsFolder(), to + BlueprintsManager.BLUEPRINT_SUFFIX);
|
||||||
|
|
||||||
|
if (toFile.exists())
|
||||||
|
{
|
||||||
|
// Ask for confirmation to overwrite
|
||||||
|
this.askConfirmation(user,
|
||||||
|
user.getTranslation("commands.admin.blueprint.file-exists"),
|
||||||
|
() -> this.rename(user, from, to, args.get(1)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.askConfirmation(user, () -> this.rename(user, from, to, args.get(1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void rename(User user, String blueprintName, String fileName, String displayName)
|
||||||
|
{
|
||||||
|
Blueprint blueprint = this.getPlugin().getBlueprintsManager().getBlueprints(this.getAddon()).get(blueprintName);
|
||||||
|
|
||||||
|
this.getPlugin().getBlueprintsManager().renameBlueprint(this.getAddon(), blueprint, fileName, displayName);
|
||||||
|
|
||||||
|
user.sendMessage("commands.admin.blueprint.rename.success",
|
||||||
|
"[old]",
|
||||||
|
blueprintName,
|
||||||
|
TextVariables.NAME,
|
||||||
|
blueprint.getName(),
|
||||||
|
"[display]",
|
||||||
|
blueprint.getDisplayName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,62 +2,115 @@ package world.bentobox.bentobox.api.commands.admin.blueprints;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
|
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
|
||||||
import world.bentobox.bentobox.api.user.User;
|
import world.bentobox.bentobox.api.user.User;
|
||||||
import world.bentobox.bentobox.blueprints.BlueprintClipboard;
|
import world.bentobox.bentobox.blueprints.BlueprintClipboard;
|
||||||
import world.bentobox.bentobox.managers.BlueprintClipboardManager;
|
import world.bentobox.bentobox.managers.BlueprintClipboardManager;
|
||||||
import world.bentobox.bentobox.managers.BlueprintsManager;
|
import world.bentobox.bentobox.managers.BlueprintsManager;
|
||||||
|
import world.bentobox.bentobox.util.Util;
|
||||||
|
|
||||||
public class AdminBlueprintSaveCommand extends ConfirmableCommand {
|
|
||||||
|
|
||||||
public AdminBlueprintSaveCommand(AdminBlueprintCommand parent) {
|
/**
|
||||||
|
* This method allows to save blueprint from the clipboard.
|
||||||
|
*/
|
||||||
|
public class AdminBlueprintSaveCommand extends ConfirmableCommand
|
||||||
|
{
|
||||||
|
public AdminBlueprintSaveCommand(AdminBlueprintCommand parent)
|
||||||
|
{
|
||||||
super(parent, "save");
|
super(parent, "save");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setup() {
|
|
||||||
inheritPermission();
|
|
||||||
setParametersHelp("commands.admin.blueprint.save.parameters");
|
|
||||||
setDescription("commands.admin.blueprint.save.description");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean execute(User user, String label, List<String> args) {
|
public void setup()
|
||||||
if (args.size() != 1) {
|
{
|
||||||
showHelp(this, user);
|
this.inheritPermission();
|
||||||
|
this.setParametersHelp("commands.admin.blueprint.save.parameters");
|
||||||
|
this.setDescription("commands.admin.blueprint.save.description");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canExecute(User user, String label, List<String> args)
|
||||||
|
{
|
||||||
|
if (args.size() != 1)
|
||||||
|
{
|
||||||
|
// Blueprint must have a name.
|
||||||
|
this.showHelp(this, user);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
AdminBlueprintCommand parent = (AdminBlueprintCommand) getParent();
|
BlueprintClipboard clipboard = ((AdminBlueprintCommand) this.getParent()).getClipboards().
|
||||||
BlueprintClipboard clipboard = parent.getClipboards().computeIfAbsent(user.getUniqueId(), v -> new BlueprintClipboard());
|
computeIfAbsent(user.getUniqueId(), v -> new BlueprintClipboard());
|
||||||
String fileName = args.get(0).toLowerCase(Locale.ENGLISH);
|
|
||||||
if (clipboard.isFull()) {
|
if (!clipboard.isFull())
|
||||||
// Check if blueprint had bedrock
|
{
|
||||||
if (clipboard.getBlueprint().getBedrock() == null) {
|
// Clipboard is not set up.
|
||||||
user.sendMessage("commands.admin.blueprint.bedrock-required");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Check if file exists
|
|
||||||
File newFile = new File(parent.getBlueprintsFolder(), fileName + BlueprintsManager.BLUEPRINT_SUFFIX);
|
|
||||||
if (newFile.exists()) {
|
|
||||||
this.askConfirmation(user, user.getTranslation("commands.admin.blueprint.file-exists"), () -> hideAndSave(user, parent, clipboard, fileName));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return hideAndSave(user, parent, clipboard, fileName);
|
|
||||||
} else {
|
|
||||||
user.sendMessage("commands.admin.blueprint.copy-first");
|
user.sendMessage("commands.admin.blueprint.copy-first");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (clipboard.getBlueprint().getBedrock() == null)
|
||||||
|
{
|
||||||
|
// Bedrock is required for all blueprints.
|
||||||
|
user.sendMessage("commands.admin.blueprint.bedrock-required");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hideAndSave(User user, AdminBlueprintCommand parent, BlueprintClipboard clipboard, String name) {
|
|
||||||
parent.hideClipboard(user);
|
@Override
|
||||||
boolean result = new BlueprintClipboardManager(getPlugin(), parent.getBlueprintsFolder(), clipboard).save(user, name);
|
public boolean execute(User user, String label, List<String> args)
|
||||||
if (result && clipboard.getBlueprint() != null) {
|
{
|
||||||
getPlugin().getBlueprintsManager().addBlueprint(getAddon(), clipboard.getBlueprint());
|
AdminBlueprintCommand parent = (AdminBlueprintCommand) this.getParent();
|
||||||
|
BlueprintClipboard clipboard = parent.getClipboards().
|
||||||
|
computeIfAbsent(user.getUniqueId(), v -> new BlueprintClipboard());
|
||||||
|
|
||||||
|
String fileName = Util.sanitizeInput(args.get(0));
|
||||||
|
|
||||||
|
// Check if file exists
|
||||||
|
File newFile = new File(parent.getBlueprintsFolder(), fileName + BlueprintsManager.BLUEPRINT_SUFFIX);
|
||||||
|
|
||||||
|
if (newFile.exists())
|
||||||
|
{
|
||||||
|
this.askConfirmation(user,
|
||||||
|
user.getTranslation("commands.admin.blueprint.file-exists"),
|
||||||
|
() -> this.hideAndSave(user, parent, clipboard, fileName, args.get(0)));
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return this.hideAndSave(user, parent, clipboard, fileName, args.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method saves given blueprint.
|
||||||
|
* @param user User that triggers blueprint save.
|
||||||
|
* @param parent Parent command that contains clipboard.
|
||||||
|
* @param clipboard Active clipboard.
|
||||||
|
* @param name Filename for the blueprint
|
||||||
|
* @param displayName Display name for the blueprint.
|
||||||
|
* @return {@code true} if blueprint is saved, {@code false} otherwise.
|
||||||
|
*/
|
||||||
|
private boolean hideAndSave(User user,
|
||||||
|
AdminBlueprintCommand parent,
|
||||||
|
BlueprintClipboard clipboard,
|
||||||
|
String name,
|
||||||
|
String displayName)
|
||||||
|
{
|
||||||
|
parent.hideClipboard(user);
|
||||||
|
boolean result = new BlueprintClipboardManager(this.getPlugin(),
|
||||||
|
parent.getBlueprintsFolder(), clipboard).
|
||||||
|
save(user, name, displayName);
|
||||||
|
|
||||||
|
if (result && clipboard.isFull())
|
||||||
|
{
|
||||||
|
this.getPlugin().getBlueprintsManager().addBlueprint(this.getAddon(), clipboard.getBlueprint());
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,10 +7,9 @@ import java.util.Map;
|
|||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Color;
|
import org.bukkit.Color;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.Material;
|
||||||
import org.bukkit.Particle;
|
import org.bukkit.Particle;
|
||||||
|
|
||||||
import com.google.common.base.Enums;
|
|
||||||
|
|
||||||
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||||
import world.bentobox.bentobox.api.user.User;
|
import world.bentobox.bentobox.api.user.User;
|
||||||
import world.bentobox.bentobox.database.objects.Island;
|
import world.bentobox.bentobox.database.objects.Island;
|
||||||
@ -24,8 +23,6 @@ public class AdminRangeDisplayCommand extends CompositeCommand {
|
|||||||
private static final String DISPLAY = "display";
|
private static final String DISPLAY = "display";
|
||||||
private static final String SHOW = "show";
|
private static final String SHOW = "show";
|
||||||
private static final String HIDE = "hide";
|
private static final String HIDE = "hide";
|
||||||
// Since 1.18, the Particle.BARRIER was replaced by BLOCK_MARKER
|
|
||||||
private static final Particle BARRIER = Enums.getIfPresent(Particle.class, "BARRIER").or(Enums.getIfPresent(Particle.class, "BLOCK_MARKER").or(Particle.LAVA));
|
|
||||||
|
|
||||||
// Map of users to which ranges must be displayed
|
// Map of users to which ranges must be displayed
|
||||||
private final Map<User, Integer> displayRanges = new HashMap<>();
|
private final Map<User, Integer> displayRanges = new HashMap<>();
|
||||||
@ -75,7 +72,7 @@ public class AdminRangeDisplayCommand extends CompositeCommand {
|
|||||||
|
|
||||||
getIslands().getIslandAt(user.getLocation()).ifPresent(island -> {
|
getIslands().getIslandAt(user.getLocation()).ifPresent(island -> {
|
||||||
// Draw the island protected area
|
// Draw the island protected area
|
||||||
drawZone(user, BARRIER, null, island, island.getProtectionRange());
|
drawZone(user, Particle.BLOCK_MARKER, Material.BARRIER.createBlockData(), island, island.getProtectionRange());
|
||||||
|
|
||||||
// Draw the default protected area if island protected zone is different
|
// Draw the default protected area if island protected zone is different
|
||||||
if (island.getProtectionRange() != getPlugin().getIWM().getIslandProtectionRange(getWorld())) {
|
if (island.getProtectionRange() != getPlugin().getIWM().getIslandProtectionRange(getWorld())) {
|
||||||
@ -94,7 +91,7 @@ public class AdminRangeDisplayCommand extends CompositeCommand {
|
|||||||
displayRanges.remove(user);
|
displayRanges.remove(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawZone(User user, Particle particle, Particle.DustOptions dustOptions, Island island, int range) {
|
private void drawZone(User user, Particle particle, Object dustOptions, Island island, int range) {
|
||||||
Location center = island.getProtectionCenter();
|
Location center = island.getProtectionCenter();
|
||||||
// Get player Y coordinate
|
// Get player Y coordinate
|
||||||
int playerY = user.getPlayer().getLocation().getBlockY() + 1;
|
int playerY = user.getPlayer().getLocation().getBlockY() + 1;
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
//
|
||||||
|
// Created by BONNe
|
||||||
|
// Copyright - 2022
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parent command for all Admin Team commands.
|
||||||
|
*/
|
||||||
|
public class AdminTeamCommand extends CompositeCommand
|
||||||
|
{
|
||||||
|
public AdminTeamCommand(CompositeCommand parent)
|
||||||
|
{
|
||||||
|
super(parent, "team");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setup()
|
||||||
|
{
|
||||||
|
this.setPermission("mod.team");
|
||||||
|
this.setDescription("commands.admin.team.description");
|
||||||
|
|
||||||
|
new AdminTeamAddCommand(this);
|
||||||
|
new AdminTeamDisbandCommand(this);
|
||||||
|
new AdminTeamFixCommand(this);
|
||||||
|
new AdminTeamKickCommand(this);
|
||||||
|
new AdminTeamSetownerCommand(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean execute(User user, String label, List<String> args)
|
||||||
|
{
|
||||||
|
this.showHelp(this, user);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -12,6 +12,8 @@ import world.bentobox.bentobox.database.objects.Island;
|
|||||||
import world.bentobox.bentobox.managers.BlueprintsManager;
|
import world.bentobox.bentobox.managers.BlueprintsManager;
|
||||||
import world.bentobox.bentobox.managers.island.NewIsland;
|
import world.bentobox.bentobox.managers.island.NewIsland;
|
||||||
import world.bentobox.bentobox.panels.IslandCreationPanel;
|
import world.bentobox.bentobox.panels.IslandCreationPanel;
|
||||||
|
import world.bentobox.bentobox.util.Util;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* /island create - Create an island.
|
* /island create - Create an island.
|
||||||
@ -63,13 +65,13 @@ public class IslandCreateCommand extends CompositeCommand {
|
|||||||
public boolean execute(User user, String label, List<String> args) {
|
public boolean execute(User user, String label, List<String> args) {
|
||||||
// Permission check if the name is not the default one
|
// Permission check if the name is not the default one
|
||||||
if (!args.isEmpty()) {
|
if (!args.isEmpty()) {
|
||||||
String name = getPlugin().getBlueprintsManager().validate(getAddon(), args.get(0).toLowerCase(java.util.Locale.ENGLISH));
|
String name = getPlugin().getBlueprintsManager().validate(getAddon(), Util.sanitizeInput(args.get(0)));
|
||||||
if (name == null) {
|
if (name == null) {
|
||||||
// The blueprint name is not valid.
|
// The blueprint name is not valid.
|
||||||
user.sendMessage("commands.island.create.unknown-blueprint");
|
user.sendMessage("commands.island.create.unknown-blueprint");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!getPlugin().getBlueprintsManager().checkPerm(getAddon(), user, args.get(0))) {
|
if (!getPlugin().getBlueprintsManager().checkPerm(getAddon(), user, Util.sanitizeInput(args.get(0)))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Make island
|
// Make island
|
||||||
|
@ -17,6 +17,8 @@ import world.bentobox.bentobox.managers.BlueprintsManager;
|
|||||||
import world.bentobox.bentobox.managers.island.NewIsland;
|
import world.bentobox.bentobox.managers.island.NewIsland;
|
||||||
import world.bentobox.bentobox.managers.island.NewIsland.Builder;
|
import world.bentobox.bentobox.managers.island.NewIsland.Builder;
|
||||||
import world.bentobox.bentobox.panels.IslandCreationPanel;
|
import world.bentobox.bentobox.panels.IslandCreationPanel;
|
||||||
|
import world.bentobox.bentobox.util.Util;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author tastybento
|
* @author tastybento
|
||||||
@ -78,13 +80,13 @@ public class IslandResetCommand extends ConfirmableCommand {
|
|||||||
public boolean execute(User user, String label, List<String> args) {
|
public boolean execute(User user, String label, List<String> args) {
|
||||||
// Permission check if the name is not the default one
|
// Permission check if the name is not the default one
|
||||||
if (!args.isEmpty()) {
|
if (!args.isEmpty()) {
|
||||||
String name = getPlugin().getBlueprintsManager().validate(getAddon(), args.get(0).toLowerCase(java.util.Locale.ENGLISH));
|
String name = getPlugin().getBlueprintsManager().validate(getAddon(), Util.sanitizeInput(args.get(0)));
|
||||||
if (name == null || name.isEmpty()) {
|
if (name == null || name.isEmpty()) {
|
||||||
// The blueprint name is not valid.
|
// The blueprint name is not valid.
|
||||||
user.sendMessage("commands.island.create.unknown-blueprint");
|
user.sendMessage("commands.island.create.unknown-blueprint");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!getPlugin().getBlueprintsManager().checkPerm(getAddon(), user, args.get(0))) {
|
if (!getPlugin().getBlueprintsManager().checkPerm(getAddon(), user, Util.sanitizeInput(args.get(0)))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return resetIsland(user, name);
|
return resetIsland(user, name);
|
||||||
|
@ -109,6 +109,8 @@ public class IslandTeamInviteCommand extends CompositeCommand {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean execute(User user, String label, List<String> args) {
|
public boolean execute(User user, String label, List<String> args) {
|
||||||
|
// Rare case when invited player is null. Could be a race condition.
|
||||||
|
if (invitedPlayer == null) return false;
|
||||||
// If that player already has an invite out then retract it.
|
// If that player already has an invite out then retract it.
|
||||||
// Players can only have one invite one at a time - interesting
|
// Players can only have one invite one at a time - interesting
|
||||||
if (itc.isInvited(invitedPlayer.getUniqueId())) {
|
if (itc.isInvited(invitedPlayer.getUniqueId())) {
|
||||||
|
@ -2,6 +2,7 @@ package world.bentobox.bentobox.api.configuration;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -31,11 +32,54 @@ public interface WorldSettings extends ConfigObject {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @return default rank settings for new islands
|
* @return default rank settings for new islands
|
||||||
|
* @deprecated since 1.21
|
||||||
|
* Map of Flag, Integer does not allow to load other plugin/addon flags.
|
||||||
|
* It cannot be replaced with Map of String, Integer due to compatibility issues.
|
||||||
|
* @see WorldSettings#getDefaultIslandFlagNames()
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
Map<Flag, Integer> getDefaultIslandFlags();
|
Map<Flag, Integer> getDefaultIslandFlags();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return map of flags ID's linked to default rank for new island.
|
||||||
|
* This is necessary so users could specify any flag names in settings file from other plugins and addons.
|
||||||
|
* Otherwise, Flag reader would mark flag as invalid and remove it.
|
||||||
|
* Default implementation is compatibility layer so GameModes that are not upgraded still works.
|
||||||
|
* @since 1.21
|
||||||
|
* @return default rank settings for new islands.
|
||||||
|
*/
|
||||||
|
default Map<String, Integer> getDefaultIslandFlagNames()
|
||||||
|
{
|
||||||
|
Map<String, Integer> flags = new HashMap<>();
|
||||||
|
this.getDefaultIslandFlags().forEach((key, value) -> flags.put(key.getID(), value));
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return default settings for new
|
||||||
|
* @deprecated since 1.21
|
||||||
|
* Map of Flag, Integer does not allow to load other plugin/addon flags.
|
||||||
|
* It cannot be replaced with Map of String, Integer due to compatibility issues.
|
||||||
|
* @see WorldSettings#getDefaultIslandSettingNames()
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
Map<Flag, Integer> getDefaultIslandSettings();
|
Map<Flag, Integer> getDefaultIslandSettings();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return map of flags ID's linked to default settings for new island.
|
||||||
|
* This is necessary so users could specify any flag names in settings file from other plugins and addons.
|
||||||
|
* Otherwise, Flag reader would mark flag as invalid and remove it.
|
||||||
|
* Default implementation is compatibility layer so GameModes that are not upgraded still works.
|
||||||
|
* @since 1.21
|
||||||
|
* @return default settings for new islands.
|
||||||
|
*/
|
||||||
|
default Map<String, Integer> getDefaultIslandSettingNames()
|
||||||
|
{
|
||||||
|
Map<String, Integer> flags = new HashMap<>();
|
||||||
|
this.getDefaultIslandSettings().forEach((key, value) -> flags.put(key.getID(), value));
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the world difficulty
|
* Get the world difficulty
|
||||||
* @return difficulty
|
* @return difficulty
|
||||||
|
@ -256,6 +256,14 @@ public abstract class FlagListener implements Listener {
|
|||||||
return plugin.getIslands();
|
return plugin.getIslands();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the island database manager
|
||||||
|
* @return the island database manager
|
||||||
|
*/
|
||||||
|
protected IslandsManager getIslandsManager() {
|
||||||
|
return plugin.getIslands();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the island world manager
|
* Get the island world manager
|
||||||
* @return Island World Manager
|
* @return Island World Manager
|
||||||
|
@ -17,10 +17,13 @@ import org.bukkit.GameMode;
|
|||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.OfflinePlayer;
|
import org.bukkit.OfflinePlayer;
|
||||||
import org.bukkit.Particle;
|
import org.bukkit.Particle;
|
||||||
|
import org.bukkit.Vibration;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.block.data.BlockData;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
|
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.bukkit.inventory.PlayerInventory;
|
import org.bukkit.inventory.PlayerInventory;
|
||||||
import org.bukkit.permissions.PermissionAttachment;
|
import org.bukkit.permissions.PermissionAttachment;
|
||||||
import org.bukkit.permissions.PermissionAttachmentInfo;
|
import org.bukkit.permissions.PermissionAttachmentInfo;
|
||||||
@ -597,22 +600,107 @@ public class User implements MetaDataAble {
|
|||||||
* @param y Y coordinate of the particle to display.
|
* @param y Y coordinate of the particle to display.
|
||||||
* @param z Z coordinate of the particle to display.
|
* @param z Z coordinate of the particle to display.
|
||||||
*/
|
*/
|
||||||
public void spawnParticle(Particle particle, Particle.DustOptions dustOptions, double x, double y, double z) {
|
public void spawnParticle(Particle particle, Object dustOptions, double x, double y, double z)
|
||||||
if (particle.equals(Particle.REDSTONE) && dustOptions == null) {
|
{
|
||||||
// Security check that will avoid later unexpected exceptions.
|
// Improve particle validation.
|
||||||
throw new IllegalArgumentException("A non-null Particle.DustOptions must be provided when using Particle.REDSTONE as particle.");
|
switch (particle)
|
||||||
|
{
|
||||||
|
case REDSTONE ->
|
||||||
|
{
|
||||||
|
if (!(dustOptions instanceof Particle.DustOptions))
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("A non-null Particle.DustOptions must be provided when using Particle.REDSTONE as particle.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case ITEM_CRACK ->
|
||||||
|
{
|
||||||
|
if (!(dustOptions instanceof ItemStack))
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("A non-null ItemStack must be provided when using Particle.ITEM_CRACK as particle.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case BLOCK_CRACK, BLOCK_DUST, FALLING_DUST, BLOCK_MARKER ->
|
||||||
|
{
|
||||||
|
if (!(dustOptions instanceof BlockData))
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("A non-null BlockData must be provided when using Particle." + particle + " as particle.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case DUST_COLOR_TRANSITION ->
|
||||||
|
{
|
||||||
|
if (!(dustOptions instanceof Particle.DustTransition))
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("A non-null Particle.DustTransition must be provided when using Particle.DUST_COLOR_TRANSITION as particle.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case VIBRATION ->
|
||||||
|
{
|
||||||
|
if (!(dustOptions instanceof Vibration))
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("A non-null Vibration must be provided when using Particle.VIBRATION as particle.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case SCULK_CHARGE ->
|
||||||
|
{
|
||||||
|
if (!(dustOptions instanceof Float))
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("A non-null Float must be provided when using Particle.SCULK_CHARGE as particle.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case SHRIEK ->
|
||||||
|
{
|
||||||
|
if (!(dustOptions instanceof Integer))
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("A non-null Integer must be provided when using Particle.SHRIEK as particle.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case LEGACY_BLOCK_CRACK, LEGACY_BLOCK_DUST, LEGACY_FALLING_DUST ->
|
||||||
|
{
|
||||||
|
if (!(dustOptions instanceof BlockData))
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("A non-null MaterialData must be provided when using Particle." + particle + " as particle.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this particle is beyond the viewing distance of the server
|
// Check if this particle is beyond the viewing distance of the server
|
||||||
if (player.getLocation().toVector().distanceSquared(new Vector(x,y,z)) < (Bukkit.getServer().getViewDistance()*256*Bukkit.getServer().getViewDistance())) {
|
if (this.player != null &&
|
||||||
if (particle.equals(Particle.REDSTONE)) {
|
this.player.getLocation().toVector().distanceSquared(new Vector(x, y, z)) <
|
||||||
|
(Bukkit.getServer().getViewDistance() * 256 * Bukkit.getServer().getViewDistance()))
|
||||||
|
{
|
||||||
|
if (particle.equals(Particle.REDSTONE))
|
||||||
|
{
|
||||||
player.spawnParticle(particle, x, y, z, 1, 0, 0, 0, 1, dustOptions);
|
player.spawnParticle(particle, x, y, z, 1, 0, 0, 0, 1, dustOptions);
|
||||||
} else {
|
}
|
||||||
|
else if (dustOptions != null)
|
||||||
|
{
|
||||||
|
player.spawnParticle(particle, x, y, z, 1, dustOptions);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
player.spawnParticle(particle, x, y, z, 1);
|
player.spawnParticle(particle, x, y, z, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spawn particles to the player.
|
||||||
|
* They are only displayed if they are within the server's view distance.
|
||||||
|
* Compatibility method for older usages.
|
||||||
|
* @param particle Particle to display.
|
||||||
|
* @param dustOptions Particle.DustOptions for the particle to display.
|
||||||
|
* Cannot be null when particle is {@link Particle#REDSTONE}.
|
||||||
|
* @param x X coordinate of the particle to display.
|
||||||
|
* @param y Y coordinate of the particle to display.
|
||||||
|
* @param z Z coordinate of the particle to display.
|
||||||
|
*/
|
||||||
|
public void spawnParticle(Particle particle, Particle.DustOptions dustOptions, double x, double y, double z)
|
||||||
|
{
|
||||||
|
this.spawnParticle(particle, (Object) dustOptions, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Spawn particles to the player.
|
* Spawn particles to the player.
|
||||||
* They are only displayed if they are within the server's view distance.
|
* They are only displayed if they are within the server's view distance.
|
||||||
@ -623,10 +711,12 @@ public class User implements MetaDataAble {
|
|||||||
* @param y Y coordinate of the particle to display.
|
* @param y Y coordinate of the particle to display.
|
||||||
* @param z Z coordinate of the particle to display.
|
* @param z Z coordinate of the particle to display.
|
||||||
*/
|
*/
|
||||||
public void spawnParticle(Particle particle, Particle.DustOptions dustOptions, int x, int y, int z) {
|
public void spawnParticle(Particle particle, Particle.DustOptions dustOptions, int x, int y, int z)
|
||||||
spawnParticle(particle, dustOptions, (double) x, (double) y, (double) z);
|
{
|
||||||
|
this.spawnParticle(particle, dustOptions, (double) x, (double) y, (double) z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see java.lang.Object#hashCode()
|
* @see java.lang.Object#hashCode()
|
||||||
*/
|
*/
|
||||||
|
@ -53,14 +53,14 @@ public class Blueprint {
|
|||||||
public String getName() {
|
public String getName() {
|
||||||
if (name == null) name = "unnamed";
|
if (name == null) name = "unnamed";
|
||||||
// Force lower case
|
// Force lower case
|
||||||
return name.toLowerCase(Locale.ENGLISH);
|
return name;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* @param name the name to set
|
* @param name the name to set
|
||||||
*/
|
*/
|
||||||
public Blueprint setName(@NonNull String name) {
|
public Blueprint setName(@NonNull String name) {
|
||||||
// Force lowercase
|
// Force lowercase
|
||||||
this.name = name.toLowerCase(Locale.ENGLISH);
|
this.name = name;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
@ -85,7 +85,7 @@ public class BlueprintClipboard {
|
|||||||
* @param user - user
|
* @param user - user
|
||||||
* @return true if successful, false if pos1 or pos2 are undefined.
|
* @return true if successful, false if pos1 or pos2 are undefined.
|
||||||
*/
|
*/
|
||||||
public boolean copy(User user, boolean copyAir) {
|
public boolean copy(User user, boolean copyAir, boolean copyBiome) {
|
||||||
if (copying) {
|
if (copying) {
|
||||||
user.sendMessage("commands.admin.blueprint.mid-copy");
|
user.sendMessage("commands.admin.blueprint.mid-copy");
|
||||||
return false;
|
return false;
|
||||||
@ -120,11 +120,11 @@ public class BlueprintClipboard {
|
|||||||
|
|
||||||
int speed = plugin.getSettings().getPasteSpeed();
|
int speed = plugin.getSettings().getPasteSpeed();
|
||||||
List<Vector> vectorsToCopy = getVectors(toCopy);
|
List<Vector> vectorsToCopy = getVectors(toCopy);
|
||||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> copyAsync(world, user, vectorsToCopy, speed, copyAir));
|
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> copyAsync(world, user, vectorsToCopy, speed, copyAir, copyBiome));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void copyAsync(World world, User user, List<Vector> vectorsToCopy, int speed, boolean copyAir) {
|
private void copyAsync(World world, User user, List<Vector> vectorsToCopy, int speed, boolean copyAir, boolean copyBiome) {
|
||||||
copying = false;
|
copying = false;
|
||||||
copyTask = Bukkit.getScheduler().runTaskTimer(plugin, () -> {
|
copyTask = Bukkit.getScheduler().runTaskTimer(plugin, () -> {
|
||||||
if (copying) {
|
if (copying) {
|
||||||
@ -139,7 +139,7 @@ public class BlueprintClipboard {
|
|||||||
Math.rint(e.getLocation().getY()),
|
Math.rint(e.getLocation().getY()),
|
||||||
Math.rint(e.getLocation().getZ())).equals(v))
|
Math.rint(e.getLocation().getZ())).equals(v))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
if (copyBlock(v.toLocation(world), copyAir, ents)) {
|
if (copyBlock(v.toLocation(world), copyAir, copyBiome, ents)) {
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -179,7 +179,7 @@ public class BlueprintClipboard {
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean copyBlock(Location l, boolean copyAir, Collection<LivingEntity> entities) {
|
private boolean copyBlock(Location l, boolean copyAir, boolean copyBiome, Collection<LivingEntity> entities) {
|
||||||
Block block = l.getBlock();
|
Block block = l.getBlock();
|
||||||
if (!copyAir && block.getType().equals(Material.AIR) && entities.isEmpty()) {
|
if (!copyAir && block.getType().equals(Material.AIR) && entities.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
@ -203,20 +203,23 @@ public class BlueprintClipboard {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BlueprintBlock b = bluePrintBlock(pos, block, copyBiome);
|
||||||
BlueprintBlock b = bluePrintBlock(pos, block);
|
|
||||||
if (b != null) {
|
if (b != null) {
|
||||||
this.bpBlocks.put(pos, b);
|
this.bpBlocks.put(pos, b);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private BlueprintBlock bluePrintBlock(Vector pos, Block block) {
|
private BlueprintBlock bluePrintBlock(Vector pos, Block block, boolean copyBiome) {
|
||||||
// Block state
|
// Block state
|
||||||
BlockState blockState = block.getState();
|
BlockState blockState = block.getState();
|
||||||
BlueprintBlock b = new BlueprintBlock(block.getBlockData().getAsString());
|
BlueprintBlock b = new BlueprintBlock(block.getBlockData().getAsString());
|
||||||
// Biome
|
|
||||||
b.setBiome(block.getBiome());
|
if (copyBiome) {
|
||||||
|
// Biome
|
||||||
|
b.setBiome(block.getBiome());
|
||||||
|
}
|
||||||
|
|
||||||
// Signs
|
// Signs
|
||||||
if (blockState instanceof Sign sign) {
|
if (blockState instanceof Sign sign) {
|
||||||
b.setSignLines(Arrays.asList(sign.getLines()));
|
b.setSignLines(Arrays.asList(sign.getLines()));
|
||||||
|
@ -1,49 +1,27 @@
|
|||||||
package world.bentobox.bentobox.blueprints;
|
package world.bentobox.bentobox.blueprints;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.math.RoundingMode;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Material;
|
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.block.Banner;
|
|
||||||
import org.bukkit.block.Block;
|
|
||||||
import org.bukkit.block.BlockFace;
|
|
||||||
import org.bukkit.block.BlockState;
|
|
||||||
import org.bukkit.block.CreatureSpawner;
|
|
||||||
import org.bukkit.block.data.BlockData;
|
|
||||||
import org.bukkit.block.data.type.Sign;
|
|
||||||
import org.bukkit.block.data.type.WallSign;
|
|
||||||
import org.bukkit.entity.LivingEntity;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.bukkit.inventory.Inventory;
|
|
||||||
import org.bukkit.inventory.InventoryHolder;
|
|
||||||
import org.bukkit.scheduler.BukkitTask;
|
import org.bukkit.scheduler.BukkitTask;
|
||||||
import org.bukkit.util.Vector;
|
import org.bukkit.util.Vector;
|
||||||
import org.eclipse.jdt.annotation.NonNull;
|
import org.eclipse.jdt.annotation.NonNull;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
|
|
||||||
import world.bentobox.bentobox.BentoBox;
|
import world.bentobox.bentobox.BentoBox;
|
||||||
import world.bentobox.bentobox.api.localization.TextVariables;
|
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||||
import world.bentobox.bentobox.api.user.User;
|
import world.bentobox.bentobox.api.user.User;
|
||||||
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBlock;
|
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.blueprints.dataobjects.BlueprintEntity;
|
||||||
import world.bentobox.bentobox.database.objects.Island;
|
import world.bentobox.bentobox.database.objects.Island;
|
||||||
|
import world.bentobox.bentobox.nms.PasteHandler;
|
||||||
import world.bentobox.bentobox.util.Util;
|
import world.bentobox.bentobox.util.Util;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class pastes the clipboard it is given
|
* This class pastes the clipboard it is given
|
||||||
* @author tastybento
|
* @author tastybento
|
||||||
@ -68,11 +46,9 @@ public class BlueprintPaster {
|
|||||||
*/
|
*/
|
||||||
private static long chunkLoadTime = 0;
|
private static long chunkLoadTime = 0;
|
||||||
|
|
||||||
private static final String MINECRAFT = "minecraft:";
|
|
||||||
|
|
||||||
private static final Map<String, String> BLOCK_CONVERSION = ImmutableMap.of("sign", "oak_sign", "wall_sign", "oak_wall_sign");
|
|
||||||
|
|
||||||
private final BentoBox plugin;
|
private final BentoBox plugin;
|
||||||
|
private final PasteHandler paster = Util.getPasteHandler();
|
||||||
|
private final World world;
|
||||||
// The minimum block position (x,y,z)
|
// The minimum block position (x,y,z)
|
||||||
private Location pos1;
|
private Location pos1;
|
||||||
// The maximum block position (x,y,z)
|
// The maximum block position (x,y,z)
|
||||||
@ -80,6 +56,7 @@ public class BlueprintPaster {
|
|||||||
private PasteState pasteState;
|
private PasteState pasteState;
|
||||||
private BukkitTask pastingTask;
|
private BukkitTask pastingTask;
|
||||||
private BlueprintClipboard clipboard;
|
private BlueprintClipboard clipboard;
|
||||||
|
private CompletableFuture<Void> currentTask = CompletableFuture.completedFuture(null);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Blueprint to paste.
|
* The Blueprint to paste.
|
||||||
@ -111,6 +88,7 @@ public class BlueprintPaster {
|
|||||||
// Calculate location for pasting
|
// Calculate location for pasting
|
||||||
this.blueprint = Objects.requireNonNull(clipboard.getBlueprint(), "Clipboard cannot have a null Blueprint");
|
this.blueprint = Objects.requireNonNull(clipboard.getBlueprint(), "Clipboard cannot have a null Blueprint");
|
||||||
this.location = location;
|
this.location = location;
|
||||||
|
this.world = location.getWorld();
|
||||||
this.island = null;
|
this.island = null;
|
||||||
|
|
||||||
// Paste
|
// Paste
|
||||||
@ -128,6 +106,7 @@ public class BlueprintPaster {
|
|||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.blueprint = bp;
|
this.blueprint = bp;
|
||||||
this.island = island;
|
this.island = island;
|
||||||
|
this.world = world;
|
||||||
// Offset due to bedrock
|
// Offset due to bedrock
|
||||||
Vector off = bp.getBedrock() != null ? bp.getBedrock() : new Vector(0,0,0);
|
Vector off = bp.getBedrock() != null ? bp.getBedrock() : new Vector(0,0,0);
|
||||||
// Calculate location for pasting
|
// Calculate location for pasting
|
||||||
@ -155,10 +134,9 @@ public class BlueprintPaster {
|
|||||||
pasteState = PasteState.CHUNK_LOAD;
|
pasteState = PasteState.CHUNK_LOAD;
|
||||||
|
|
||||||
// If this is an island OVERWORLD paste, get the island owner.
|
// If this is an island OVERWORLD paste, get the island owner.
|
||||||
final Optional<User> owner = Optional.ofNullable(island)
|
final Optional<User> owner = Optional.ofNullable(island).map(i -> User.getInstance(i.getOwner()));
|
||||||
.filter(i -> location.getWorld().getEnvironment().equals(World.Environment.NORMAL))
|
|
||||||
.map(i -> User.getInstance(i.getOwner()));
|
// Tell the owner we're pasting blocks and how much time it might take
|
||||||
// Tell the owner we're pasting blocks and how much time it might take
|
|
||||||
owner.ifPresent(user -> tellOwner(user, blocks.size(), attached.size(), entities.size(), plugin.getSettings().getPasteSpeed()));
|
owner.ifPresent(user -> tellOwner(user, blocks.size(), attached.size(), entities.size(), plugin.getSettings().getPasteSpeed()));
|
||||||
Bits bits = new Bits(blocks, attached, entities,
|
Bits bits = new Bits(blocks, attached, entities,
|
||||||
blocks.entrySet().iterator(), attached.entrySet().iterator(), entities.entrySet().iterator(),
|
blocks.entrySet().iterator(), attached.entrySet().iterator(), entities.entrySet().iterator(),
|
||||||
@ -169,6 +147,8 @@ public class BlueprintPaster {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void pasterTask(CompletableFuture<Boolean> result, Optional<User> owner, Bits bits) {
|
private void pasterTask(CompletableFuture<Boolean> result, Optional<User> owner, Bits bits) {
|
||||||
|
if (!currentTask.isDone()) return;
|
||||||
|
|
||||||
final int pasteSpeed = plugin.getSettings().getPasteSpeed();
|
final int pasteSpeed = plugin.getSettings().getPasteSpeed();
|
||||||
|
|
||||||
long timer = System.currentTimeMillis();
|
long timer = System.currentTimeMillis();
|
||||||
@ -176,7 +156,7 @@ public class BlueprintPaster {
|
|||||||
if (pasteState.equals(PasteState.CHUNK_LOAD)) {
|
if (pasteState.equals(PasteState.CHUNK_LOAD)) {
|
||||||
pasteState = PasteState.CHUNK_LOADING;
|
pasteState = PasteState.CHUNK_LOADING;
|
||||||
// Load chunk
|
// Load chunk
|
||||||
Util.getChunkAtAsync(location).thenRun(() -> {
|
currentTask = Util.getChunkAtAsync(location).thenRun(() -> {
|
||||||
pasteState = PasteState.BLOCKS;
|
pasteState = PasteState.BLOCKS;
|
||||||
long duration = System.currentTimeMillis() - timer;
|
long duration = System.currentTimeMillis() - timer;
|
||||||
if (duration > chunkLoadTime) {
|
if (duration > chunkLoadTime) {
|
||||||
@ -184,34 +164,72 @@ public class BlueprintPaster {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
while (pasteState.equals(PasteState.BLOCKS) && count < pasteSpeed && bits.it.hasNext()) {
|
else if (pasteState.equals(PasteState.BLOCKS) || pasteState.equals(PasteState.ATTACHMENTS)) {
|
||||||
pasteBlock(location, bits.it.next());
|
Iterator<Entry<Vector, BlueprintBlock>> it = pasteState.equals(PasteState.BLOCKS) ? bits.it : bits.it2;
|
||||||
count++;
|
if (it.hasNext()) {
|
||||||
}
|
Map<Location, BlueprintBlock> blockMap = new HashMap<>();
|
||||||
while (pasteState.equals(PasteState.ATTACHMENTS) && count < pasteSpeed && bits.it2.hasNext()) {
|
// Paste blocks
|
||||||
pasteBlock(location, bits.it2.next());
|
while (count < pasteSpeed) {
|
||||||
count++;
|
if (!it.hasNext()) {
|
||||||
}
|
break;
|
||||||
while (pasteState.equals(PasteState.ENTITIES) && count < pasteSpeed && bits.it3.hasNext()) {
|
}
|
||||||
pasteEntity(location, bits.it3.next());
|
Entry<Vector, BlueprintBlock> entry = it.next();
|
||||||
count++;
|
Location pasteTo = location.clone().add(entry.getKey());
|
||||||
}
|
// pos1 and pos2 update
|
||||||
// STATE SHIFT
|
updatePos(pasteTo);
|
||||||
if (pasteState.equals(PasteState.BLOCKS) && !bits.it.hasNext()) {
|
|
||||||
// Blocks done
|
BlueprintBlock block = entry.getValue();
|
||||||
// Next paste attachments
|
blockMap.put(pasteTo, block);
|
||||||
pasteState = PasteState.ATTACHMENTS;
|
count++;
|
||||||
}
|
}
|
||||||
else if (pasteState.equals(PasteState.ATTACHMENTS) && !bits.it2.hasNext()) {
|
if (!blockMap.isEmpty()) {
|
||||||
// Attachments done. Next paste entities
|
currentTask = paster.pasteBlocks(island, world, blockMap);
|
||||||
pasteState = PasteState.ENTITIES;
|
}
|
||||||
if (bits.entities.size() != 0) {
|
} else {
|
||||||
owner.ifPresent(user -> user.sendMessage("commands.island.create.pasting.entities", TextVariables.NUMBER, String.valueOf(bits.entities.size())));
|
if (pasteState.equals(PasteState.BLOCKS)) {
|
||||||
|
// Blocks done
|
||||||
|
// Next paste attachments
|
||||||
|
pasteState = PasteState.ATTACHMENTS;
|
||||||
|
} else {
|
||||||
|
// Attachments done. Next paste entities
|
||||||
|
pasteState = PasteState.ENTITIES;
|
||||||
|
if (bits.entities.size() != 0) {
|
||||||
|
owner.ifPresent(user -> user.sendMessage("commands.island.create.pasting.entities", TextVariables.NUMBER, String.valueOf(bits.entities.size())));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (pasteState.equals(PasteState.ENTITIES) && !bits.it3.hasNext()) {
|
else if (pasteState.equals(PasteState.ENTITIES)) {
|
||||||
pasteState = PasteState.DONE;
|
if (bits.it3().hasNext()) {
|
||||||
owner.ifPresent(user -> user.sendMessage("commands.island.create.pasting.done"));
|
Map<Location, List<BlueprintEntity>> entityMap = new HashMap<>();
|
||||||
|
// Paste entities
|
||||||
|
while (count < pasteSpeed) {
|
||||||
|
if (!bits.it3().hasNext()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Entry<Vector, List<BlueprintEntity>> entry = bits.it3().next();
|
||||||
|
int x = location.getBlockX() + entry.getKey().getBlockX();
|
||||||
|
int y = location.getBlockY() + entry.getKey().getBlockY();
|
||||||
|
int z = location.getBlockZ() + entry.getKey().getBlockZ();
|
||||||
|
Location center = new Location(world, x, y, z).add(new Vector(0.5, 0.5, 0.5));
|
||||||
|
List<BlueprintEntity> entities = entry.getValue();
|
||||||
|
entityMap.put(center, entities);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
if (!entityMap.isEmpty()) {
|
||||||
|
currentTask = paster.pasteEntities(island, world, entityMap);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pasteState = PasteState.DONE;
|
||||||
|
|
||||||
|
String world = switch (location.getWorld().getEnvironment()) {
|
||||||
|
case NETHER -> owner.map(user -> user.getTranslation("general.worlds.nether")).orElse("");
|
||||||
|
case THE_END -> owner.map(user -> user.getTranslation("general.worlds.the-end")).orElse("");
|
||||||
|
default -> owner.map(user -> user.getTranslation("general.worlds.overworld")).orElse("");
|
||||||
|
};
|
||||||
|
|
||||||
|
owner.ifPresent(user -> user.sendMessage("commands.island.create.pasting.dimension-done", "[world]", world));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (pasteState.equals(PasteState.DONE)) {
|
else if (pasteState.equals(PasteState.DONE)) {
|
||||||
// All done. Cancel task
|
// All done. Cancel task
|
||||||
@ -238,135 +256,6 @@ public class BlueprintPaster {
|
|||||||
user.sendMessage("commands.island.create.pasting.blocks", TextVariables.NUMBER, String.valueOf(blocksSize + attachedSize));
|
user.sendMessage("commands.island.create.pasting.blocks", TextVariables.NUMBER, String.valueOf(blocksSize + attachedSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pasteBlock(Location location, Entry<Vector, BlueprintBlock> entry) {
|
|
||||||
World world = location.getWorld();
|
|
||||||
Location pasteTo = location.clone().add(entry.getKey());
|
|
||||||
BlueprintBlock bpBlock = entry.getValue();
|
|
||||||
Util.getChunkAtAsync(pasteTo).thenRun(() -> {
|
|
||||||
Block block = pasteTo.getBlock();
|
|
||||||
// Set the block data - default is AIR
|
|
||||||
BlockData bd;
|
|
||||||
try {
|
|
||||||
bd = Bukkit.createBlockData(bpBlock.getBlockData());
|
|
||||||
} catch (Exception e) {
|
|
||||||
bd = convertBlockData(world, bpBlock);
|
|
||||||
}
|
|
||||||
block.setBlockData(bd, false);
|
|
||||||
setBlockState(block, bpBlock);
|
|
||||||
// Set biome
|
|
||||||
if (bpBlock.getBiome() != null) {
|
|
||||||
block.setBiome(bpBlock.getBiome());
|
|
||||||
}
|
|
||||||
// pos1 and pos2 update
|
|
||||||
updatePos(block.getLocation());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tries to convert the BlockData to a newer version, and logs a warning if it fails to do so.
|
|
||||||
* @return the converted BlockData or a default AIR BlockData.
|
|
||||||
* @since 1.6.0
|
|
||||||
*/
|
|
||||||
private BlockData convertBlockData(World world, BlueprintBlock block) {
|
|
||||||
BlockData blockData = Bukkit.createBlockData(Material.AIR);
|
|
||||||
try {
|
|
||||||
for (Entry<String, String> en : BLOCK_CONVERSION.entrySet()) {
|
|
||||||
if (block.getBlockData().startsWith(MINECRAFT + en.getKey())) {
|
|
||||||
blockData = Bukkit.createBlockData(block.getBlockData().replace(MINECRAFT + en.getKey(), MINECRAFT + en.getValue()));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
// This may happen if the block type is no longer supported by the server
|
|
||||||
plugin.logWarning("Blueprint references materials not supported on this server version.");
|
|
||||||
plugin.logWarning("Load blueprint manually, check and save to fix for this server version.");
|
|
||||||
plugin.logWarning("World: " + world.getName() + "; Failed block data: " + block.getBlockData());
|
|
||||||
}
|
|
||||||
return blockData;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void pasteEntity(Location location, Entry<Vector, List<BlueprintEntity>> entry) {
|
|
||||||
int x = location.getBlockX() + entry.getKey().getBlockX();
|
|
||||||
int y = location.getBlockY() + entry.getKey().getBlockY();
|
|
||||||
int z = location.getBlockZ() + entry.getKey().getBlockZ();
|
|
||||||
setEntity(new Location(location.getWorld(), x, y, z), entry.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles signs, chests and mob spawner blocks
|
|
||||||
* @param block - block
|
|
||||||
* @param bpBlock - config
|
|
||||||
*/
|
|
||||||
private void setBlockState(Block block, BlueprintBlock bpBlock) {
|
|
||||||
// Get the block state
|
|
||||||
BlockState bs = block.getState();
|
|
||||||
// Signs
|
|
||||||
if (bs instanceof org.bukkit.block.Sign sign) {
|
|
||||||
writeSign(block, bpBlock.getSignLines(), bpBlock.isGlowingText());
|
|
||||||
}
|
|
||||||
// Chests, in general
|
|
||||||
if (bs instanceof InventoryHolder holder) {
|
|
||||||
Inventory ih = holder.getInventory();
|
|
||||||
// Double chests are pasted as two blocks so inventory is filled twice.
|
|
||||||
// This code stops over-filling for the first block.
|
|
||||||
bpBlock.getInventory().forEach(ih::setItem);
|
|
||||||
}
|
|
||||||
// Mob spawners
|
|
||||||
if (bs instanceof CreatureSpawner spawner) {
|
|
||||||
setSpawner(spawner, bpBlock.getCreatureSpawner());
|
|
||||||
}
|
|
||||||
// Banners
|
|
||||||
if (bs instanceof Banner banner && bpBlock.getBannerPatterns() != null) {
|
|
||||||
bpBlock.getBannerPatterns().removeIf(Objects::isNull);
|
|
||||||
banner.setPatterns(bpBlock.getBannerPatterns());
|
|
||||||
banner.update(true, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setSpawner(CreatureSpawner spawner, BlueprintCreatureSpawner s) {
|
|
||||||
spawner.setSpawnedType(s.getSpawnedType());
|
|
||||||
spawner.setMaxNearbyEntities(s.getMaxNearbyEntities());
|
|
||||||
spawner.setMaxSpawnDelay(s.getMaxSpawnDelay());
|
|
||||||
spawner.setMinSpawnDelay(s.getMinSpawnDelay());
|
|
||||||
spawner.setDelay(s.getDelay());
|
|
||||||
spawner.setRequiredPlayerRange(s.getRequiredPlayerRange());
|
|
||||||
spawner.setSpawnRange(s.getSpawnRange());
|
|
||||||
spawner.update(true, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets any entity that is in this location
|
|
||||||
* @param location - location
|
|
||||||
* @param list - list of entities to paste
|
|
||||||
*/
|
|
||||||
private void setEntity(Location location, List<BlueprintEntity> list) {
|
|
||||||
list.stream().filter(k -> k.getType() != null).forEach(k -> {
|
|
||||||
// Center, and just a bit high
|
|
||||||
Location center = location.add(new Vector(0.5, 0.5, 0.5));
|
|
||||||
Util.getChunkAtAsync(center).thenRun(() -> {
|
|
||||||
LivingEntity e = (LivingEntity)location.getWorld().spawnEntity(center, k.getType());
|
|
||||||
if (k.getCustomName() != null) {
|
|
||||||
String customName = k.getCustomName();
|
|
||||||
|
|
||||||
if (island != null) {
|
|
||||||
// Parse any placeholders in the entity's name, if the owner's connected (he should)
|
|
||||||
Player owner = User.getInstance(island.getOwner()).getPlayer();
|
|
||||||
if (owner != null) {
|
|
||||||
// Parse for the player's name first (in case placeholders might need it)
|
|
||||||
customName = customName.replace(TextVariables.NAME, owner.getName());
|
|
||||||
// Now parse the placeholders
|
|
||||||
customName = plugin.getPlaceholdersManager().replacePlaceholders(owner, customName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Actually set the custom name
|
|
||||||
e.setCustomName(customName);
|
|
||||||
}
|
|
||||||
k.configureEntity(e);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tracks the minimum and maximum block positions
|
* Tracks the minimum and maximum block positions
|
||||||
* @param l - location of block pasted
|
* @param l - location of block pasted
|
||||||
@ -397,50 +286,4 @@ public class BlueprintPaster {
|
|||||||
pos2.setZ(l.getBlockZ());
|
pos2.setZ(l.getBlockZ());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeSign(final Block block, final List<String> lines, boolean glow) {
|
|
||||||
BlockFace bf;
|
|
||||||
if (block.getType().name().contains("WALL_SIGN")) {
|
|
||||||
WallSign wallSign = (WallSign)block.getBlockData();
|
|
||||||
bf = wallSign.getFacing();
|
|
||||||
} else {
|
|
||||||
Sign sign = (Sign)block.getBlockData();
|
|
||||||
bf = sign.getRotation();
|
|
||||||
}
|
|
||||||
// Handle spawn sign
|
|
||||||
if (island != null && !lines.isEmpty() && lines.get(0).equalsIgnoreCase(TextVariables.SPAWN_HERE)) {
|
|
||||||
block.setType(Material.AIR);
|
|
||||||
// Orient to face same direction as sign
|
|
||||||
Location spawnPoint = new Location(block.getWorld(), block.getX() + 0.5D, block.getY(),
|
|
||||||
block.getZ() + 0.5D, Util.blockFaceToFloat(bf.getOppositeFace()), 30F);
|
|
||||||
island.setSpawnPoint(block.getWorld().getEnvironment(), spawnPoint);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Get the name of the player
|
|
||||||
String name = "";
|
|
||||||
if (island != null) {
|
|
||||||
name = plugin.getPlayers().getName(island.getOwner());
|
|
||||||
}
|
|
||||||
// Handle locale text for starting sign
|
|
||||||
org.bukkit.block.Sign s = (org.bukkit.block.Sign)block.getState();
|
|
||||||
// Sign text must be stored under the addon's name.sign.line0,1,2,3 in the yaml file
|
|
||||||
if (island != null && !lines.isEmpty() && lines.get(0).equalsIgnoreCase(TextVariables.START_TEXT)) {
|
|
||||||
// Get the addon that is operating in this world
|
|
||||||
String addonName = plugin.getIWM().getAddon(island.getWorld()).map(addon -> addon.getDescription().getName().toLowerCase(Locale.ENGLISH)).orElse("");
|
|
||||||
if (island.getOwner() != null) {
|
|
||||||
for (int i = 0; i < 4; i++) {
|
|
||||||
s.setLine(i, Util.translateColorCodes(plugin.getLocalesManager().getOrDefault(User.getInstance(island.getOwner()),
|
|
||||||
addonName + ".sign.line" + i,"").replace(TextVariables.NAME, name)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Just paste
|
|
||||||
for (int i = 0; i < 4; i++) {
|
|
||||||
s.setLine(i, lines.get(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s.setGlowingText(glow);
|
|
||||||
// Update the sign
|
|
||||||
s.update();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package world.bentobox.bentobox.blueprints.conversation;
|
package world.bentobox.bentobox.blueprints.conversation;
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
import org.bukkit.conversations.ConversationContext;
|
import org.bukkit.conversations.ConversationContext;
|
||||||
import org.bukkit.conversations.Prompt;
|
import org.bukkit.conversations.Prompt;
|
||||||
import org.bukkit.conversations.StringPrompt;
|
import org.bukkit.conversations.StringPrompt;
|
||||||
@ -18,60 +16,74 @@ import world.bentobox.bentobox.managers.BlueprintsManager;
|
|||||||
import world.bentobox.bentobox.util.Util;
|
import world.bentobox.bentobox.util.Util;
|
||||||
|
|
||||||
|
|
||||||
public class NamePrompt extends StringPrompt {
|
public class NamePrompt extends StringPrompt
|
||||||
|
{
|
||||||
private final GameModeAddon addon;
|
private final GameModeAddon addon;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private final BlueprintBundle bb;
|
private final BlueprintBundle bb;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private Blueprint bp;
|
private Blueprint bp;
|
||||||
|
|
||||||
public NamePrompt(@NonNull GameModeAddon addon, @Nullable BlueprintBundle bb) {
|
|
||||||
|
public NamePrompt(@NonNull GameModeAddon addon, @Nullable BlueprintBundle bb)
|
||||||
|
{
|
||||||
this.addon = addon;
|
this.addon = addon;
|
||||||
this.bb = bb;
|
this.bb = bb;
|
||||||
}
|
}
|
||||||
|
|
||||||
public NamePrompt(@NonNull GameModeAddon addon, @Nullable Blueprint bp, @Nullable BlueprintBundle bb) {
|
|
||||||
|
public NamePrompt(@NonNull GameModeAddon addon, @Nullable Blueprint bp, @Nullable BlueprintBundle bb)
|
||||||
|
{
|
||||||
this.addon = addon;
|
this.addon = addon;
|
||||||
this.bp = bp;
|
this.bp = bp;
|
||||||
this.bb = bb;
|
this.bb = bb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull String getPromptText(ConversationContext context) {
|
public @NonNull String getPromptText(ConversationContext context)
|
||||||
User user = User.getInstance((Player)context.getForWhom());
|
{
|
||||||
|
User user = User.getInstance((Player) context.getForWhom());
|
||||||
return user.getTranslation("commands.admin.blueprint.management.name.prompt");
|
return user.getTranslation("commands.admin.blueprint.management.name.prompt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Prompt acceptInput(ConversationContext context, String input) {
|
public Prompt acceptInput(ConversationContext context, String input)
|
||||||
User user = User.getInstance((Player)context.getForWhom());
|
{
|
||||||
|
User user = User.getInstance((Player) context.getForWhom());
|
||||||
|
String uniqueId = Util.sanitizeInput(input);
|
||||||
|
|
||||||
// Convert color codes
|
// Convert color codes
|
||||||
input = Util.translateColorCodes(input);
|
if (ChatColor.stripColor(Util.translateColorCodes(input)).length() > 32)
|
||||||
if (ChatColor.stripColor(input).length() > 32) {
|
{
|
||||||
context.getForWhom().sendRawMessage("Too long");
|
context.getForWhom().sendRawMessage(
|
||||||
|
user.getTranslation("commands.admin.blueprint.management.name.too-long"));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
if (bb == null || !bb.getUniqueId().equals(BlueprintsManager.DEFAULT_BUNDLE_NAME)) {
|
|
||||||
// Make a uniqueid
|
if (this.bb == null || !this.bb.getUniqueId().equals(BlueprintsManager.DEFAULT_BUNDLE_NAME))
|
||||||
StringBuilder uniqueId = new StringBuilder(ChatColor.stripColor(input).toLowerCase(Locale.ENGLISH).replace(" ", "_"));
|
{
|
||||||
// Check if this name is unique
|
// Check if this name is unique
|
||||||
int max = 0;
|
if (this.addon.getPlugin().getBlueprintsManager().getBlueprintBundles(this.addon).containsKey(uniqueId))
|
||||||
while (max++ < 32 && addon.getPlugin().getBlueprintsManager().getBlueprintBundles(addon).containsKey(uniqueId.toString())) {
|
{
|
||||||
uniqueId.append("x");
|
context.getForWhom().sendRawMessage(
|
||||||
}
|
user.getTranslation("commands.admin.blueprint.management.name.pick-a-unique-name"));
|
||||||
if (max == 32) {
|
|
||||||
context.getForWhom().sendRawMessage(user.getTranslation("commands.admin.blueprint.management.name.pick-a-unique-name"));
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
context.setSessionData("uniqueId", uniqueId.toString());
|
|
||||||
} else {
|
context.setSessionData("uniqueId", uniqueId);
|
||||||
// Default stays as default
|
|
||||||
context.setSessionData("uniqueId", bb.getUniqueId());
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Default stays as default
|
||||||
|
context.setSessionData("uniqueId", this.bb.getUniqueId());
|
||||||
|
}
|
||||||
|
|
||||||
context.setSessionData("name", input);
|
context.setSessionData("name", input);
|
||||||
return new NameSuccessPrompt(addon, bb, bp);
|
|
||||||
|
return new NameSuccessPrompt(this.addon, this.bb, this.bp);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,58 +15,76 @@ import world.bentobox.bentobox.blueprints.Blueprint;
|
|||||||
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBundle;
|
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBundle;
|
||||||
import world.bentobox.bentobox.panels.BlueprintManagementPanel;
|
import world.bentobox.bentobox.panels.BlueprintManagementPanel;
|
||||||
|
|
||||||
public class NameSuccessPrompt extends MessagePrompt {
|
public class NameSuccessPrompt extends MessagePrompt
|
||||||
|
{
|
||||||
|
|
||||||
private final GameModeAddon addon;
|
private final GameModeAddon addon;
|
||||||
|
|
||||||
private BlueprintBundle bb;
|
private BlueprintBundle bb;
|
||||||
|
|
||||||
private final Blueprint bp;
|
private final Blueprint bp;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the name processing
|
* Handles the name processing
|
||||||
|
*
|
||||||
* @param addon - Game Mode addon
|
* @param addon - Game Mode addon
|
||||||
* @param bb - Blueprint Bundle
|
* @param bb - Blueprint Bundle
|
||||||
* @param bp - blueprint
|
* @param bp - blueprint
|
||||||
*/
|
*/
|
||||||
public NameSuccessPrompt(@NonNull GameModeAddon addon, @Nullable BlueprintBundle bb, @Nullable Blueprint bp) {
|
public NameSuccessPrompt(@NonNull GameModeAddon addon, @Nullable BlueprintBundle bb, @Nullable Blueprint bp)
|
||||||
|
{
|
||||||
this.addon = addon;
|
this.addon = addon;
|
||||||
this.bb = bb;
|
this.bb = bb;
|
||||||
this.bp = bp;
|
this.bp = bp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull String getPromptText(ConversationContext context) {
|
public @NonNull String getPromptText(ConversationContext context)
|
||||||
|
{
|
||||||
String name = (String) context.getSessionData("name");
|
String name = (String) context.getSessionData("name");
|
||||||
String uniqueId = (String) context.getSessionData("uniqueId");
|
String uniqueId = (String) context.getSessionData("uniqueId");
|
||||||
User user = User.getInstance((Player)context.getForWhom());
|
|
||||||
// Rename blueprint
|
|
||||||
if (bp != null) {
|
|
||||||
BentoBox.getInstance().getBlueprintsManager().renameBlueprint(addon, bp, name);
|
|
||||||
new BlueprintManagementPanel(BentoBox.getInstance(), user, addon).openBB(bb);
|
|
||||||
} else {
|
|
||||||
// Blueprint Bundle
|
|
||||||
if (bb == null) {
|
|
||||||
// New Blueprint bundle
|
|
||||||
bb = new BlueprintBundle();
|
|
||||||
bb.setIcon(Material.RED_WOOL);
|
|
||||||
} else {
|
|
||||||
// Rename - remove old named file
|
|
||||||
BentoBox.getInstance().getBlueprintsManager().deleteBlueprintBundle(addon, bb);
|
|
||||||
}
|
|
||||||
bb.setDisplayName(name);
|
|
||||||
bb.setUniqueId(uniqueId);
|
|
||||||
BentoBox.getInstance().getBlueprintsManager().addBlueprintBundle(addon, bb);
|
|
||||||
BentoBox.getInstance().getBlueprintsManager().saveBlueprintBundle(addon, bb);
|
|
||||||
|
|
||||||
new BlueprintManagementPanel(BentoBox.getInstance(), user, addon).openPanel();
|
User user = User.getInstance((Player) context.getForWhom());
|
||||||
// Set the name
|
|
||||||
// if successfully
|
// Rename blueprint
|
||||||
|
if (this.bp != null)
|
||||||
|
{
|
||||||
|
this.addon.getPlugin().getBlueprintsManager().renameBlueprint(this.addon, this.bp, uniqueId, name);
|
||||||
|
new BlueprintManagementPanel(this.addon.getPlugin(), user, this.addon).openBB(this.bb);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Blueprint Bundle
|
||||||
|
if (this.bb == null)
|
||||||
|
{
|
||||||
|
// New Blueprint bundle
|
||||||
|
this.bb = new BlueprintBundle();
|
||||||
|
this.bb.setIcon(Material.RED_WOOL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Rename - remove old named file
|
||||||
|
this.addon.getPlugin().getBlueprintsManager().deleteBlueprintBundle(this.addon, this.bb);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.bb.setDisplayName(name);
|
||||||
|
this.bb.setUniqueId(uniqueId);
|
||||||
|
this.addon.getPlugin().getBlueprintsManager().addBlueprintBundle(this.addon, this.bb);
|
||||||
|
this.addon.getPlugin().getBlueprintsManager().saveBlueprintBundle(this.addon, this.bb);
|
||||||
|
|
||||||
|
new BlueprintManagementPanel(this.addon.getPlugin(), user, this.addon).openPanel();
|
||||||
|
// Set the name
|
||||||
|
}
|
||||||
|
|
||||||
return user.getTranslation("commands.admin.blueprint.management.description.success");
|
return user.getTranslation("commands.admin.blueprint.management.description.success");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Prompt getNextPrompt(@NonNull ConversationContext context) {
|
protected Prompt getNextPrompt(@NonNull ConversationContext context)
|
||||||
|
{
|
||||||
return Prompt.END_OF_CONVERSATION;
|
return Prompt.END_OF_CONVERSATION;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -69,7 +69,7 @@ public class BlueprintBundle implements DataObject {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String getUniqueId() {
|
public String getUniqueId() {
|
||||||
return uniqueId.toLowerCase(Locale.ENGLISH);
|
return uniqueId;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* @param uniqueId the uniqueId to set
|
* @param uniqueId the uniqueId to set
|
||||||
|
@ -56,7 +56,7 @@ public class BentoboxTypeAdapterFactory implements TypeAdapterFactory {
|
|||||||
if (Location.class.isAssignableFrom(rawType)) {
|
if (Location.class.isAssignableFrom(rawType)) {
|
||||||
// Use our current location adapter for backward compatibility
|
// Use our current location adapter for backward compatibility
|
||||||
return (TypeAdapter<T>) new LocationTypeAdapter();
|
return (TypeAdapter<T>) new LocationTypeAdapter();
|
||||||
} else if (Biome.class.isAssignableFrom(rawType) && !ServerCompatibility.getInstance().isVersion(ServerCompatibility.ServerVersion.V1_17_1)) { // TODO: Any better way ?
|
} else if (Biome.class.isAssignableFrom(rawType)) {
|
||||||
return (TypeAdapter<T>) new BiomeTypeAdapter();
|
return (TypeAdapter<T>) new BiomeTypeAdapter();
|
||||||
} else if (Enum.class.isAssignableFrom(rawType)) {
|
} else if (Enum.class.isAssignableFrom(rawType)) {
|
||||||
return new EnumTypeAdapter(rawType);
|
return new EnumTypeAdapter(rawType);
|
||||||
|
@ -18,6 +18,7 @@ import java.util.stream.Collectors;
|
|||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.GameMode;
|
||||||
import org.bukkit.World.Environment;
|
import org.bukkit.World.Environment;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.util.BoundingBox;
|
import org.bukkit.util.BoundingBox;
|
||||||
@ -38,8 +39,6 @@ import world.bentobox.bentobox.api.metadata.MetaDataAble;
|
|||||||
import world.bentobox.bentobox.api.metadata.MetaDataValue;
|
import world.bentobox.bentobox.api.metadata.MetaDataValue;
|
||||||
import world.bentobox.bentobox.api.user.User;
|
import world.bentobox.bentobox.api.user.User;
|
||||||
import world.bentobox.bentobox.database.objects.adapters.Adapter;
|
import world.bentobox.bentobox.database.objects.adapters.Adapter;
|
||||||
import world.bentobox.bentobox.database.objects.adapters.FlagSerializer;
|
|
||||||
import world.bentobox.bentobox.database.objects.adapters.FlagSerializer3;
|
|
||||||
import world.bentobox.bentobox.database.objects.adapters.LogEntryListAdapter;
|
import world.bentobox.bentobox.database.objects.adapters.LogEntryListAdapter;
|
||||||
import world.bentobox.bentobox.lists.Flags;
|
import world.bentobox.bentobox.lists.Flags;
|
||||||
import world.bentobox.bentobox.managers.RanksManager;
|
import world.bentobox.bentobox.managers.RanksManager;
|
||||||
@ -158,9 +157,8 @@ public class Island implements DataObject, MetaDataAble {
|
|||||||
private boolean purgeProtected = false;
|
private boolean purgeProtected = false;
|
||||||
|
|
||||||
//// Protection flags ////
|
//// Protection flags ////
|
||||||
@Adapter(FlagSerializer.class)
|
|
||||||
@Expose
|
@Expose
|
||||||
private Map<Flag, Integer> flags = new HashMap<>();
|
private Map<String, Integer> flags = new HashMap<>();
|
||||||
|
|
||||||
//// Island History ////
|
//// Island History ////
|
||||||
@Adapter(LogEntryListAdapter.class)
|
@Adapter(LogEntryListAdapter.class)
|
||||||
@ -179,9 +177,8 @@ public class Island implements DataObject, MetaDataAble {
|
|||||||
/**
|
/**
|
||||||
* Used to store flag cooldowns for this island
|
* Used to store flag cooldowns for this island
|
||||||
*/
|
*/
|
||||||
@Adapter(FlagSerializer3.class)
|
|
||||||
@Expose
|
@Expose
|
||||||
private Map<Flag, Long> cooldowns = new HashMap<>();
|
private Map<String, Long> cooldowns = new HashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Commands and the rank required to use them for this island
|
* Commands and the rank required to use them for this island
|
||||||
@ -367,13 +364,13 @@ public class Island implements DataObject, MetaDataAble {
|
|||||||
* @return flag value
|
* @return flag value
|
||||||
*/
|
*/
|
||||||
public int getFlag(@NonNull Flag flag) {
|
public int getFlag(@NonNull Flag flag) {
|
||||||
return flags.computeIfAbsent(flag, k -> flag.getDefaultRank());
|
return flags.computeIfAbsent(flag.getID(), k -> flag.getDefaultRank());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the flags
|
* @return the flags
|
||||||
*/
|
*/
|
||||||
public Map<Flag, Integer> getFlags() {
|
public Map<String, Integer> getFlags() {
|
||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -643,6 +640,55 @@ public class Island implements DataObject, MetaDataAble {
|
|||||||
return world;
|
return world;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the nether world
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public World getNetherWorld()
|
||||||
|
{
|
||||||
|
return this.getWorld(Environment.NETHER);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the end world
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public World getEndWorld()
|
||||||
|
{
|
||||||
|
return this.getWorld(Environment.THE_END);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns this island world in given environment. This method can return {@code null} if dimension is
|
||||||
|
* disabled.
|
||||||
|
* @param environment The environment of the island world.
|
||||||
|
* @return the world in given environment.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public World getWorld(Environment environment)
|
||||||
|
{
|
||||||
|
if (Environment.NORMAL.equals(environment))
|
||||||
|
{
|
||||||
|
return this.world;
|
||||||
|
}
|
||||||
|
else if (Environment.THE_END.equals(environment) && this.isEndIslandEnabled())
|
||||||
|
{
|
||||||
|
return this.getPlugin().getIWM().getEndWorld(this.world);
|
||||||
|
}
|
||||||
|
else if (Environment.NETHER.equals(environment) && this.isNetherIslandEnabled())
|
||||||
|
{
|
||||||
|
return this.getPlugin().getIWM().getNetherWorld(this.world);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the x coordinate of the island center
|
* @return the x coordinate of the island center
|
||||||
*/
|
*/
|
||||||
@ -679,8 +725,13 @@ public class Island implements DataObject, MetaDataAble {
|
|||||||
* @param location - location
|
* @param location - location
|
||||||
* @return true if in island space
|
* @return true if in island space
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("ConstantConditions")
|
||||||
public boolean inIslandSpace(Location location) {
|
public boolean inIslandSpace(Location location) {
|
||||||
return Util.sameWorld(world, location.getWorld()) && inIslandSpace(location.getBlockX(), location.getBlockZ());
|
return Util.sameWorld(this.world, location.getWorld()) &&
|
||||||
|
(location.getWorld().getEnvironment().equals(Environment.NORMAL) ||
|
||||||
|
this.getPlugin().getIWM().isIslandNether(location.getWorld()) ||
|
||||||
|
this.getPlugin().getIWM().isIslandEnd(location.getWorld())) &&
|
||||||
|
this.inIslandSpace(location.getBlockX(), location.getBlockZ());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -693,12 +744,80 @@ public class Island implements DataObject, MetaDataAble {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a {@link BoundingBox} of the full island space.
|
* Returns a {@link BoundingBox} of the full island space for overworld.
|
||||||
* @return a {@link BoundingBox} of the full island space.
|
* @return a {@link BoundingBox} of the full island space.
|
||||||
* @since 1.5.2
|
* @since 1.5.2
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("ConstantConditions")
|
||||||
|
@NotNull
|
||||||
public BoundingBox getBoundingBox() {
|
public BoundingBox getBoundingBox() {
|
||||||
return new BoundingBox(getMinX(), world.getMinHeight(), getMinZ(), getMaxX(), world.getMaxHeight(), getMaxZ());
|
return this.getBoundingBox(Environment.NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@link BoundingBox} of this island's space area in requested dimension.
|
||||||
|
* @param environment the requested dimension.
|
||||||
|
* @return a {@link BoundingBox} of this island's space area or {@code null} if island is not created in requested dimension.
|
||||||
|
* @since 1.21.0
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public BoundingBox getBoundingBox(Environment environment)
|
||||||
|
{
|
||||||
|
BoundingBox boundingBox;
|
||||||
|
|
||||||
|
if (Environment.NORMAL.equals(environment))
|
||||||
|
{
|
||||||
|
// Return normal world bounding box.
|
||||||
|
boundingBox = new BoundingBox(this.getMinX(),
|
||||||
|
this.world.getMinHeight(),
|
||||||
|
this.getMinZ(),
|
||||||
|
this.getMaxX(),
|
||||||
|
this.world.getMaxHeight(),
|
||||||
|
this.getMaxZ());
|
||||||
|
}
|
||||||
|
else if (Environment.THE_END.equals(environment) && this.isEndIslandEnabled())
|
||||||
|
{
|
||||||
|
// If end world is generated, return end island bounding box.
|
||||||
|
//noinspection ConstantConditions
|
||||||
|
boundingBox = new BoundingBox(this.getMinX(),
|
||||||
|
this.getEndWorld().getMinHeight(),
|
||||||
|
this.getMinZ(),
|
||||||
|
this.getMaxX(),
|
||||||
|
this.getEndWorld().getMaxHeight(),
|
||||||
|
this.getMaxZ());
|
||||||
|
}
|
||||||
|
else if (Environment.NETHER.equals(environment) && this.isNetherIslandEnabled())
|
||||||
|
{
|
||||||
|
// If nether world is generated, return nether island bounding box.
|
||||||
|
//noinspection ConstantConditions
|
||||||
|
boundingBox = new BoundingBox(this.getMinX(),
|
||||||
|
this.getNetherWorld().getMinHeight(),
|
||||||
|
this.getMinZ(),
|
||||||
|
this.getMaxX(),
|
||||||
|
this.getNetherWorld().getMaxHeight(),
|
||||||
|
this.getMaxZ());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
boundingBox = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return boundingBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Using this method in the filtering for getVisitors and hasVisitors
|
||||||
|
* @param player The player that must be checked.
|
||||||
|
* @return true if player is a visitor
|
||||||
|
*/
|
||||||
|
private boolean playerIsVisitor(Player player) {
|
||||||
|
if (player.getGameMode() == GameMode.SPECTATOR) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return onIsland(player.getLocation()) && getRank(User.getInstance(player)) == RanksManager.VISITOR_RANK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -708,9 +827,7 @@ public class Island implements DataObject, MetaDataAble {
|
|||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public List<Player> getVisitors() {
|
public List<Player> getVisitors() {
|
||||||
return Bukkit.getOnlinePlayers().stream()
|
return Bukkit.getOnlinePlayers().stream().filter(this::playerIsVisitor).collect(Collectors.toList());
|
||||||
.filter(player -> onIsland(player.getLocation()) && getRank(User.getInstance(player)) == RanksManager.VISITOR_RANK)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -722,7 +839,7 @@ public class Island implements DataObject, MetaDataAble {
|
|||||||
* @see #getVisitors()
|
* @see #getVisitors()
|
||||||
*/
|
*/
|
||||||
public boolean hasVisitors() {
|
public boolean hasVisitors() {
|
||||||
return Bukkit.getOnlinePlayers().stream().anyMatch(player -> onIsland(player.getLocation()) && getRank(User.getInstance(player)) == RanksManager.VISITOR_RANK);
|
return Bukkit.getOnlinePlayers().stream().anyMatch(this::playerIsVisitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -793,19 +910,83 @@ public class Island implements DataObject, MetaDataAble {
|
|||||||
* @param target location to check, not null
|
* @param target location to check, not null
|
||||||
* @return {@code true} if this location is within this island's protected area, {@code false} otherwise.
|
* @return {@code true} if this location is within this island's protected area, {@code false} otherwise.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("ConstantConditions")
|
||||||
public boolean onIsland(@NonNull Location target) {
|
public boolean onIsland(@NonNull Location target) {
|
||||||
return Util.sameWorld(world, target.getWorld()) && target.getBlockX() >= getMinProtectedX() && target.getBlockX() < (getMinProtectedX() + protectionRange * 2) && target.getBlockZ() >= getMinProtectedZ() && target.getBlockZ() < (getMinProtectedZ() + protectionRange * 2);
|
return Util.sameWorld(this.world, target.getWorld()) &&
|
||||||
|
(target.getWorld().getEnvironment().equals(Environment.NORMAL) ||
|
||||||
|
this.getPlugin().getIWM().isIslandNether(target.getWorld()) ||
|
||||||
|
this.getPlugin().getIWM().isIslandEnd(target.getWorld())) &&
|
||||||
|
target.getBlockX() >= this.getMinProtectedX() &&
|
||||||
|
target.getBlockX() < (this.getMinProtectedX() + this.protectionRange * 2) &&
|
||||||
|
target.getBlockZ() >= this.getMinProtectedZ() &&
|
||||||
|
target.getBlockZ() < (this.getMinProtectedZ() + this.protectionRange * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a {@link BoundingBox} of this island's protected area.
|
* Returns a {@link BoundingBox} of this island's protected area for overworld.
|
||||||
* @return a {@link BoundingBox} of this island's protected area.
|
* @return a {@link BoundingBox} of this island's protected area.
|
||||||
* @since 1.5.2
|
* @since 1.5.2
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("ConstantConditions")
|
||||||
|
@NotNull
|
||||||
public BoundingBox getProtectionBoundingBox() {
|
public BoundingBox getProtectionBoundingBox() {
|
||||||
return new BoundingBox(getMinProtectedX(), 0.0D, getMinProtectedZ(), getMaxProtectedX()-1.0D, world.getMaxHeight(), getMaxProtectedZ()-1.0D);
|
return this.getProtectionBoundingBox(Environment.NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@link BoundingBox} of this island's protected area.
|
||||||
|
* @param environment an environment of bounding box area.
|
||||||
|
* @return a {@link BoundingBox} of this island's protected area or {@code null} if island is not created in required dimension.
|
||||||
|
* in required dimension.
|
||||||
|
* @since 1.21.0
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public BoundingBox getProtectionBoundingBox(Environment environment)
|
||||||
|
{
|
||||||
|
BoundingBox boundingBox;
|
||||||
|
|
||||||
|
if (Environment.NORMAL.equals(environment))
|
||||||
|
{
|
||||||
|
// Return normal world bounding box.
|
||||||
|
boundingBox = new BoundingBox(this.getMinProtectedX(),
|
||||||
|
this.world.getMinHeight(),
|
||||||
|
this.getMinProtectedZ(),
|
||||||
|
this.getMaxProtectedX(),
|
||||||
|
this.world.getMaxHeight(),
|
||||||
|
this.getMaxProtectedZ());
|
||||||
|
}
|
||||||
|
else if (Environment.THE_END.equals(environment) && this.isEndIslandEnabled())
|
||||||
|
{
|
||||||
|
// If end world is generated, return end island bounding box.
|
||||||
|
//noinspection ConstantConditions
|
||||||
|
boundingBox = new BoundingBox(this.getMinProtectedX(),
|
||||||
|
this.getEndWorld().getMinHeight(),
|
||||||
|
this.getMinProtectedZ(),
|
||||||
|
this.getMaxProtectedX(),
|
||||||
|
this.getEndWorld().getMaxHeight(),
|
||||||
|
this.getMaxProtectedZ());
|
||||||
|
}
|
||||||
|
else if (Environment.NETHER.equals(environment) && this.isNetherIslandEnabled())
|
||||||
|
{
|
||||||
|
// If nether world is generated, return nether island bounding box.
|
||||||
|
//noinspection ConstantConditions
|
||||||
|
boundingBox = new BoundingBox(this.getMinProtectedX(),
|
||||||
|
this.getNetherWorld().getMinHeight(),
|
||||||
|
this.getMinProtectedZ(),
|
||||||
|
this.getMaxProtectedX(),
|
||||||
|
this.getNetherWorld().getMaxHeight(),
|
||||||
|
this.getMaxProtectedZ());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
boundingBox = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return boundingBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes a player from the team member map. Generally, you should
|
* Removes a player from the team member map. Generally, you should
|
||||||
* use {@link world.bentobox.bentobox.managers.IslandsManager#removePlayer(World, UUID)}
|
* use {@link world.bentobox.bentobox.managers.IslandsManager#removePlayer(World, UUID)}
|
||||||
@ -851,7 +1032,7 @@ public class Island implements DataObject, MetaDataAble {
|
|||||||
* @param doSubflags - whether to set subflags
|
* @param doSubflags - whether to set subflags
|
||||||
*/
|
*/
|
||||||
public void setFlag(Flag flag, int value, boolean doSubflags) {
|
public void setFlag(Flag flag, int value, boolean doSubflags) {
|
||||||
flags.put(flag, value);
|
flags.put(flag.getID(), value);
|
||||||
// Subflag support
|
// Subflag support
|
||||||
if (doSubflags && flag.hasSubflags()) {
|
if (doSubflags && flag.hasSubflags()) {
|
||||||
// Ensure that a subflag isn't a subflag of itself or else we're in trouble!
|
// Ensure that a subflag isn't a subflag of itself or else we're in trouble!
|
||||||
@ -863,7 +1044,7 @@ public class Island implements DataObject, MetaDataAble {
|
|||||||
/**
|
/**
|
||||||
* @param flags the flags to set
|
* @param flags the flags to set
|
||||||
*/
|
*/
|
||||||
public void setFlags(Map<Flag, Integer> flags) {
|
public void setFlags(Map<String, Integer> flags) {
|
||||||
this.flags = flags;
|
this.flags = flags;
|
||||||
setChanged();
|
setChanged();
|
||||||
}
|
}
|
||||||
@ -874,11 +1055,13 @@ public class Island implements DataObject, MetaDataAble {
|
|||||||
*/
|
*/
|
||||||
public void setFlagsDefaults() {
|
public void setFlagsDefaults() {
|
||||||
BentoBox plugin = BentoBox.getInstance();
|
BentoBox plugin = BentoBox.getInstance();
|
||||||
Map<Flag, Integer> result = new HashMap<>();
|
Map<String, Integer> result = new HashMap<>();
|
||||||
plugin.getFlagsManager().getFlags().stream().filter(f -> f.getType().equals(Flag.Type.PROTECTION))
|
plugin.getFlagsManager().getFlags().stream().
|
||||||
.forEach(f -> result.put(f, plugin.getIWM().getDefaultIslandFlags(world).getOrDefault(f, f.getDefaultRank())));
|
filter(f -> f.getType().equals(Flag.Type.PROTECTION)).
|
||||||
plugin.getFlagsManager().getFlags().stream().filter(f -> f.getType().equals(Flag.Type.SETTING))
|
forEach(f -> result.put(f.getID(), plugin.getIWM().getDefaultIslandFlags(world).getOrDefault(f, f.getDefaultRank())));
|
||||||
.forEach(f -> result.put(f, plugin.getIWM().getDefaultIslandSettings(world).getOrDefault(f, f.getDefaultRank())));
|
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);
|
this.setFlags(result);
|
||||||
setChanged();
|
setChanged();
|
||||||
}
|
}
|
||||||
@ -1120,7 +1303,7 @@ public class Island implements DataObject, MetaDataAble {
|
|||||||
public void setSettingsFlag(Flag flag, boolean state, boolean doSubflags) {
|
public void setSettingsFlag(Flag flag, boolean state, boolean doSubflags) {
|
||||||
int newState = state ? 1 : -1;
|
int newState = state ? 1 : -1;
|
||||||
if (flag.getType().equals(Flag.Type.SETTING) || flag.getType().equals(Flag.Type.WORLD_SETTING)) {
|
if (flag.getType().equals(Flag.Type.SETTING) || flag.getType().equals(Flag.Type.WORLD_SETTING)) {
|
||||||
flags.put(flag, newState);
|
flags.put(flag.getID(), newState);
|
||||||
if (doSubflags && flag.hasSubflags()) {
|
if (doSubflags && flag.hasSubflags()) {
|
||||||
// If we have circular subflags or a flag is a subflag of itself we are in trouble!
|
// If we have circular subflags or a flag is a subflag of itself we are in trouble!
|
||||||
flag.getSubflags().forEach(subflag -> setSettingsFlag(subflag, state, true));
|
flag.getSubflags().forEach(subflag -> setSettingsFlag(subflag, state, true));
|
||||||
@ -1243,6 +1426,15 @@ public class Island implements DataObject, MetaDataAble {
|
|||||||
return nether != null && !getCenter().toVector().toLocation(nether).getBlock().getType().isAir();
|
return nether != null && !getCenter().toVector().toLocation(nether).getBlock().getType().isAir();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether this island has its nether island mode enabled or not.
|
||||||
|
* @return {@code true} if this island has its nether island enabled, {@code false} otherwise.
|
||||||
|
* @since 1.21.0
|
||||||
|
*/
|
||||||
|
public boolean isNetherIslandEnabled() {
|
||||||
|
return this.getPlugin().getIWM().isNetherGenerate(this.world) && this.getPlugin().getIWM().isNetherIslands(this.world);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether this island has its end island generated or not.
|
* Checks whether this island has its end island generated or not.
|
||||||
* @return {@code true} if this island has its end island generated, {@code false} otherwise.
|
* @return {@code true} if this island has its end island generated, {@code false} otherwise.
|
||||||
@ -1254,6 +1446,16 @@ public class Island implements DataObject, MetaDataAble {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether this island has its end island mode enabled or not.
|
||||||
|
* @return {@code true} if this island has its end island enabled, {@code false} otherwise.
|
||||||
|
* @since 1.21.0
|
||||||
|
*/
|
||||||
|
public boolean isEndIslandEnabled() {
|
||||||
|
return this.getPlugin().getIWM().isEndGenerate(this.world) && this.getPlugin().getIWM().isEndIslands(this.world);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if a flag is on cooldown. Only stored in memory so a server restart will reset the cooldown.
|
* Checks if a flag is on cooldown. Only stored in memory so a server restart will reset the cooldown.
|
||||||
* @param flag - flag
|
* @param flag - flag
|
||||||
@ -1261,10 +1463,10 @@ public class Island implements DataObject, MetaDataAble {
|
|||||||
* @since 1.6.0
|
* @since 1.6.0
|
||||||
*/
|
*/
|
||||||
public boolean isCooldown(Flag flag) {
|
public boolean isCooldown(Flag flag) {
|
||||||
if (cooldowns.containsKey(flag) && cooldowns.get(flag) > System.currentTimeMillis()) {
|
if (cooldowns.containsKey(flag.getID()) && cooldowns.get(flag.getID()) > System.currentTimeMillis()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
cooldowns.remove(flag);
|
cooldowns.remove(flag.getID());
|
||||||
setChanged();
|
setChanged();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1274,21 +1476,21 @@ public class Island implements DataObject, MetaDataAble {
|
|||||||
* @param flag - Flag to cooldown
|
* @param flag - Flag to cooldown
|
||||||
*/
|
*/
|
||||||
public void setCooldown(Flag flag) {
|
public void setCooldown(Flag flag) {
|
||||||
cooldowns.put(flag, flag.getCooldown() * 1000L + System.currentTimeMillis());
|
cooldowns.put(flag.getID(), flag.getCooldown() * 1000L + System.currentTimeMillis());
|
||||||
setChanged();
|
setChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the cooldowns
|
* @return the cooldowns
|
||||||
*/
|
*/
|
||||||
public Map<Flag, Long> getCooldowns() {
|
public Map<String, Long> getCooldowns() {
|
||||||
return cooldowns;
|
return cooldowns;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param cooldowns the cooldowns to set
|
* @param cooldowns the cooldowns to set
|
||||||
*/
|
*/
|
||||||
public void setCooldowns(Map<Flag, Long> cooldowns) {
|
public void setCooldowns(Map<String, Long> cooldowns) {
|
||||||
this.cooldowns = cooldowns;
|
this.cooldowns = cooldowns;
|
||||||
setChanged();
|
setChanged();
|
||||||
}
|
}
|
||||||
@ -1661,9 +1863,4 @@ public class Island implements DataObject, MetaDataAble {
|
|||||||
+ ", cooldowns=" + cooldowns + ", commandRanks=" + commandRanks + ", reserved=" + reserved
|
+ ", cooldowns=" + cooldowns + ", commandRanks=" + commandRanks + ", reserved=" + reserved
|
||||||
+ ", metaData=" + metaData + ", homes=" + homes + ", maxHomes=" + maxHomes + "]";
|
+ ", metaData=" + metaData + ", homes=" + homes + ", maxHomes=" + maxHomes + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,65 @@
|
|||||||
|
package world.bentobox.bentobox.database.objects.adapters;
|
||||||
|
|
||||||
|
|
||||||
|
import org.bukkit.configuration.MemorySection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This Serializer migrates Map of String, Boolean to Map of String, Integer in serialization process.
|
||||||
|
* It is necessary because current implementation requires flags to be mapped to Integer value.
|
||||||
|
* @author BONNe
|
||||||
|
*/
|
||||||
|
public class FlagBooleanSerializer implements AdapterInterface<Map<String, Integer>, Map<String, Boolean>>
|
||||||
|
{
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public Map<String, Integer> deserialize(Object object)
|
||||||
|
{
|
||||||
|
Map<String, Integer> result = new HashMap<>();
|
||||||
|
if (object == null)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
// For YAML
|
||||||
|
if (object instanceof MemorySection section)
|
||||||
|
{
|
||||||
|
for (String key : section.getKeys(false))
|
||||||
|
{
|
||||||
|
result.put(key, section.getBoolean(key) ? 0 : -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (Entry<String, Boolean> en : ((Map<String, Boolean>) object).entrySet())
|
||||||
|
{
|
||||||
|
result.put(en.getKey(), en.getValue() ? 0 : -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public Map<String, Boolean> serialize(Object object)
|
||||||
|
{
|
||||||
|
Map<String, Boolean> result = new HashMap<>();
|
||||||
|
|
||||||
|
if (object == null)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Integer> flags = (Map<String, Integer>) object;
|
||||||
|
|
||||||
|
for (Entry<String, Integer> en : flags.entrySet())
|
||||||
|
{
|
||||||
|
result.put(en.getKey(), en.getValue() >= 0);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
@ -626,6 +626,8 @@ public class LangUtilsHook extends Hook {
|
|||||||
case MUSIC_DISC_11 -> "C418 - 11";
|
case MUSIC_DISC_11 -> "C418 - 11";
|
||||||
case MUSIC_DISC_WAIT -> "C418 - wait";
|
case MUSIC_DISC_WAIT -> "C418 - wait";
|
||||||
case MUSIC_DISC_PIGSTEP -> "Lena Raine - Pigstep";
|
case MUSIC_DISC_PIGSTEP -> "Lena Raine - Pigstep";
|
||||||
|
case MUSIC_DISC_5 -> "Samuel Åberg - 5";
|
||||||
|
case MUSIC_DISC_OTHERSIDE -> "Lena Raine - otherside";
|
||||||
default -> null;
|
default -> null;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ import world.bentobox.bentobox.database.objects.Island;
|
|||||||
/**
|
/**
|
||||||
* Abstracts PlayerPortalEvent and EntityPortalEvent
|
* Abstracts PlayerPortalEvent and EntityPortalEvent
|
||||||
* @author tastybento
|
* @author tastybento
|
||||||
*
|
* @deprecated replaced not used in new listeners.
|
||||||
*/
|
*/
|
||||||
public class PlayerEntityPortalEvent {
|
public class PlayerEntityPortalEvent {
|
||||||
|
|
||||||
|
@ -40,7 +40,11 @@ import world.bentobox.bentobox.util.teleport.SafeSpotTeleport;
|
|||||||
* Handles teleportation via the Nether/End portals to the Nether and End dimensions of the worlds added by the GameModeAddons.
|
* Handles teleportation via the Nether/End portals to the Nether and End dimensions of the worlds added by the GameModeAddons.
|
||||||
*
|
*
|
||||||
* @author tastybento
|
* @author tastybento
|
||||||
|
* @deprecated replaced by better listeners.
|
||||||
|
* @see world.bentobox.bentobox.listeners.teleports.PlayerTeleportListener
|
||||||
|
* @see world.bentobox.bentobox.listeners.teleports.EntityTeleportListener
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public class PortalTeleportationListener implements Listener {
|
public class PortalTeleportationListener implements Listener {
|
||||||
|
|
||||||
private final BentoBox plugin;
|
private final BentoBox plugin;
|
||||||
@ -167,7 +171,7 @@ public class PortalTeleportationListener implements Listener {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Bukkit.getServer().getAllowNether()) {
|
if (!Bukkit.getAllowNether()) {
|
||||||
e.setCancelled(true);
|
e.setCancelled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -256,19 +260,30 @@ public class PortalTeleportationListener implements Listener {
|
|||||||
* @param env - environment
|
* @param env - environment
|
||||||
* @param toWorld - to world
|
* @param toWorld - to world
|
||||||
*/
|
*/
|
||||||
Location getTo(PlayerEntityPortalEvent e, Environment env, World toWorld) {
|
Location getTo(PlayerEntityPortalEvent e, Environment env, World toWorld)
|
||||||
|
{
|
||||||
// Null check - not that useful
|
// Null check - not that useful
|
||||||
if (e.getFrom().getWorld() == null || toWorld == null) {
|
if (e.getFrom().getWorld() == null || toWorld == null)
|
||||||
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (!e.getCanCreatePortal()) {
|
|
||||||
|
Location toLocation = e.getIsland().map(island -> island.getSpawnPoint(env)).
|
||||||
|
orElse(e.getFrom().toVector().toLocation(toWorld));
|
||||||
|
|
||||||
|
// Limit Y to the min/max world height.
|
||||||
|
toLocation.setY(Math.max(Math.min(toLocation.getY(), toWorld.getMaxHeight()), toWorld.getMinHeight()));
|
||||||
|
|
||||||
|
if (!e.getCanCreatePortal())
|
||||||
|
{
|
||||||
// Legacy portaling
|
// Legacy portaling
|
||||||
return e.getIsland().map(i -> i.getSpawnPoint(env)).orElse(e.getFrom().toVector().toLocation(toWorld));
|
return toLocation;
|
||||||
}
|
}
|
||||||
// Make portals
|
// Make portals
|
||||||
// For anywhere other than the end - it is the player's location that is used
|
// For anywhere other than the end - it is the player's location that is used
|
||||||
if (!env.equals(Environment.THE_END)) {
|
if (!env.equals(Environment.THE_END))
|
||||||
return e.getFrom().toVector().toLocation(toWorld);
|
{
|
||||||
|
return toLocation;
|
||||||
}
|
}
|
||||||
// If the-end then we want the platform to always be generated in the same place no matter where
|
// If the-end then we want the platform to always be generated in the same place no matter where
|
||||||
// they enter the portal
|
// they enter the portal
|
||||||
@ -279,13 +294,15 @@ public class PortalTeleportationListener implements Listener {
|
|||||||
int j = z;
|
int j = z;
|
||||||
int k = y;
|
int k = y;
|
||||||
// If the from is not a portal, then we have to find it
|
// If the from is not a portal, then we have to find it
|
||||||
if (!e.getFrom().getBlock().getType().equals(Material.END_PORTAL)) {
|
if (!e.getFrom().getBlock().getType().equals(Material.END_PORTAL))
|
||||||
|
{
|
||||||
// Find the portal - due to speed, it is possible that the player will be below or above the portal
|
// Find the portal - due to speed, it is possible that the player will be below or above the portal
|
||||||
for (k = 0; (k < e.getWorld().getMaxHeight()) && !e.getWorld().getBlockAt(x, k, z).getType().equals(Material.END_PORTAL); k++);
|
for (k = toWorld.getMinHeight(); (k < e.getWorld().getMaxHeight()) &&
|
||||||
|
!e.getWorld().getBlockAt(x, k, z).getType().equals(Material.END_PORTAL); k++);
|
||||||
}
|
}
|
||||||
// Find the maximum x and z corner
|
// Find the maximum x and z corner
|
||||||
for (; (i < x + 5) && e.getWorld().getBlockAt(i, k, z).getType().equals(Material.END_PORTAL); i++);
|
for (; (i < x + 5) && e.getWorld().getBlockAt(i, k, z).getType().equals(Material.END_PORTAL); i++) ;
|
||||||
for (; (j < z + 5) && e.getWorld().getBlockAt(x, k, j).getType().equals(Material.END_PORTAL); j++);
|
for (; (j < z + 5) && e.getWorld().getBlockAt(x, k, j).getType().equals(Material.END_PORTAL); j++) ;
|
||||||
|
|
||||||
// Mojang end platform generation is:
|
// Mojang end platform generation is:
|
||||||
// AIR
|
// AIR
|
||||||
@ -293,7 +310,7 @@ public class PortalTeleportationListener implements Listener {
|
|||||||
// OBSIDIAN
|
// OBSIDIAN
|
||||||
// and player is placed on second air block above obsidian.
|
// and player is placed on second air block above obsidian.
|
||||||
// If Y coordinate is below 2, then obsidian platform is not generated and player falls in void.
|
// If Y coordinate is below 2, then obsidian platform is not generated and player falls in void.
|
||||||
return new Location(toWorld, i, Math.max(2, k), j);
|
return new Location(toWorld, i, Math.max(toWorld.getMinHeight() + 2, k), j);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -304,7 +321,9 @@ public class PortalTeleportationListener implements Listener {
|
|||||||
* @return true or false
|
* @return true or false
|
||||||
*/
|
*/
|
||||||
private boolean isMakePortals(GameModeAddon gm, Environment env) {
|
private boolean isMakePortals(GameModeAddon gm, Environment env) {
|
||||||
return env.equals(Environment.NETHER) ? gm.getWorldSettings().isMakeNetherPortals() : gm.getWorldSettings().isMakeEndPortals();
|
return env.equals(Environment.NETHER) ?
|
||||||
|
gm.getWorldSettings().isMakeNetherPortals() && Bukkit.getAllowNether() :
|
||||||
|
gm.getWorldSettings().isMakeEndPortals() && Bukkit.getAllowEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
package world.bentobox.bentobox.listeners.flags.protection;
|
package world.bentobox.bentobox.listeners.flags.protection;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.bukkit.FluidCollisionMode;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.Tag;
|
import org.bukkit.Tag;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
|
import org.bukkit.block.data.Waterlogged;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.Event;
|
import org.bukkit.event.Event;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
@ -26,235 +29,269 @@ import world.bentobox.bentobox.lists.Flags;
|
|||||||
* Handle interaction with blocks
|
* Handle interaction with blocks
|
||||||
* @author tastybento
|
* @author tastybento
|
||||||
*/
|
*/
|
||||||
public class BlockInteractionListener extends FlagListener {
|
public class BlockInteractionListener extends FlagListener
|
||||||
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* These cover materials in another server version.
|
* These cover materials in another server version. This avoids run time errors due to unknown enum values, at the
|
||||||
* This avoids run time errors due to unknown enum values, at the expense of a string comparison
|
* expense of a string comparison
|
||||||
*/
|
*/
|
||||||
private final static Map<String, String> stringFlags;
|
private final static Map<String, String> stringFlags;
|
||||||
static {
|
|
||||||
stringFlags = Map.of("RESPAWN_ANCHOR", "PLACE_BLOCKS");
|
static
|
||||||
|
{
|
||||||
|
stringFlags = Map.of(
|
||||||
|
"ACACIA_CHEST_BOAT", "CHEST",
|
||||||
|
"BIRCH_CHEST_BOAT", "CHEST",
|
||||||
|
"JUNGLE_CHEST_BOAT", "CHEST",
|
||||||
|
"DARK_OAK_CHEST_BOAT", "CHEST",
|
||||||
|
"MANGROVE_CHEST_BOAT", "CHEST",
|
||||||
|
"OAK_CHEST_BOAT", "CHEST",
|
||||||
|
"SPRUCE_CHEST_BOAT", "CHEST");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle interaction with blocks
|
* Handle interaction with blocks
|
||||||
|
*
|
||||||
* @param e - event
|
* @param e - event
|
||||||
*/
|
*/
|
||||||
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
||||||
public void onPlayerInteract(final PlayerInteractEvent e) {
|
public void onPlayerInteract(final PlayerInteractEvent e)
|
||||||
|
{
|
||||||
// We only care about the RIGHT_CLICK_BLOCK action.
|
// We only care about the RIGHT_CLICK_BLOCK action.
|
||||||
if (!e.getAction().equals(Action.RIGHT_CLICK_BLOCK) || e.getClickedBlock() == null) {
|
if (!e.getAction().equals(Action.RIGHT_CLICK_BLOCK) || e.getClickedBlock() == null)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check clicked block
|
// Check clicked block
|
||||||
checkClickedBlock(e, e.getPlayer(), e.getClickedBlock().getLocation(), e.getClickedBlock().getType());
|
this.checkClickedBlock(e, e.getPlayer(), e.getClickedBlock().getLocation(), e.getClickedBlock().getType());
|
||||||
|
|
||||||
// Now check for in-hand items
|
// Now check for in-hand items
|
||||||
if (e.getItem() != null && !e.getItem().getType().equals(Material.AIR)) {
|
if (e.getItem() != null && !e.getItem().getType().equals(Material.AIR))
|
||||||
|
{
|
||||||
// Boats
|
// Boats
|
||||||
if (e.getItem().getType().name().endsWith("_BOAT")) {
|
if (e.getItem().getType().name().endsWith("BOAT"))
|
||||||
checkIsland(e, e.getPlayer(), e.getClickedBlock().getLocation(), Flags.BOAT);
|
{
|
||||||
|
this.checkIsland(e, e.getPlayer(), e.getClickedBlock().getLocation(), Flags.BOAT);
|
||||||
}
|
}
|
||||||
// Spawn eggs
|
else if (e.getItem().getType().name().endsWith("_SPAWN_EGG"))
|
||||||
else if (e.getItem().getType().name().endsWith("_SPAWN_EGG")) {
|
{
|
||||||
checkIsland(e, e.getPlayer(), e.getClickedBlock().getLocation(), Flags.SPAWN_EGGS);
|
this.checkIsland(e, e.getPlayer(), e.getClickedBlock().getLocation(), Flags.SPAWN_EGGS);
|
||||||
}
|
}
|
||||||
// Now check for in-hand items
|
else if (e.getItem().getType() == Material.ENDER_PEARL)
|
||||||
if (e.getItem() != null) {
|
{
|
||||||
if (e.getItem().getType().name().contains("BOAT")) {
|
this.checkIsland(e, e.getPlayer(), e.getClickedBlock().getLocation(), Flags.ENDER_PEARL);
|
||||||
checkIsland(e, e.getPlayer(), e.getClickedBlock().getLocation(), Flags.PLACE_BLOCKS);
|
}
|
||||||
return;
|
else if (e.getItem().getType() == Material.BONE_MEAL)
|
||||||
}
|
{
|
||||||
switch (e.getItem().getType()) {
|
this.checkIsland(e, e.getPlayer(), e.getClickedBlock().getLocation(), Flags.PLACE_BLOCKS);
|
||||||
case ENDER_PEARL:
|
}
|
||||||
checkIsland(e, e.getPlayer(), e.getClickedBlock().getLocation(), Flags.ENDER_PEARL);
|
else if (e.getItem().getType() == Material.GLASS_BOTTLE)
|
||||||
break;
|
{
|
||||||
case BONE_MEAL:
|
Block targetedBlock = e.getPlayer().getTargetBlockExact(5, FluidCollisionMode.ALWAYS);
|
||||||
checkIsland(e, e.getPlayer(), e.getClickedBlock().getLocation(), Flags.PLACE_BLOCKS);
|
|
||||||
break;
|
// Check if player is clicking on water or waterlogged block with a bottle.
|
||||||
default:
|
if (targetedBlock != null && (Material.WATER.equals(targetedBlock.getType()) ||
|
||||||
break;
|
targetedBlock.getBlockData() instanceof Waterlogged))
|
||||||
|
{
|
||||||
|
this.checkIsland(e, e.getPlayer(), e.getClickedBlock().getLocation(), Flags.BREWING);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if an action can occur on a clicked block
|
* Check if an action can occur on a clicked block
|
||||||
|
*
|
||||||
* @param e - event called
|
* @param e - event called
|
||||||
* @param player - player
|
* @param player - player
|
||||||
* @param loc - location of clicked block
|
* @param loc - location of clicked block
|
||||||
* @param type - material type of clicked block
|
* @param type - material type of clicked block
|
||||||
*/
|
*/
|
||||||
private void checkClickedBlock(Event e, Player player, Location loc, Material type) {
|
private void checkClickedBlock(Event e, Player player, Location loc, Material type)
|
||||||
|
{
|
||||||
// Handle pots
|
// Handle pots
|
||||||
if (type.name().startsWith("POTTED")) {
|
if (type.name().startsWith("POTTED"))
|
||||||
checkIsland(e, player, loc, Flags.FLOWER_POT);
|
{
|
||||||
return;
|
this.checkIsland(e, player, loc, Flags.FLOWER_POT);
|
||||||
}
|
|
||||||
if (Tag.ANVIL.isTagged(type)) {
|
|
||||||
checkIsland(e, player, loc, Flags.ANVIL);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (Tag.BUTTONS.isTagged(type)) {
|
|
||||||
checkIsland(e, player, loc, Flags.BUTTON);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (Tag.BEDS.isTagged(type)) {
|
|
||||||
checkIsland(e, player, loc, Flags.BED);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (Tag.DOORS.isTagged(type)) {
|
|
||||||
checkIsland(e, player, loc, Flags.DOOR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (Tag.SHULKER_BOXES.isTagged(type)) {
|
|
||||||
checkIsland(e, player, loc, Flags.SHULKER_BOX);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (Tag.TRAPDOORS.isTagged(type)) {
|
|
||||||
checkIsland(e, player, loc, Flags.TRAPDOOR);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (type) {
|
if (Tag.ANVIL.isTagged(type))
|
||||||
case BEACON:
|
{
|
||||||
checkIsland(e, player, loc, Flags.BEACON);
|
this.checkIsland(e, player, loc, Flags.ANVIL);
|
||||||
break;
|
return;
|
||||||
case BREWING_STAND:
|
}
|
||||||
case CAULDRON:
|
|
||||||
checkIsland(e, player, loc, Flags.BREWING);
|
if (Tag.BUTTONS.isTagged(type))
|
||||||
break;
|
{
|
||||||
case BEEHIVE:
|
this.checkIsland(e, player, loc, Flags.BUTTON);
|
||||||
case BEE_NEST:
|
return;
|
||||||
checkIsland(e, player, loc, Flags.HIVE);
|
}
|
||||||
break;
|
|
||||||
case BARREL:
|
if (Tag.BEDS.isTagged(type))
|
||||||
checkIsland(e, player, loc, Flags.BARREL);
|
{
|
||||||
break;
|
this.checkIsland(e, player, loc, Flags.BED);
|
||||||
case CHEST:
|
return;
|
||||||
case CHEST_MINECART:
|
}
|
||||||
checkIsland(e, player, loc, Flags.CHEST);
|
|
||||||
break;
|
if (Tag.DOORS.isTagged(type))
|
||||||
case TRAPPED_CHEST:
|
{
|
||||||
checkIsland(e, player, loc, Flags.TRAPPED_CHEST);
|
this.checkIsland(e, player, loc, Flags.DOOR);
|
||||||
break;
|
return;
|
||||||
case FLOWER_POT:
|
}
|
||||||
checkIsland(e, player, loc, Flags.FLOWER_POT);
|
|
||||||
break;
|
if (Tag.SHULKER_BOXES.isTagged(type))
|
||||||
case COMPOSTER:
|
{
|
||||||
checkIsland(e, player, loc, Flags.COMPOSTER);
|
this.checkIsland(e, player, loc, Flags.SHULKER_BOX);
|
||||||
break;
|
return;
|
||||||
case DISPENSER:
|
}
|
||||||
checkIsland(e, player, loc, Flags.DISPENSER);
|
|
||||||
break;
|
if (Tag.TRAPDOORS.isTagged(type))
|
||||||
case DROPPER:
|
{
|
||||||
checkIsland(e, player, loc, Flags.DROPPER);
|
this.checkIsland(e, player, loc, Flags.TRAPDOOR);
|
||||||
break;
|
return;
|
||||||
case HOPPER:
|
}
|
||||||
case HOPPER_MINECART:
|
|
||||||
checkIsland(e, player, loc, Flags.HOPPER);
|
if (Tag.FENCE_GATES.isTagged(type))
|
||||||
break;
|
{
|
||||||
case BLAST_FURNACE:
|
this.checkIsland(e, player, loc, Flags.GATE);
|
||||||
case CAMPFIRE:
|
}
|
||||||
case FURNACE_MINECART:
|
// TODO: 1.18 compatibility
|
||||||
case FURNACE:
|
// if (Tag.ITEMS_CHEST_BOATS.isTagged(type)) {
|
||||||
case SMOKER:
|
// this.checkIsland(e, player, loc, Flags.CHEST);
|
||||||
checkIsland(e, player, loc, Flags.FURNACE);
|
// }
|
||||||
break;
|
|
||||||
case ENCHANTING_TABLE:
|
switch (type)
|
||||||
checkIsland(e, player, loc, Flags.ENCHANTING);
|
{
|
||||||
break;
|
case BEACON -> this.checkIsland(e, player, loc, Flags.BEACON);
|
||||||
case ENDER_CHEST:
|
case BREWING_STAND -> this.checkIsland(e, player, loc, Flags.BREWING);
|
||||||
checkIsland(e, player, loc, Flags.ENDER_CHEST);
|
case BEEHIVE, BEE_NEST -> this.checkIsland(e, player, loc, Flags.HIVE);
|
||||||
break;
|
case BARREL -> this.checkIsland(e, player, loc, Flags.BARREL);
|
||||||
case JUKEBOX:
|
case CHEST, CHEST_MINECART -> this.checkIsland(e, player, loc, Flags.CHEST);
|
||||||
checkIsland(e, player, loc, Flags.JUKEBOX);
|
case TRAPPED_CHEST -> this.checkIsland(e, player, loc, Flags.TRAPPED_CHEST);
|
||||||
break;
|
case FLOWER_POT -> this.checkIsland(e, player, loc, Flags.FLOWER_POT);
|
||||||
case NOTE_BLOCK:
|
case COMPOSTER -> this.checkIsland(e, player, loc, Flags.COMPOSTER);
|
||||||
checkIsland(e, player, loc, Flags.NOTE_BLOCK);
|
case DISPENSER -> this.checkIsland(e, player, loc, Flags.DISPENSER);
|
||||||
break;
|
case DROPPER -> this.checkIsland(e, player, loc, Flags.DROPPER);
|
||||||
case CRAFTING_TABLE:
|
case HOPPER, HOPPER_MINECART -> this.checkIsland(e, player, loc, Flags.HOPPER);
|
||||||
case CARTOGRAPHY_TABLE:
|
case BLAST_FURNACE, CAMPFIRE, FURNACE_MINECART, FURNACE, SMOKER ->
|
||||||
case GRINDSTONE:
|
this.checkIsland(e, player, loc, Flags.FURNACE);
|
||||||
case STONECUTTER:
|
case ENCHANTING_TABLE -> this.checkIsland(e, player, loc, Flags.ENCHANTING);
|
||||||
case LOOM:
|
case ENDER_CHEST -> this.checkIsland(e, player, loc, Flags.ENDER_CHEST);
|
||||||
checkIsland(e, player, loc, Flags.CRAFTING);
|
case JUKEBOX -> this.checkIsland(e, player, loc, Flags.JUKEBOX);
|
||||||
break;
|
case NOTE_BLOCK -> this.checkIsland(e, player, loc, Flags.NOTE_BLOCK);
|
||||||
case LEVER:
|
case CRAFTING_TABLE, CARTOGRAPHY_TABLE, GRINDSTONE, STONECUTTER, LOOM ->
|
||||||
checkIsland(e, player, loc, Flags.LEVER);
|
this.checkIsland(e, player, loc, Flags.CRAFTING);
|
||||||
break;
|
case LEVER -> this.checkIsland(e, player, loc, Flags.LEVER);
|
||||||
case REDSTONE_WIRE:
|
case REDSTONE_WIRE, REPEATER, COMPARATOR, DAYLIGHT_DETECTOR -> this.checkIsland(e, player, loc, Flags.REDSTONE);
|
||||||
case REPEATER:
|
case DRAGON_EGG -> this.checkIsland(e, player, loc, Flags.DRAGON_EGG);
|
||||||
case COMPARATOR:
|
case END_PORTAL_FRAME, RESPAWN_ANCHOR -> this.checkIsland(e, player, loc, Flags.PLACE_BLOCKS);
|
||||||
case DAYLIGHT_DETECTOR:
|
case GLOW_ITEM_FRAME, ITEM_FRAME -> this.checkIsland(e, player, loc, Flags.ITEM_FRAME);
|
||||||
checkIsland(e, player, loc, Flags.REDSTONE);
|
case SWEET_BERRY_BUSH, CAVE_VINES -> this.checkIsland(e, player, loc, Flags.BREAK_BLOCKS);
|
||||||
break;
|
case CAKE -> this.checkIsland(e, player, loc, Flags.CAKE);
|
||||||
case DRAGON_EGG:
|
case LAVA_CAULDRON ->
|
||||||
checkIsland(e, player, loc, Flags.DRAGON_EGG);
|
{
|
||||||
break;
|
if (BlockInteractionListener.holds(player, Material.BUCKET))
|
||||||
case END_PORTAL_FRAME:
|
{
|
||||||
checkIsland(e, player, loc, Flags.PLACE_BLOCKS);
|
this.checkIsland(e, player, loc, Flags.COLLECT_LAVA);
|
||||||
break;
|
}
|
||||||
case ITEM_FRAME:
|
}
|
||||||
checkIsland(e, player, loc, Flags.ITEM_FRAME);
|
case WATER_CAULDRON ->
|
||||||
break;
|
{
|
||||||
case SWEET_BERRY_BUSH:
|
if (BlockInteractionListener.holds(player, Material.BUCKET))
|
||||||
checkIsland(e, player, loc, Flags.BREAK_BLOCKS);
|
{
|
||||||
break;
|
this.checkIsland(e, player, loc, Flags.COLLECT_WATER);
|
||||||
case CAKE:
|
}
|
||||||
checkIsland(e, player, loc, Flags.CAKE);
|
else if (BlockInteractionListener.holds(player, Material.GLASS_BOTTLE) ||
|
||||||
break;
|
BlockInteractionListener.holds(player, Material.POTION))
|
||||||
case OAK_FENCE_GATE:
|
{
|
||||||
case SPRUCE_FENCE_GATE:
|
this.checkIsland(e, player, loc, Flags.BREWING);
|
||||||
case BIRCH_FENCE_GATE:
|
}
|
||||||
case JUNGLE_FENCE_GATE:
|
}
|
||||||
case DARK_OAK_FENCE_GATE:
|
case POWDER_SNOW_CAULDRON ->
|
||||||
case ACACIA_FENCE_GATE:
|
{
|
||||||
case CRIMSON_FENCE_GATE:
|
if (BlockInteractionListener.holds(player, Material.BUCKET))
|
||||||
case WARPED_FENCE_GATE:
|
{
|
||||||
checkIsland(e, player, loc, Flags.GATE);
|
this.checkIsland(e, player, loc, Flags.COLLECT_POWDERED_SNOW);
|
||||||
break;
|
}
|
||||||
default:
|
}
|
||||||
if (stringFlags.containsKey(type.name())) {
|
case CAULDRON ->
|
||||||
Optional<Flag> f = BentoBox.getInstance().getFlagsManager().getFlag(stringFlags.get(type.name()));
|
{
|
||||||
f.ifPresent(flag -> checkIsland(e, player, loc, flag));
|
if (BlockInteractionListener.holds(player, Material.WATER_BUCKET) ||
|
||||||
|
BlockInteractionListener.holds(player, Material.LAVA_BUCKET) ||
|
||||||
|
BlockInteractionListener.holds(player, Material.POWDER_SNOW_BUCKET))
|
||||||
|
{
|
||||||
|
this.checkIsland(e, player, loc, Flags.BUCKET);
|
||||||
|
}
|
||||||
|
else if (BlockInteractionListener.holds(player, Material.POTION))
|
||||||
|
{
|
||||||
|
this.checkIsland(e, player, loc, Flags.BREWING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default ->
|
||||||
|
{
|
||||||
|
if (stringFlags.containsKey(type.name()))
|
||||||
|
{
|
||||||
|
Optional<Flag> f = BentoBox.getInstance().getFlagsManager().getFlag(stringFlags.get(type.name()));
|
||||||
|
f.ifPresent(flag -> this.checkIsland(e, player, loc, flag));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When breaking blocks is allowed, this protects
|
* When breaking blocks is allowed, this protects specific blocks from being broken, which would bypass the
|
||||||
* specific blocks from being broken, which would bypass the protection.
|
* protection. For example, player enables break blocks, but chests are still protected Fires after the BreakBlocks
|
||||||
* For example, player enables break blocks, but chests are still protected
|
* check.
|
||||||
* Fires after the BreakBlocks check.
|
|
||||||
*
|
*
|
||||||
* @param e - event
|
* @param e - event
|
||||||
*/
|
*/
|
||||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||||
public void onBlockBreak(final BlockBreakEvent e) {
|
public void onBlockBreak(final BlockBreakEvent e)
|
||||||
checkClickedBlock(e, e.getPlayer(), e.getBlock().getLocation(), e.getBlock().getType());
|
{
|
||||||
|
this.checkClickedBlock(e, e.getPlayer(), e.getBlock().getLocation(), e.getBlock().getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prevents dragon eggs from flying out of an island's protected space
|
* Prevents dragon eggs from flying out of an island's protected space
|
||||||
|
*
|
||||||
* @param e - event
|
* @param e - event
|
||||||
*/
|
*/
|
||||||
@EventHandler(priority = EventPriority.LOWEST)
|
@EventHandler(priority = EventPriority.LOWEST)
|
||||||
public void onDragonEggTeleport(BlockFromToEvent e) {
|
public void onDragonEggTeleport(BlockFromToEvent e)
|
||||||
|
{
|
||||||
Block block = e.getBlock();
|
Block block = e.getBlock();
|
||||||
if (!block.getType().equals(Material.DRAGON_EGG) || !getIWM().inWorld(block.getLocation())) {
|
|
||||||
|
if (!block.getType().equals(Material.DRAGON_EGG) || !this.getIWM().inWorld(block.getLocation()))
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If egg starts in a protected island...
|
// If egg starts in a protected island...
|
||||||
// Cancel if toIsland is not fromIsland or if there is no protected island there
|
// Cancel if toIsland is not fromIsland or if there is no protected island there
|
||||||
// This protects against eggs dropping into adjacent islands, e.g. island distance and protection range are equal
|
// This protects against eggs dropping into adjacent islands, e.g. island distance and protection range are equal
|
||||||
Optional<Island> fromIsland = getIslands().getProtectedIslandAt(block.getLocation());
|
Optional<Island> fromIsland = this.getIslands().getProtectedIslandAt(block.getLocation());
|
||||||
Optional<Island> toIsland = getIslands().getProtectedIslandAt(e.getToBlock().getLocation());
|
Optional<Island> toIsland = this.getIslands().getProtectedIslandAt(e.getToBlock().getLocation());
|
||||||
fromIsland.ifPresent(from -> e.setCancelled(toIsland.map(to -> to != from).orElse(true)));
|
fromIsland.ifPresent(from -> e.setCancelled(toIsland.map(to -> to != from).orElse(true)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns if player is holding given material in main or offhand.
|
||||||
|
* @param player Player that must be checked.
|
||||||
|
* @param material item that mus t be checjed.
|
||||||
|
* @return {@code true} if player is holding item in main hand or offhand.
|
||||||
|
*/
|
||||||
|
private static boolean holds(Player player, Material material)
|
||||||
|
{
|
||||||
|
return player.getInventory().getItemInMainHand().getType().equals(material) ||
|
||||||
|
player.getInventory().getItemInOffHand().getType().equals(material);
|
||||||
|
}
|
||||||
}
|
}
|
@ -58,19 +58,23 @@ public class BreakBlocksListener extends FlagListener {
|
|||||||
* @param e - event
|
* @param e - event
|
||||||
*/
|
*/
|
||||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||||
public void onPlayerInteract(final PlayerInteractEvent e) {
|
public void onPlayerInteract(final PlayerInteractEvent e)
|
||||||
|
{
|
||||||
// Only handle hitting things
|
// Only handle hitting things
|
||||||
if (!e.getAction().equals(Action.LEFT_CLICK_BLOCK)) {
|
if (!e.getAction().equals(Action.LEFT_CLICK_BLOCK) || e.getClickedBlock() == null)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Player p = e.getPlayer();
|
Player p = e.getPlayer();
|
||||||
Location l = e.getClickedBlock().getLocation();
|
Location l = e.getClickedBlock().getLocation();
|
||||||
switch (e.getClickedBlock().getType()) {
|
|
||||||
case CAKE -> checkIsland(e, p, l, Flags.BREAK_BLOCKS);
|
switch (e.getClickedBlock().getType())
|
||||||
case SPAWNER -> checkIsland(e, p, l, Flags.BREAK_SPAWNERS);
|
{
|
||||||
case DRAGON_EGG -> checkIsland(e, p, l, Flags.DRAGON_EGG);
|
case CAKE -> this.checkIsland(e, p, l, Flags.BREAK_BLOCKS);
|
||||||
case HOPPER -> checkIsland(e, p, l, Flags.BREAK_HOPPERS);
|
case SPAWNER -> this.checkIsland(e, p, l, Flags.BREAK_SPAWNERS);
|
||||||
default -> {}
|
case DRAGON_EGG -> this.checkIsland(e, p, l, Flags.DRAGON_EGG);
|
||||||
|
case HOPPER -> this.checkIsland(e, p, l, Flags.BREAK_HOPPERS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,16 +83,26 @@ public class BreakBlocksListener extends FlagListener {
|
|||||||
* @param e - event
|
* @param e - event
|
||||||
*/
|
*/
|
||||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled=true)
|
@EventHandler(priority = EventPriority.LOW, ignoreCancelled=true)
|
||||||
public void onVehicleDamageEvent(VehicleDamageEvent e) {
|
public void onVehicleDamageEvent(VehicleDamageEvent e)
|
||||||
|
{
|
||||||
Location l = e.getVehicle().getLocation();
|
Location l = e.getVehicle().getLocation();
|
||||||
if (getIWM().inWorld(l) && e.getAttacker() instanceof Player p) {
|
|
||||||
String vehicleType = e.getVehicle().getType().toString();
|
if (getIWM().inWorld(l) && e.getAttacker() instanceof Player p)
|
||||||
if (e.getVehicle().getType().equals(EntityType.BOAT)) {
|
{
|
||||||
checkIsland(e, p, l, Flags.BOAT);
|
String vehicleType = e.getVehicle().getType().name();
|
||||||
} else if (vehicleType.contains("MINECART")) {
|
|
||||||
checkIsland(e, p, l, Flags.MINECART);
|
// 1.19 introduced Chest Boat.
|
||||||
} else {
|
if (vehicleType.contains("BOAT"))
|
||||||
checkIsland(e, p, l, Flags.BREAK_BLOCKS);
|
{
|
||||||
|
this.checkIsland(e, p, l, Flags.BOAT);
|
||||||
|
}
|
||||||
|
else if (vehicleType.contains("MINECART"))
|
||||||
|
{
|
||||||
|
this.checkIsland(e, p, l, Flags.MINECART);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.checkIsland(e, p, l, Flags.BREAK_BLOCKS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,16 +56,27 @@ public class BreedingListener extends FlagListener {
|
|||||||
bi.put(EntityType.TURTLE, Collections.singletonList(Material.SEAGRASS));
|
bi.put(EntityType.TURTLE, Collections.singletonList(Material.SEAGRASS));
|
||||||
bi.put(EntityType.PANDA, Collections.singletonList(Material.BAMBOO));
|
bi.put(EntityType.PANDA, Collections.singletonList(Material.BAMBOO));
|
||||||
bi.put(EntityType.FOX, Collections.singletonList(Material.SWEET_BERRIES));
|
bi.put(EntityType.FOX, Collections.singletonList(Material.SWEET_BERRIES));
|
||||||
if (Enums.getIfPresent(EntityType.class, "BEES").isPresent()) { // 1.15.2
|
// 1.15+
|
||||||
bi.put(EntityType.BEE, Arrays.asList(Material.SUNFLOWER, Material.ORANGE_TULIP, Material.PINK_TULIP,
|
bi.put(EntityType.BEE, Arrays.asList(Material.SUNFLOWER, Material.ORANGE_TULIP, Material.PINK_TULIP,
|
||||||
Material.RED_TULIP, Material.WHITE_TULIP, Material.ALLIUM,
|
Material.RED_TULIP, Material.WHITE_TULIP, Material.ALLIUM,
|
||||||
Material.AZURE_BLUET, Material.BLUE_ORCHID, Material.CORNFLOWER,
|
Material.AZURE_BLUET, Material.BLUE_ORCHID, Material.CORNFLOWER,
|
||||||
Material.DANDELION, Material.OXEYE_DAISY, Material.PEONY, Material.POPPY));
|
Material.DANDELION, Material.OXEYE_DAISY, Material.PEONY, Material.POPPY));
|
||||||
}
|
// 1.16+
|
||||||
if (Enums.getIfPresent(EntityType.class, "HOGLIN").isPresent()) {
|
bi.put(EntityType.HOGLIN, Collections.singletonList(Material.CRIMSON_FUNGUS));
|
||||||
bi.put(EntityType.HOGLIN, Collections.singletonList(Material.CRIMSON_FUNGUS)); // 1.16.1
|
bi.put(EntityType.STRIDER, Collections.singletonList(Material.WARPED_FUNGUS));
|
||||||
bi.put(EntityType.STRIDER, Collections.singletonList(Material.WARPED_FUNGUS)); // 1.16.1
|
// 1.18+
|
||||||
|
bi.put(EntityType.AXOLOTL, Collections.singletonList(Material.TROPICAL_FISH_BUCKET));
|
||||||
|
bi.put(EntityType.GOAT, Collections.singletonList(Material.WHEAT));
|
||||||
|
// 1.19+
|
||||||
|
// TODO: remove one 1.18 is dropped.
|
||||||
|
if (Enums.getIfPresent(EntityType.class, "FROG").isPresent()) {
|
||||||
|
bi.put(EntityType.FROG, Collections.singletonList(Material.SLIME_BALL));
|
||||||
|
bi.put(EntityType.ALLAY, Collections.singletonList(Material.AMETHYST_SHARD));
|
||||||
}
|
}
|
||||||
|
// Helper
|
||||||
|
// if (Enums.getIfPresent(EntityType.class, "<name>").isPresent()) {
|
||||||
|
// bi.put(EntityType.<type>, Collections.singletonList(Material.<material>));
|
||||||
|
// }
|
||||||
BREEDING_ITEMS = Collections.unmodifiableMap(bi);
|
BREEDING_ITEMS = Collections.unmodifiableMap(bi);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,9 +3,10 @@ package world.bentobox.bentobox.listeners.flags.protection;
|
|||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
|
import org.bukkit.entity.Axolotl;
|
||||||
|
import org.bukkit.entity.Fish;
|
||||||
import org.bukkit.entity.MushroomCow;
|
import org.bukkit.entity.MushroomCow;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.entity.TropicalFish;
|
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.EventPriority;
|
import org.bukkit.event.EventPriority;
|
||||||
import org.bukkit.event.player.PlayerBucketEmptyEvent;
|
import org.bukkit.event.player.PlayerBucketEmptyEvent;
|
||||||
@ -31,7 +32,7 @@ public class BucketListener extends FlagListener {
|
|||||||
public void onBucketEmpty(final PlayerBucketEmptyEvent e) {
|
public void onBucketEmpty(final PlayerBucketEmptyEvent e) {
|
||||||
// This is where the water or lava actually will be dumped
|
// This is where the water or lava actually will be dumped
|
||||||
Block dumpBlock = e.getBlockClicked().getRelative(e.getBlockFace());
|
Block dumpBlock = e.getBlockClicked().getRelative(e.getBlockFace());
|
||||||
checkIsland(e, e.getPlayer(), dumpBlock.getLocation(), Flags.BUCKET);
|
this.checkIsland(e, e.getPlayer(), dumpBlock.getLocation(), Flags.BUCKET);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,19 +43,35 @@ public class BucketListener extends FlagListener {
|
|||||||
public void onBucketFill(final PlayerBucketFillEvent e) {
|
public void onBucketFill(final PlayerBucketFillEvent e) {
|
||||||
Player p = e.getPlayer();
|
Player p = e.getPlayer();
|
||||||
Location l = e.getBlockClicked().getLocation();
|
Location l = e.getBlockClicked().getLocation();
|
||||||
|
|
||||||
|
if (e.getItemStack() == null)
|
||||||
|
{
|
||||||
|
// Null-pointer check.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Check filling of various liquids
|
// Check filling of various liquids
|
||||||
switch (e.getItemStack().getType()) {
|
switch (e.getItemStack().getType())
|
||||||
case LAVA_BUCKET -> checkIsland(e, p, l, Flags.COLLECT_LAVA);
|
{
|
||||||
case WATER_BUCKET -> checkIsland(e, p, l, Flags.COLLECT_WATER);
|
case LAVA_BUCKET -> this.checkIsland(e, p, l, Flags.COLLECT_LAVA);
|
||||||
case MILK_BUCKET -> checkIsland(e, p, l, Flags.MILKING);
|
case WATER_BUCKET -> this.checkIsland(e, p, l, Flags.COLLECT_WATER);
|
||||||
default -> checkIsland(e, p, l, Flags.BUCKET);
|
case POWDER_SNOW_BUCKET -> this.checkIsland(e, p, l, Flags.COLLECT_POWDERED_SNOW);
|
||||||
|
case MILK_BUCKET -> this.checkIsland(e, p, l, Flags.MILKING);
|
||||||
|
default -> this.checkIsland(e, p, l, Flags.BUCKET);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||||
public void onTropicalFishScooping(final PlayerInteractEntityEvent e) {
|
public void onTropicalFishScooping(final PlayerInteractEntityEvent e) {
|
||||||
if (e.getRightClicked() instanceof TropicalFish && e.getPlayer().getInventory().getItemInMainHand().getType().equals(Material.WATER_BUCKET)) {
|
if (e.getRightClicked() instanceof Fish &&
|
||||||
checkIsland(e, e.getPlayer(), e.getRightClicked().getLocation(), Flags.FISH_SCOOPING);
|
e.getPlayer().getInventory().getItemInMainHand().getType().equals(Material.WATER_BUCKET))
|
||||||
|
{
|
||||||
|
this.checkIsland(e, e.getPlayer(), e.getRightClicked().getLocation(), Flags.FISH_SCOOPING);
|
||||||
|
}
|
||||||
|
else if (e.getRightClicked() instanceof Axolotl &&
|
||||||
|
e.getPlayer().getInventory().getItemInMainHand().getType().equals(Material.WATER_BUCKET))
|
||||||
|
{
|
||||||
|
this.checkIsland(e, e.getPlayer(), e.getRightClicked().getLocation(), Flags.AXOLOTL_SCOOPING);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package world.bentobox.bentobox.listeners.flags.protection;
|
package world.bentobox.bentobox.listeners.flags.protection;
|
||||||
|
|
||||||
import org.bukkit.entity.EntityType;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.EventPriority;
|
import org.bukkit.event.EventPriority;
|
||||||
import org.bukkit.event.block.Action;
|
import org.bukkit.event.block.Action;
|
||||||
import org.bukkit.event.player.PlayerInteractEntityEvent;
|
import org.bukkit.event.entity.SheepDyeWoolEvent;
|
||||||
import org.bukkit.event.player.PlayerInteractEvent;
|
import org.bukkit.event.player.PlayerInteractEvent;
|
||||||
|
|
||||||
import world.bentobox.bentobox.api.flags.FlagListener;
|
import world.bentobox.bentobox.api.flags.FlagListener;
|
||||||
@ -22,24 +22,35 @@ public class DyeListener extends FlagListener {
|
|||||||
* @param e - event
|
* @param e - event
|
||||||
*/
|
*/
|
||||||
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
||||||
public void onPlayerInteract(final PlayerInteractEvent e) {
|
public void onPlayerInteract(final PlayerInteractEvent e)
|
||||||
if (e.getClickedBlock() == null || e.getItem() == null) {
|
{
|
||||||
|
if (e.getClickedBlock() == null || e.getItem() == null)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.getAction().equals(Action.RIGHT_CLICK_BLOCK) && e.getClickedBlock().getType().name().contains("SIGN")
|
if (e.getAction().equals(Action.RIGHT_CLICK_BLOCK) &&
|
||||||
&& e.getItem().getType().name().contains("DYE")) {
|
e.getClickedBlock().getType().name().contains("SIGN") &&
|
||||||
checkIsland(e, e.getPlayer(), e.getClickedBlock().getLocation(), Flags.DYE);
|
(e.getItem().getType().name().contains("DYE") || e.getItem().getType().equals(Material.GLOW_INK_SAC)))
|
||||||
|
{
|
||||||
|
this.checkIsland(e, e.getPlayer(), e.getClickedBlock().getLocation(), Flags.DYE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevents from interacting with sheep.
|
||||||
|
* @param e - event
|
||||||
|
*/
|
||||||
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
||||||
public void onPlayerInteract(final PlayerInteractEntityEvent e) {
|
public void onPlayerInteract(final SheepDyeWoolEvent e)
|
||||||
// We cannot use SheepDyeWoolEvent since it doesn't provide who dyed the sheep
|
{
|
||||||
if (e.getRightClicked().getType().equals(EntityType.SHEEP)
|
if (e.getPlayer() == null)
|
||||||
&& (e.getPlayer().getInventory().getItemInMainHand().getType().name().contains("DYE")
|
{
|
||||||
|| e.getPlayer().getInventory().getItemInOffHand().getType().name().contains("DYE"))) {
|
// Sheep is not dyed by the player.
|
||||||
checkIsland(e, e.getPlayer(), e.getRightClicked().getLocation(), Flags.DYE);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.checkIsland(e, e.getPlayer(), e.getPlayer().getLocation(), Flags.DYE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,7 @@ package world.bentobox.bentobox.listeners.flags.protection;
|
|||||||
|
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.entity.Animals;
|
import org.bukkit.entity.*;
|
||||||
import org.bukkit.entity.ArmorStand;
|
|
||||||
import org.bukkit.entity.Boat;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.bukkit.entity.Vehicle;
|
|
||||||
import org.bukkit.entity.Villager;
|
|
||||||
import org.bukkit.entity.WanderingTrader;
|
|
||||||
import org.bukkit.entity.minecart.RideableMinecart;
|
import org.bukkit.entity.minecart.RideableMinecart;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.EventPriority;
|
import org.bukkit.event.EventPriority;
|
||||||
@ -17,6 +11,8 @@ import org.bukkit.event.player.PlayerInteractEntityEvent;
|
|||||||
|
|
||||||
import world.bentobox.bentobox.api.flags.FlagListener;
|
import world.bentobox.bentobox.api.flags.FlagListener;
|
||||||
import world.bentobox.bentobox.lists.Flags;
|
import world.bentobox.bentobox.lists.Flags;
|
||||||
|
import world.bentobox.bentobox.versions.ServerCompatibility;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles interaction with entities like armor stands
|
* Handles interaction with entities like armor stands
|
||||||
@ -34,34 +30,68 @@ public class EntityInteractListener extends FlagListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||||
public void onPlayerInteractEntity(PlayerInteractEntityEvent e) {
|
public void onPlayerInteractEntity(PlayerInteractEntityEvent e)
|
||||||
|
{
|
||||||
Player p = e.getPlayer();
|
Player p = e.getPlayer();
|
||||||
Location l = e.getRightClicked().getLocation();
|
Location l = e.getRightClicked().getLocation();
|
||||||
if (e.getRightClicked() instanceof Vehicle) {
|
|
||||||
// Animal riding
|
if (e.getRightClicked() instanceof Vehicle)
|
||||||
if (e.getRightClicked() instanceof Animals) {
|
{
|
||||||
checkIsland(e, p, l, Flags.RIDING);
|
if (e.getRightClicked() instanceof Animals)
|
||||||
|
{
|
||||||
|
// Animal riding
|
||||||
|
this.checkIsland(e, p, l, Flags.RIDING);
|
||||||
}
|
}
|
||||||
// Minecart riding
|
else if (e.getRightClicked() instanceof RideableMinecart)
|
||||||
else if (e.getRightClicked() instanceof RideableMinecart) {
|
{
|
||||||
checkIsland(e, p, l, Flags.MINECART);
|
// Minecart riding
|
||||||
|
this.checkIsland(e, p, l, Flags.MINECART);
|
||||||
}
|
}
|
||||||
// Boat riding
|
else if (!ServerCompatibility.getInstance().isVersion(
|
||||||
else if (e.getRightClicked() instanceof Boat) {
|
ServerCompatibility.ServerVersion.V1_18,
|
||||||
checkIsland(e, p, l, Flags.BOAT);
|
ServerCompatibility.ServerVersion.V1_18_1,
|
||||||
|
ServerCompatibility.ServerVersion.V1_18_2) &&
|
||||||
|
e.getPlayer().isSneaking() && e.getRightClicked() instanceof ChestBoat)
|
||||||
|
{
|
||||||
|
// Access to chest boat since 1.19
|
||||||
|
this.checkIsland(e, p, l, Flags.CHEST);
|
||||||
|
}
|
||||||
|
else if (e.getRightClicked() instanceof Boat)
|
||||||
|
{
|
||||||
|
// Boat riding
|
||||||
|
this.checkIsland(e, p, l, Flags.BOAT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Villager trading
|
else if (e.getRightClicked() instanceof Villager || e.getRightClicked() instanceof WanderingTrader)
|
||||||
else if (e.getRightClicked() instanceof Villager || e.getRightClicked() instanceof WanderingTrader) {
|
{
|
||||||
|
// Villager trading
|
||||||
// Check naming and check trading
|
// Check naming and check trading
|
||||||
checkIsland(e, p, l, Flags.TRADING);
|
this.checkIsland(e, p, l, Flags.TRADING);
|
||||||
if (e.getPlayer().getInventory().getItemInMainHand().getType().equals(Material.NAME_TAG)) {
|
|
||||||
checkIsland(e, p, l, Flags.NAME_TAG);
|
if (e.getPlayer().getInventory().getItemInMainHand().getType().equals(Material.NAME_TAG))
|
||||||
|
{
|
||||||
|
this.checkIsland(e, p, l, Flags.NAME_TAG);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Name tags
|
else if (!ServerCompatibility.getInstance().isVersion(
|
||||||
else if (e.getPlayer().getInventory().getItemInMainHand().getType().equals(Material.NAME_TAG)) {
|
ServerCompatibility.ServerVersion.V1_18,
|
||||||
checkIsland(e, p, l, Flags.NAME_TAG);
|
ServerCompatibility.ServerVersion.V1_18_1,
|
||||||
|
ServerCompatibility.ServerVersion.V1_18_2) &&
|
||||||
|
e.getRightClicked() instanceof Allay)
|
||||||
|
{
|
||||||
|
// Allay item giving/taking
|
||||||
|
this.checkIsland(e, p, l, Flags.ALLAY);
|
||||||
|
|
||||||
|
// Check naming
|
||||||
|
if (e.getPlayer().getInventory().getItemInMainHand().getType().equals(Material.NAME_TAG))
|
||||||
|
{
|
||||||
|
this.checkIsland(e, p, l, Flags.NAME_TAG);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (e.getPlayer().getInventory().getItemInMainHand().getType().equals(Material.NAME_TAG))
|
||||||
|
{
|
||||||
|
// Name tags
|
||||||
|
this.checkIsland(e, p, l, Flags.NAME_TAG);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,6 @@ import org.bukkit.entity.LivingEntity;
|
|||||||
import org.bukkit.entity.Parrot;
|
import org.bukkit.entity.Parrot;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.entity.Projectile;
|
import org.bukkit.entity.Projectile;
|
||||||
import org.bukkit.entity.Villager;
|
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.EventPriority;
|
import org.bukkit.event.EventPriority;
|
||||||
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||||
@ -31,7 +30,6 @@ import world.bentobox.bentobox.api.flags.Flag;
|
|||||||
import world.bentobox.bentobox.api.flags.FlagListener;
|
import world.bentobox.bentobox.api.flags.FlagListener;
|
||||||
import world.bentobox.bentobox.lists.Flags;
|
import world.bentobox.bentobox.lists.Flags;
|
||||||
import world.bentobox.bentobox.util.Util;
|
import world.bentobox.bentobox.util.Util;
|
||||||
import world.bentobox.bentobox.versions.ServerCompatibility;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -50,17 +48,24 @@ public class HurtingListener extends FlagListener {
|
|||||||
* @param e - event
|
* @param e - event
|
||||||
*/
|
*/
|
||||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||||
public void onEntityDamage(final EntityDamageByEntityEvent e) {
|
public void onEntityDamage(final EntityDamageByEntityEvent e)
|
||||||
|
{
|
||||||
// Mobs being hurt
|
// Mobs being hurt
|
||||||
if (Util.isPassiveEntity(e.getEntity())) {
|
if (Util.isPassiveEntity(e.getEntity()))
|
||||||
respond(e, e.getDamager(), Flags.HURT_ANIMALS);
|
{
|
||||||
} else if (e.getEntity() instanceof Villager || e.getEntityType().name().equals("WANDERING_TRADER")) { // TODO: Simplify when 1.13.2 support is dropped
|
this.respond(e, e.getDamager(), Flags.HURT_ANIMALS);
|
||||||
respond(e, e.getDamager(), Flags.HURT_VILLAGERS);
|
}
|
||||||
} else if (Util.isHostileEntity(e.getEntity())) {
|
else if (e.getEntity() instanceof AbstractVillager)
|
||||||
respond(e, e.getDamager(), Flags.HURT_MONSTERS);
|
{
|
||||||
|
this.respond(e, e.getDamager(), Flags.HURT_VILLAGERS);
|
||||||
|
}
|
||||||
|
else if (Util.isHostileEntity(e.getEntity()))
|
||||||
|
{
|
||||||
|
this.respond(e, e.getDamager(), Flags.HURT_MONSTERS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds the true attacker, even if the attack was via a projectile
|
* Finds the true attacker, even if the attack was via a projectile
|
||||||
* @param e - event
|
* @param e - event
|
||||||
@ -159,12 +164,6 @@ public class HurtingListener extends FlagListener {
|
|||||||
*/
|
*/
|
||||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled=true)
|
@EventHandler(priority = EventPriority.LOW, ignoreCancelled=true)
|
||||||
public void onLingeringPotionSplash(final LingeringPotionSplashEvent e) {
|
public void onLingeringPotionSplash(final LingeringPotionSplashEvent e) {
|
||||||
// TODO Switch this to 1.13 when we move to 1.14 officially
|
|
||||||
if (!ServerCompatibility.getInstance().isVersion(ServerCompatibility.ServerVersion.V1_14, ServerCompatibility.ServerVersion.V1_14_1)) {
|
|
||||||
// We're disabling this check for non-1.14 servers.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to get the shooter
|
// Try to get the shooter
|
||||||
Projectile projectile = e.getEntity();
|
Projectile projectile = e.getEntity();
|
||||||
if (projectile.getShooter() instanceof Player) {
|
if (projectile.getShooter() instanceof Player) {
|
||||||
|
@ -13,6 +13,7 @@ import org.bukkit.block.Furnace;
|
|||||||
import org.bukkit.block.Hopper;
|
import org.bukkit.block.Hopper;
|
||||||
import org.bukkit.block.ShulkerBox;
|
import org.bukkit.block.ShulkerBox;
|
||||||
import org.bukkit.entity.Animals;
|
import org.bukkit.entity.Animals;
|
||||||
|
import org.bukkit.entity.ChestBoat;
|
||||||
import org.bukkit.entity.NPC;
|
import org.bukkit.entity.NPC;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.entity.minecart.HopperMinecart;
|
import org.bukkit.entity.minecart.HopperMinecart;
|
||||||
@ -20,83 +21,146 @@ import org.bukkit.entity.minecart.StorageMinecart;
|
|||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.EventPriority;
|
import org.bukkit.event.EventPriority;
|
||||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||||
|
import org.bukkit.event.inventory.InventoryOpenEvent;
|
||||||
import org.bukkit.inventory.InventoryHolder;
|
import org.bukkit.inventory.InventoryHolder;
|
||||||
|
|
||||||
import world.bentobox.bentobox.BentoBox;
|
|
||||||
import world.bentobox.bentobox.api.flags.FlagListener;
|
import world.bentobox.bentobox.api.flags.FlagListener;
|
||||||
import world.bentobox.bentobox.lists.Flags;
|
import world.bentobox.bentobox.lists.Flags;
|
||||||
|
import world.bentobox.bentobox.versions.ServerCompatibility;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles inventory protection
|
* Handles inventory protection
|
||||||
* @author tastybento
|
* @author tastybento
|
||||||
*/
|
*/
|
||||||
public class InventoryListener extends FlagListener {
|
public class InventoryListener extends FlagListener
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Prevents players opening inventories
|
||||||
|
* @param event - event
|
||||||
|
*/
|
||||||
|
@EventHandler(priority = EventPriority.LOW, ignoreCancelled=true)
|
||||||
|
public void onInventoryOpen(InventoryOpenEvent event)
|
||||||
|
{
|
||||||
|
InventoryHolder inventoryHolder = event.getInventory().getHolder();
|
||||||
|
|
||||||
|
if (inventoryHolder == null || !(event.getPlayer() instanceof Player player))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inventoryHolder instanceof Animals)
|
||||||
|
{
|
||||||
|
// Prevent opening animal inventories.
|
||||||
|
this.checkIsland(event, player, event.getInventory().getLocation(), Flags.MOUNT_INVENTORY);
|
||||||
|
}
|
||||||
|
else if (!ServerCompatibility.getInstance().isVersion(ServerCompatibility.ServerVersion.V1_18,
|
||||||
|
ServerCompatibility.ServerVersion.V1_18_1,
|
||||||
|
ServerCompatibility.ServerVersion.V1_18_2) &&
|
||||||
|
inventoryHolder instanceof ChestBoat)
|
||||||
|
{
|
||||||
|
// Prevent opening chest inventories
|
||||||
|
this.checkIsland(event, player, event.getInventory().getLocation(), Flags.CHEST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prevents players picking items from inventories
|
* Prevents players picking items from inventories
|
||||||
* @param e - event
|
* @param e - event
|
||||||
*/
|
*/
|
||||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled=true)
|
@EventHandler(priority = EventPriority.LOW, ignoreCancelled=true)
|
||||||
public void onInventoryClick(InventoryClickEvent e) {
|
public void onInventoryClick(InventoryClickEvent e)
|
||||||
Player player = (Player)e.getWhoClicked();
|
{
|
||||||
|
Player player = (Player) e.getWhoClicked();
|
||||||
InventoryHolder inventoryHolder = e.getInventory().getHolder();
|
InventoryHolder inventoryHolder = e.getInventory().getHolder();
|
||||||
if (inventoryHolder == null || !(e.getWhoClicked() instanceof Player)) {
|
|
||||||
|
if (inventoryHolder == null || !(e.getWhoClicked() instanceof Player))
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (inventoryHolder instanceof Animals) {
|
|
||||||
checkIsland(e, player, e.getInventory().getLocation(), Flags.MOUNT_INVENTORY);
|
if (inventoryHolder instanceof Animals)
|
||||||
|
{
|
||||||
|
this.checkIsland(e, player, e.getInventory().getLocation(), Flags.MOUNT_INVENTORY);
|
||||||
}
|
}
|
||||||
else if (inventoryHolder instanceof Dispenser) {
|
else if (inventoryHolder instanceof Dispenser)
|
||||||
checkIsland(e, player, e.getInventory().getLocation(), Flags.DISPENSER);
|
{
|
||||||
|
this.checkIsland(e, player, e.getInventory().getLocation(), Flags.DISPENSER);
|
||||||
}
|
}
|
||||||
else if (inventoryHolder instanceof Dropper) {
|
else if (inventoryHolder instanceof Dropper)
|
||||||
checkIsland(e, player, e.getInventory().getLocation(), Flags.DROPPER);
|
{
|
||||||
|
this.checkIsland(e, player, e.getInventory().getLocation(), Flags.DROPPER);
|
||||||
}
|
}
|
||||||
else if (inventoryHolder instanceof Hopper
|
else if (inventoryHolder instanceof Hopper || inventoryHolder instanceof HopperMinecart)
|
||||||
|| inventoryHolder instanceof HopperMinecart) {
|
{
|
||||||
checkIsland(e, player, e.getInventory().getLocation(), Flags.HOPPER);
|
this.checkIsland(e, player, e.getInventory().getLocation(), Flags.HOPPER);
|
||||||
}
|
}
|
||||||
else if (inventoryHolder instanceof Furnace) {
|
else if (inventoryHolder instanceof Furnace)
|
||||||
checkIsland(e, player, e.getInventory().getLocation(), Flags.FURNACE);
|
{
|
||||||
|
this.checkIsland(e, player, e.getInventory().getLocation(), Flags.FURNACE);
|
||||||
}
|
}
|
||||||
else if (inventoryHolder instanceof BrewingStand) {
|
else if (inventoryHolder instanceof BrewingStand)
|
||||||
checkIsland(e, player, e.getInventory().getLocation(), Flags.BREWING);
|
{
|
||||||
|
this.checkIsland(e, player, e.getInventory().getLocation(), Flags.BREWING);
|
||||||
}
|
}
|
||||||
else if (inventoryHolder instanceof Beacon) {
|
else if (inventoryHolder instanceof Beacon)
|
||||||
checkIsland(e, player, e.getInventory().getLocation(), Flags.BEACON);
|
{
|
||||||
|
this.checkIsland(e, player, e.getInventory().getLocation(), Flags.BEACON);
|
||||||
}
|
}
|
||||||
else if (inventoryHolder instanceof NPC) {
|
else if (inventoryHolder instanceof NPC)
|
||||||
checkIsland(e, player, e.getInventory().getLocation(), Flags.TRADING);
|
{
|
||||||
|
this.checkIsland(e, player, e.getInventory().getLocation(), Flags.TRADING);
|
||||||
}
|
}
|
||||||
else if (inventoryHolder instanceof Barrel) {
|
else if (inventoryHolder instanceof Barrel)
|
||||||
checkIsland(e, player, e.getInventory().getLocation(), Flags.BARREL);
|
{
|
||||||
|
this.checkIsland(e, player, e.getInventory().getLocation(), Flags.BARREL);
|
||||||
}
|
}
|
||||||
else if (inventoryHolder instanceof ShulkerBox) {
|
else if (inventoryHolder instanceof ShulkerBox)
|
||||||
checkIsland(e, player, e.getInventory().getLocation(), Flags.SHULKER_BOX);
|
{
|
||||||
|
this.checkIsland(e, player, e.getInventory().getLocation(), Flags.SHULKER_BOX);
|
||||||
}
|
}
|
||||||
else if (inventoryHolder instanceof Chest c) {
|
else if (inventoryHolder instanceof Chest c)
|
||||||
checkInvHolder(c.getLocation(), e, player);
|
{
|
||||||
|
this.checkInvHolder(c.getLocation(), e, player);
|
||||||
}
|
}
|
||||||
else if (inventoryHolder instanceof DoubleChest dc) {
|
else if (inventoryHolder instanceof DoubleChest dc)
|
||||||
checkInvHolder(dc.getLocation(), e, player);
|
{
|
||||||
|
this.checkInvHolder(dc.getLocation(), e, player);
|
||||||
}
|
}
|
||||||
else if (inventoryHolder instanceof StorageMinecart) {
|
else if (inventoryHolder instanceof StorageMinecart)
|
||||||
checkIsland(e, player, e.getInventory().getLocation(), Flags.CHEST);
|
{
|
||||||
|
this.checkIsland(e, player, e.getInventory().getLocation(), Flags.CHEST);
|
||||||
}
|
}
|
||||||
else if (!(inventoryHolder instanceof Player)) {
|
else if (!ServerCompatibility.getInstance().isVersion(ServerCompatibility.ServerVersion.V1_18, ServerCompatibility.ServerVersion.V1_18_1, ServerCompatibility.ServerVersion.V1_18_2) &&
|
||||||
|
inventoryHolder instanceof ChestBoat)
|
||||||
|
{
|
||||||
|
// TODO: 1.19 added chest boat. Remove compatibility check when 1.18 is dropped.
|
||||||
|
this.checkIsland(e, player, e.getInventory().getLocation(), Flags.CHEST);
|
||||||
|
}
|
||||||
|
else if (!(inventoryHolder instanceof Player))
|
||||||
|
{
|
||||||
// All other containers
|
// All other containers
|
||||||
checkIsland(e, player, e.getInventory().getLocation(), Flags.CONTAINER);
|
this.checkIsland(e, player, e.getInventory().getLocation(), Flags.CONTAINER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkInvHolder(Location l, InventoryClickEvent e, Player player) {
|
|
||||||
if (l.getBlock().getType().equals(Material.TRAPPED_CHEST)) {
|
/**
|
||||||
checkIsland(e, player, l, Flags.TRAPPED_CHEST);
|
* This method runs check based on clicked chest type.
|
||||||
} else {
|
* @param l location of chest.
|
||||||
checkIsland(e, player, l, Flags.CHEST);
|
* @param e click event.
|
||||||
|
* @param player player who clicked.
|
||||||
|
*/
|
||||||
|
private void checkInvHolder(Location l, InventoryClickEvent e, Player player)
|
||||||
|
{
|
||||||
|
if (l.getBlock().getType().equals(Material.TRAPPED_CHEST))
|
||||||
|
{
|
||||||
|
this.checkIsland(e, player, l, Flags.TRAPPED_CHEST);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.checkIsland(e, player, l, Flags.CHEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -104,22 +104,31 @@ public class LockAndBanListener extends FlagListener {
|
|||||||
* @param loc - location to check
|
* @param loc - location to check
|
||||||
* @return CheckResult LOCKED, BANNED or OPEN. If an island is locked, that will take priority over banned
|
* @return CheckResult LOCKED, BANNED or OPEN. If an island is locked, that will take priority over banned
|
||||||
*/
|
*/
|
||||||
private CheckResult check(@NonNull Player player, Location loc) {
|
private CheckResult check(@NonNull Player player, Location loc)
|
||||||
|
{
|
||||||
// Ops or NPC's are allowed everywhere
|
// Ops or NPC's are allowed everywhere
|
||||||
if (player.isOp() || player.hasMetadata("NPC")) {
|
if (player.isOp() || player.hasMetadata("NPC"))
|
||||||
|
{
|
||||||
return CheckResult.OPEN;
|
return CheckResult.OPEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
// See if the island is locked to non-members or player is banned
|
// See if the island is locked to non-members or player is banned
|
||||||
return getIslands().getProtectedIslandAt(loc)
|
return this.getIslands().getProtectedIslandAt(loc).
|
||||||
.map(is -> {
|
map(is ->
|
||||||
if (is.isBanned(player.getUniqueId())) {
|
{
|
||||||
return player.hasPermission(getIWM().getPermissionPrefix(loc.getWorld()) + "mod.bypassban") ? CheckResult.OPEN : CheckResult.BANNED;
|
if (is.isBanned(player.getUniqueId()))
|
||||||
}
|
{
|
||||||
if (!is.isAllowed(User.getInstance(player), Flags.LOCK)) {
|
return player.hasPermission(getIWM().getPermissionPrefix(loc.getWorld()) + "mod.bypassban") ?
|
||||||
return player.hasPermission(getIWM().getPermissionPrefix(loc.getWorld()) + "mod.bypasslock") ? CheckResult.OPEN : CheckResult.LOCKED;
|
CheckResult.OPEN : CheckResult.BANNED;
|
||||||
}
|
}
|
||||||
return CheckResult.OPEN;
|
if (!is.isAllowed(User.getInstance(player), Flags.LOCK))
|
||||||
}).orElse(CheckResult.OPEN);
|
{
|
||||||
|
return player.hasPermission(getIWM().getPermissionPrefix(loc.getWorld()) + "mod.bypasslock") ?
|
||||||
|
CheckResult.OPEN : CheckResult.LOCKED;
|
||||||
|
}
|
||||||
|
return CheckResult.OPEN;
|
||||||
|
}).
|
||||||
|
orElse(CheckResult.OPEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -128,19 +137,17 @@ public class LockAndBanListener extends FlagListener {
|
|||||||
* @param loc - location to check
|
* @param loc - location to check
|
||||||
* @return true if banned
|
* @return true if banned
|
||||||
*/
|
*/
|
||||||
private CheckResult checkAndNotify(@NonNull Player player, Location loc) {
|
private CheckResult checkAndNotify(@NonNull Player player, Location loc)
|
||||||
CheckResult r = check(player,loc);
|
{
|
||||||
switch (r) {
|
CheckResult result = this.check(player, loc);
|
||||||
case BANNED:
|
|
||||||
User.getInstance(player).notify("commands.island.ban.you-are-banned");
|
switch (result)
|
||||||
break;
|
{
|
||||||
case LOCKED:
|
case BANNED -> User.getInstance(player).notify("commands.island.ban.you-are-banned");
|
||||||
User.getInstance(player).notify("protection.locked");
|
case LOCKED -> User.getInstance(player).notify("protection.locked");
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
return r;
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -13,67 +13,66 @@ import org.bukkit.event.player.PlayerInteractEvent;
|
|||||||
import world.bentobox.bentobox.api.flags.FlagListener;
|
import world.bentobox.bentobox.api.flags.FlagListener;
|
||||||
import world.bentobox.bentobox.lists.Flags;
|
import world.bentobox.bentobox.lists.Flags;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author tastybento
|
* @author tastybento
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class PhysicalInteractionListener extends FlagListener {
|
public class PhysicalInteractionListener extends FlagListener
|
||||||
|
{
|
||||||
/**
|
/**
|
||||||
* Handle physical interaction with blocks
|
* Handle physical interaction with blocks
|
||||||
* Crop trample, pressure plates, triggering redstone, tripwires
|
* Crop trample, pressure plates, triggering redstone, tripwires
|
||||||
* @param e - event
|
* @param e - event
|
||||||
*/
|
*/
|
||||||
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
||||||
public void onPlayerInteract(PlayerInteractEvent e) {
|
public void onPlayerInteract(PlayerInteractEvent e)
|
||||||
if (!e.getAction().equals(Action.PHYSICAL)) {
|
{
|
||||||
|
if (!e.getAction().equals(Action.PHYSICAL) || e.getClickedBlock() == null)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isPressurePlate(e.getClickedBlock().getType())) {
|
|
||||||
|
if (Tag.PRESSURE_PLATES.isTagged(e.getClickedBlock().getType()))
|
||||||
|
{
|
||||||
// Pressure plates
|
// Pressure plates
|
||||||
checkIsland(e, e.getPlayer(), e.getPlayer().getLocation(), Flags.PRESSURE_PLATE);
|
this.checkIsland(e, e.getPlayer(), e.getPlayer().getLocation(), Flags.PRESSURE_PLATE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switch (e.getClickedBlock().getType()) {
|
|
||||||
case FARMLAND:
|
switch (e.getClickedBlock().getType())
|
||||||
// Crop trample
|
{
|
||||||
checkIsland(e, e.getPlayer(), e.getPlayer().getLocation(), Flags.CROP_TRAMPLE);
|
case FARMLAND -> this.checkIsland(e, e.getPlayer(), e.getPlayer().getLocation(), Flags.CROP_TRAMPLE);
|
||||||
break;
|
case TURTLE_EGG -> this.checkIsland(e, e.getPlayer(), e.getPlayer().getLocation(), Flags.TURTLE_EGGS);
|
||||||
case TURTLE_EGG:
|
|
||||||
checkIsland(e, e.getPlayer(), e.getPlayer().getLocation(), Flags.TURTLE_EGGS);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Protects buttons and plates from being activated by projectiles
|
* Protects buttons and plates from being activated by projectiles
|
||||||
* @param e - event
|
* @param e - event
|
||||||
*/
|
*/
|
||||||
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
||||||
public void onProjectileHit(EntityInteractEvent e) {
|
public void onProjectileHit(EntityInteractEvent e)
|
||||||
if (!(e.getEntity() instanceof Projectile p)) {
|
{
|
||||||
|
if (!(e.getEntity() instanceof Projectile p))
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (p.getShooter() instanceof Player) {
|
|
||||||
if (Tag.WOODEN_BUTTONS.isTagged(e.getBlock().getType())) {
|
if (p.getShooter() instanceof Player)
|
||||||
checkIsland(e, (Player)p.getShooter(), e.getBlock().getLocation(), Flags.BUTTON);
|
{
|
||||||
|
if (Tag.WOODEN_BUTTONS.isTagged(e.getBlock().getType()))
|
||||||
|
{
|
||||||
|
this.checkIsland(e, (Player) p.getShooter(), e.getBlock().getLocation(), Flags.BUTTON);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPressurePlate(e.getBlock().getType())) {
|
if (Tag.PRESSURE_PLATES.isTagged(e.getBlock().getType()))
|
||||||
|
{
|
||||||
// Pressure plates
|
// Pressure plates
|
||||||
checkIsland(e, (Player)p.getShooter(), e.getBlock().getLocation(), Flags.PRESSURE_PLATE);
|
this.checkIsland(e, (Player) p.getShooter(), e.getBlock().getLocation(), Flags.PRESSURE_PLATE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isPressurePlate(Material material) {
|
|
||||||
return switch (material) {
|
|
||||||
case STONE_PRESSURE_PLATE, POLISHED_BLACKSTONE_PRESSURE_PLATE, ACACIA_PRESSURE_PLATE, BIRCH_PRESSURE_PLATE, CRIMSON_PRESSURE_PLATE, DARK_OAK_PRESSURE_PLATE, HEAVY_WEIGHTED_PRESSURE_PLATE, JUNGLE_PRESSURE_PLATE, LIGHT_WEIGHTED_PRESSURE_PLATE, OAK_PRESSURE_PLATE, SPRUCE_PRESSURE_PLATE, WARPED_PRESSURE_PLATE -> true;
|
|
||||||
default -> false;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,95 +18,126 @@ import world.bentobox.bentobox.lists.Flags;
|
|||||||
/**
|
/**
|
||||||
* @author tastybento
|
* @author tastybento
|
||||||
*/
|
*/
|
||||||
public class PlaceBlocksListener extends FlagListener {
|
public class PlaceBlocksListener extends FlagListener
|
||||||
|
{
|
||||||
/**
|
/**
|
||||||
* Check blocks being placed in general
|
* Check blocks being placed in general
|
||||||
*
|
*
|
||||||
* @param e - event
|
* @param e - event
|
||||||
*/
|
*/
|
||||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||||
public void onBlockPlace(final BlockPlaceEvent e) {
|
public void onBlockPlace(final BlockPlaceEvent e)
|
||||||
if (e.getBlock().getType().equals(Material.FIRE)
|
{
|
||||||
|| e.getItemInHand() == null // Note that this should never happen officially, but it's possible for other plugins to cause it to happen
|
if (e.getBlock().getType().equals(Material.FIRE) ||
|
||||||
|| e.getItemInHand().getType().equals(Material.WRITABLE_BOOK)
|
e.getItemInHand() == null || // Note that this should never happen officially, but it's possible for other plugins to cause it to happen
|
||||||
|| e.getItemInHand().getType().equals(Material.WRITTEN_BOOK)) {
|
e.getItemInHand().getType().equals(Material.WRITABLE_BOOK) ||
|
||||||
|
e.getItemInHand().getType().equals(Material.WRITTEN_BOOK))
|
||||||
|
{
|
||||||
// Books can only be placed on lecterns and as such are protected by the LECTERN flag.
|
// Books can only be placed on lecterns and as such are protected by the LECTERN flag.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
checkIsland(e, e.getPlayer(), e.getBlock().getLocation(), Flags.PLACE_BLOCKS);
|
|
||||||
|
this.checkIsland(e, e.getPlayer(), e.getBlock().getLocation(), Flags.PLACE_BLOCKS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check for paintings and other hanging placements
|
* Check for paintings and other hanging placements
|
||||||
|
*
|
||||||
* @param e - event
|
* @param e - event
|
||||||
*/
|
*/
|
||||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||||
public void onHangingPlace(final HangingPlaceEvent e) {
|
public void onHangingPlace(final HangingPlaceEvent e)
|
||||||
checkIsland(e, e.getPlayer(), e.getBlock().getLocation(), Flags.PLACE_BLOCKS);
|
{
|
||||||
|
this.checkIsland(e, e.getPlayer(), e.getBlock().getLocation(), Flags.PLACE_BLOCKS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles placing items into ItemFrames
|
* Handles placing items into ItemFrames
|
||||||
|
*
|
||||||
* @param e - event
|
* @param e - event
|
||||||
*/
|
*/
|
||||||
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
||||||
public void onPlayerHitItemFrame(PlayerInteractEntityEvent e) {
|
public void onPlayerHitItemFrame(PlayerInteractEntityEvent e)
|
||||||
if (e.getRightClicked().getType().equals(EntityType.ITEM_FRAME)) {
|
{
|
||||||
if (!checkIsland(e, e.getPlayer(), e.getRightClicked().getLocation(), Flags.PLACE_BLOCKS)) return;
|
if (e.getRightClicked().getType().equals(EntityType.ITEM_FRAME) ||
|
||||||
checkIsland(e, e.getPlayer(), e.getRightClicked().getLocation(), Flags.ITEM_FRAME);
|
e.getRightClicked().getType().equals(EntityType.GLOW_ITEM_FRAME))
|
||||||
|
{
|
||||||
|
if (!this.checkIsland(e, e.getPlayer(), e.getRightClicked().getLocation(), Flags.PLACE_BLOCKS))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.checkIsland(e, e.getPlayer(), e.getRightClicked().getLocation(), Flags.ITEM_FRAME);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle placing of fireworks, item frames, mine carts, end crystals, chests and boats on land
|
* Handle placing of fireworks, item frames, mine carts, end crystals, chests and boats on land The doors and chests
|
||||||
* The doors and chests are related to an exploit.
|
* are related to an exploit.
|
||||||
|
*
|
||||||
* @param e - event
|
* @param e - event
|
||||||
*/
|
*/
|
||||||
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
||||||
public void onPlayerInteract(final PlayerInteractEvent e) {
|
public void onPlayerInteract(final PlayerInteractEvent e)
|
||||||
if (!e.getAction().equals(Action.RIGHT_CLICK_BLOCK) || e.getClickedBlock() == null) {
|
{
|
||||||
|
if (!e.getAction().equals(Action.RIGHT_CLICK_BLOCK) || e.getClickedBlock() == null)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (e.getClickedBlock().getType()) {
|
switch (e.getClickedBlock().getType())
|
||||||
case FIREWORK_ROCKET:
|
{
|
||||||
checkIsland(e, e.getPlayer(), e.getClickedBlock().getLocation(), Flags.PLACE_BLOCKS);
|
case FIREWORK_ROCKET ->
|
||||||
return;
|
{
|
||||||
case RAIL:
|
this.checkIsland(e, e.getPlayer(), e.getClickedBlock().getLocation(), Flags.PLACE_BLOCKS);
|
||||||
case POWERED_RAIL:
|
|
||||||
case DETECTOR_RAIL:
|
|
||||||
case ACTIVATOR_RAIL:
|
|
||||||
if ((e.getMaterial() == Material.MINECART || e.getMaterial() == Material.CHEST_MINECART || e.getMaterial() == Material.HOPPER_MINECART
|
|
||||||
|| e.getMaterial() == Material.TNT_MINECART || e.getMaterial() == Material.FURNACE_MINECART)) {
|
|
||||||
checkIsland(e, e.getPlayer(), e.getClickedBlock().getLocation(), Flags.MINECART);
|
|
||||||
}
|
}
|
||||||
return;
|
case RAIL, POWERED_RAIL, DETECTOR_RAIL, ACTIVATOR_RAIL ->
|
||||||
default:
|
{
|
||||||
// Check in-hand items
|
if (e.getMaterial() == Material.MINECART ||
|
||||||
if (e.getMaterial().equals(Material.FIREWORK_ROCKET)
|
e.getMaterial() == Material.CHEST_MINECART ||
|
||||||
|| e.getMaterial().equals(Material.ARMOR_STAND)
|
e.getMaterial() == Material.HOPPER_MINECART ||
|
||||||
|| e.getMaterial().equals(Material.END_CRYSTAL)
|
e.getMaterial() == Material.TNT_MINECART ||
|
||||||
|| e.getMaterial().equals(Material.ITEM_FRAME)
|
e.getMaterial() == Material.FURNACE_MINECART)
|
||||||
//|| Tag.DOORS.isTagged(e.getMaterial())
|
{
|
||||||
|| e.getMaterial().equals(Material.CHEST)
|
this.checkIsland(e, e.getPlayer(), e.getClickedBlock().getLocation(), Flags.MINECART);
|
||||||
|| e.getMaterial().equals(Material.TRAPPED_CHEST)) {
|
}
|
||||||
checkIsland(e, e.getPlayer(), e.getPlayer().getLocation(), Flags.PLACE_BLOCKS);
|
|
||||||
}
|
}
|
||||||
else if (e.getMaterial().name().contains("BOAT")) {
|
default ->
|
||||||
checkIsland(e, e.getPlayer(), e.getPlayer().getLocation(), Flags.BOAT);
|
{
|
||||||
|
// Check in-hand items
|
||||||
|
if (e.getMaterial() == Material.FIREWORK_ROCKET ||
|
||||||
|
e.getMaterial() == Material.ARMOR_STAND ||
|
||||||
|
e.getMaterial() == Material.END_CRYSTAL ||
|
||||||
|
e.getMaterial() == Material.ITEM_FRAME ||
|
||||||
|
e.getMaterial() == Material.GLOW_ITEM_FRAME ||
|
||||||
|
e.getMaterial() == Material.CHEST ||
|
||||||
|
e.getMaterial() == Material.TRAPPED_CHEST)
|
||||||
|
{
|
||||||
|
this.checkIsland(e, e.getPlayer(), e.getPlayer().getLocation(), Flags.PLACE_BLOCKS);
|
||||||
|
}
|
||||||
|
else if (e.getMaterial().name().contains("BOAT"))
|
||||||
|
{
|
||||||
|
this.checkIsland(e, e.getPlayer(), e.getPlayer().getLocation(), Flags.BOAT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles Frost Walking on visitor's islands. This creates ice blocks, which is like placing blocks
|
* Handles Frost Walking on visitor's islands. This creates ice blocks, which is like placing blocks
|
||||||
|
*
|
||||||
* @param e - event
|
* @param e - event
|
||||||
*/
|
*/
|
||||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled=true)
|
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||||
public void onBlockForm(EntityBlockFormEvent e) {
|
public void onBlockForm(EntityBlockFormEvent e)
|
||||||
if (e.getNewState().getType().equals(Material.FROSTED_ICE) && e.getEntity() instanceof Player) {
|
{
|
||||||
checkIsland(e, (Player)e.getEntity(), e.getBlock().getLocation(), Flags.FROST_WALKER);
|
if (e.getNewState().getType().equals(Material.FROSTED_ICE) && e.getEntity() instanceof Player)
|
||||||
|
{
|
||||||
|
this.checkIsland(e, (Player) e.getEntity(), e.getBlock().getLocation(), Flags.FROST_WALKER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,53 @@
|
|||||||
|
//
|
||||||
|
// Created by BONNe
|
||||||
|
// Copyright - 2022
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
package world.bentobox.bentobox.listeners.flags.protection;
|
||||||
|
|
||||||
|
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
|
import org.bukkit.event.block.BlockReceiveGameEvent;
|
||||||
|
|
||||||
|
import world.bentobox.bentobox.api.flags.FlagListener;
|
||||||
|
import world.bentobox.bentobox.lists.Flags;
|
||||||
|
import world.bentobox.bentobox.versions.ServerCompatibility;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method prevents sculk sensor from activation based on protection settings.
|
||||||
|
*/
|
||||||
|
public class SculkSensorListener extends FlagListener
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* This listener detects if a visitor activates sculk sensor, and block it, if required.
|
||||||
|
* @param event Sculk activation event.
|
||||||
|
*/
|
||||||
|
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||||
|
public void onSculkSensor(BlockReceiveGameEvent event)
|
||||||
|
{
|
||||||
|
if (!this.getIWM().inWorld(event.getBlock().getWorld()))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ServerCompatibility.getInstance().isVersion(ServerCompatibility.ServerVersion.V1_18,
|
||||||
|
ServerCompatibility.ServerVersion.V1_18_1,
|
||||||
|
ServerCompatibility.ServerVersion.V1_18_2))
|
||||||
|
{
|
||||||
|
// TODO: 1.18 compatibility exit
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.getBlock().getType() == Material.SCULK_SENSOR &&
|
||||||
|
event.getEntity() != null &&
|
||||||
|
event.getEntity() instanceof Player player)
|
||||||
|
{
|
||||||
|
this.checkIsland(event, player, event.getBlock().getLocation(), Flags.SCULK_SENSOR, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
//
|
||||||
|
// Created by BONNe
|
||||||
|
// Copyright - 2022
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
package world.bentobox.bentobox.listeners.flags.protection;
|
||||||
|
|
||||||
|
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
|
import org.bukkit.event.block.BlockReceiveGameEvent;
|
||||||
|
|
||||||
|
import world.bentobox.bentobox.api.flags.FlagListener;
|
||||||
|
import world.bentobox.bentobox.lists.Flags;
|
||||||
|
import world.bentobox.bentobox.versions.ServerCompatibility;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method prevents sculk shrieker from activation based on protection settings.
|
||||||
|
*/
|
||||||
|
public class SculkShriekerListener extends FlagListener
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* This listener detects if a visitor activates sculk sensor, and block it, if required.
|
||||||
|
* @param event Sculk activation event.
|
||||||
|
*/
|
||||||
|
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||||
|
public void onSculkShrieker(BlockReceiveGameEvent event)
|
||||||
|
{
|
||||||
|
if (!this.getIWM().inWorld(event.getBlock().getWorld()))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ServerCompatibility.getInstance().isVersion(ServerCompatibility.ServerVersion.V1_18,
|
||||||
|
ServerCompatibility.ServerVersion.V1_18_1,
|
||||||
|
ServerCompatibility.ServerVersion.V1_18_2))
|
||||||
|
{
|
||||||
|
// TODO: 1.18 compatibility exit
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.getBlock().getType() == Material.SCULK_SHRIEKER &&
|
||||||
|
event.getEntity() != null &&
|
||||||
|
event.getEntity() instanceof Player player)
|
||||||
|
{
|
||||||
|
this.checkIsland(event, player, event.getBlock().getLocation(), Flags.SCULK_SHRIEKER, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,11 +3,13 @@ package world.bentobox.bentobox.listeners.flags.settings;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.*;
|
||||||
import org.bukkit.entity.PufferFish;
|
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.EventPriority;
|
import org.bukkit.event.EventPriority;
|
||||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||||
|
import org.bukkit.event.raid.RaidFinishEvent;
|
||||||
|
import org.bukkit.event.raid.RaidTriggerEvent;
|
||||||
|
import org.bukkit.potion.PotionEffectType;
|
||||||
|
|
||||||
import world.bentobox.bentobox.api.flags.Flag;
|
import world.bentobox.bentobox.api.flags.Flag;
|
||||||
import world.bentobox.bentobox.api.flags.FlagListener;
|
import world.bentobox.bentobox.api.flags.FlagListener;
|
||||||
@ -19,68 +21,136 @@ import world.bentobox.bentobox.util.Util;
|
|||||||
* Handles natural mob spawning.
|
* Handles natural mob spawning.
|
||||||
* @author tastybento
|
* @author tastybento
|
||||||
*/
|
*/
|
||||||
public class MobSpawnListener extends FlagListener {
|
public class MobSpawnListener extends FlagListener
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Prevents mobs spawning naturally
|
||||||
|
*
|
||||||
|
* @param e - event
|
||||||
|
*/
|
||||||
|
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||||
|
public void onMobSpawnEvent(CreatureSpawnEvent e)
|
||||||
|
{
|
||||||
|
this.onMobSpawn(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This prevents to start a raid if mob spawning rules prevents it.
|
||||||
|
* @param event RaidTriggerEvent
|
||||||
|
*/
|
||||||
|
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||||
|
public void onRaidStartEvent(RaidTriggerEvent event)
|
||||||
|
{
|
||||||
|
// If not in the right world exit immediately.
|
||||||
|
if (!this.getIWM().inWorld(event.getWorld()))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<Island> island = getIslands().getIslandAt(event.getPlayer().getLocation());
|
||||||
|
|
||||||
|
if (island.map(i -> !i.isAllowed(Flags.MONSTER_NATURAL_SPAWN)).orElseGet(
|
||||||
|
() -> !Flags.MONSTER_NATURAL_SPAWN.isSetForWorld(event.getWorld())))
|
||||||
|
{
|
||||||
|
// Monster spawning is disabled on island or world. Cancel the raid.
|
||||||
|
event.setCancelled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This removes HERO_OF_THE_VILLAGE from players that cheated victory.
|
||||||
|
* @param event RaidFinishEvent
|
||||||
|
*/
|
||||||
|
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||||
|
public void onRaidFinishEvent(RaidFinishEvent event)
|
||||||
|
{
|
||||||
|
// If not in the right world exit immediately.
|
||||||
|
if (!this.getIWM().inWorld(event.getWorld()))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<Island> island = getIslands().getIslandAt(event.getRaid().getLocation());
|
||||||
|
|
||||||
|
if (island.map(i -> !i.isAllowed(Flags.MONSTER_NATURAL_SPAWN)).orElseGet(
|
||||||
|
() -> !Flags.MONSTER_NATURAL_SPAWN.isSetForWorld(event.getWorld())))
|
||||||
|
{
|
||||||
|
// CHEATERS. PUNISH THEM.
|
||||||
|
event.getWinners().forEach(player ->
|
||||||
|
{
|
||||||
|
if (player.isOnline())
|
||||||
|
{
|
||||||
|
player.removePotionEffect(PotionEffectType.HERO_OF_THE_VILLAGE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prevents mobs spawning naturally
|
* Prevents mobs spawning naturally
|
||||||
*
|
|
||||||
* @param e - event
|
* @param e - event
|
||||||
*/
|
*/
|
||||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
void onMobSpawn(CreatureSpawnEvent e)
|
||||||
public void onMobSpawnEvent(CreatureSpawnEvent e) {
|
{
|
||||||
onMobSpawn(e);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Prevents mobs spawning naturally
|
|
||||||
*
|
|
||||||
* @param e - event
|
|
||||||
* @return true if cancelled
|
|
||||||
*/
|
|
||||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
|
||||||
void onMobSpawn(CreatureSpawnEvent e) {
|
|
||||||
// If not in the right world, or spawning is not natural return
|
// If not in the right world, or spawning is not natural return
|
||||||
if (!getIWM().inWorld(e.getEntity().getLocation())) {
|
if (!this.getIWM().inWorld(e.getEntity().getLocation()))
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switch (e.getSpawnReason()) {
|
|
||||||
// Natural
|
switch (e.getSpawnReason())
|
||||||
case DEFAULT:
|
{
|
||||||
case DROWNED:
|
// Natural
|
||||||
case JOCKEY:
|
case DEFAULT, DROWNED, JOCKEY, LIGHTNING, MOUNT, NATURAL, NETHER_PORTAL, OCELOT_BABY, PATROL,
|
||||||
case LIGHTNING:
|
RAID, REINFORCEMENTS, SILVERFISH_BLOCK, TRAP, VILLAGE_DEFENSE, VILLAGE_INVASION ->
|
||||||
case MOUNT:
|
{
|
||||||
case NATURAL:
|
boolean cancelNatural = this.shouldCancel(e.getEntity(),
|
||||||
case NETHER_PORTAL:
|
e.getLocation(),
|
||||||
case OCELOT_BABY:
|
Flags.ANIMAL_NATURAL_SPAWN,
|
||||||
case PATROL:
|
Flags.MONSTER_NATURAL_SPAWN);
|
||||||
case RAID:
|
e.setCancelled(cancelNatural);
|
||||||
case REINFORCEMENTS:
|
}
|
||||||
case SILVERFISH_BLOCK:
|
|
||||||
//case SLIME_SPLIT: messes with slimes from spawners, slime must have previously existed to create another
|
|
||||||
case TRAP:
|
|
||||||
case VILLAGE_DEFENSE:
|
|
||||||
case VILLAGE_INVASION:
|
|
||||||
boolean cancelNatural = shouldCancel(e.getEntity(), e.getLocation(), Flags.ANIMAL_NATURAL_SPAWN, Flags.MONSTER_NATURAL_SPAWN);
|
|
||||||
e.setCancelled(cancelNatural);
|
|
||||||
return;
|
|
||||||
// Spawners
|
// Spawners
|
||||||
case SPAWNER:
|
case SPAWNER ->
|
||||||
boolean cancelSpawners = shouldCancel(e.getEntity(), e.getLocation(), Flags.ANIMAL_SPAWNERS_SPAWN, Flags.MONSTER_SPAWNERS_SPAWN);
|
{
|
||||||
e.setCancelled(cancelSpawners);
|
boolean cancelSpawners = this.shouldCancel(e.getEntity(),
|
||||||
return;
|
e.getLocation(),
|
||||||
default:
|
Flags.ANIMAL_SPAWNERS_SPAWN,
|
||||||
return;
|
Flags.MONSTER_SPAWNERS_SPAWN);
|
||||||
|
e.setCancelled(cancelSpawners);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean shouldCancel(Entity entity, Location loc, Flag animalSpawnFlag, Flag monsterSpawnFlag) {
|
|
||||||
|
/**
|
||||||
|
* This method checks if entity should be cancelled from spawning in given location base on flag values.
|
||||||
|
* @param entity Entity that is checked.
|
||||||
|
* @param loc location where entity is spawned.
|
||||||
|
* @param animalSpawnFlag Animal Spawn Flag.
|
||||||
|
* @param monsterSpawnFlag Monster Spawn Flag.
|
||||||
|
* @return {@code true} if flag prevents entity to spawn, {@code false} otherwise.
|
||||||
|
*/
|
||||||
|
private boolean shouldCancel(Entity entity, Location loc, Flag animalSpawnFlag, Flag monsterSpawnFlag)
|
||||||
|
{
|
||||||
Optional<Island> island = getIslands().getIslandAt(loc);
|
Optional<Island> island = getIslands().getIslandAt(loc);
|
||||||
if (Util.isHostileEntity(entity) && !(entity instanceof PufferFish)) {
|
|
||||||
return island.map(i -> !i.isAllowed(monsterSpawnFlag)).orElseGet(() -> !monsterSpawnFlag.isSetForWorld(entity.getWorld()));
|
|
||||||
} else if (Util.isPassiveEntity(entity) || entity instanceof PufferFish) {
|
|
||||||
return island.map(i -> !i.isAllowed(animalSpawnFlag)).orElseGet(() -> !animalSpawnFlag.isSetForWorld(entity.getWorld()));
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (Util.isHostileEntity(entity) && !(entity instanceof PufferFish))
|
||||||
|
{
|
||||||
|
return island.map(i -> !i.isAllowed(monsterSpawnFlag)).
|
||||||
|
orElseGet(() -> !monsterSpawnFlag.isSetForWorld(entity.getWorld()));
|
||||||
|
}
|
||||||
|
else if (Util.isPassiveEntity(entity) || entity instanceof PufferFish)
|
||||||
|
{
|
||||||
|
return island.map(i -> !i.isAllowed(animalSpawnFlag)).
|
||||||
|
orElseGet(() -> !animalSpawnFlag.isSetForWorld(entity.getWorld()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package world.bentobox.bentobox.listeners.flags.worldsettings;
|
package world.bentobox.bentobox.listeners.flags.worldsettings;
|
||||||
|
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.Tag;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.EventPriority;
|
import org.bukkit.event.EventPriority;
|
||||||
import org.bukkit.event.entity.EntityExplodeEvent;
|
import org.bukkit.event.entity.EntityExplodeEvent;
|
||||||
@ -18,12 +19,13 @@ public class ChestDamageListener extends FlagListener {
|
|||||||
* @param e - event
|
* @param e - event
|
||||||
*/
|
*/
|
||||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||||
public void onExplosion(final EntityExplodeEvent e) {
|
public void onExplosion(final EntityExplodeEvent e)
|
||||||
if (getIWM().inWorld(e.getLocation()) && !Flags.CHEST_DAMAGE.isSetForWorld(e.getLocation().getWorld())) {
|
{
|
||||||
e.blockList().removeIf(b -> b.getType().equals(Material.CHEST)
|
if (getIWM().inWorld(e.getLocation()) && !Flags.CHEST_DAMAGE.isSetForWorld(e.getLocation().getWorld()))
|
||||||
|| b.getType().equals(Material.TRAPPED_CHEST)
|
{
|
||||||
|| b.getType().name().contains("SHULKER_BOX")
|
e.blockList().removeIf(b -> b.getType().equals(Material.CHEST) ||
|
||||||
);
|
b.getType().equals(Material.TRAPPED_CHEST) ||
|
||||||
|
Tag.SHULKER_BOXES.isTagged(b.getType()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,28 @@ public class CoarseDirtTillingListener extends FlagListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protect Coarse dirt and podzol from being made as dirt path block.
|
||||||
|
* @param e PlayerInteractEvent
|
||||||
|
*/
|
||||||
|
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||||
|
public void onDirtPathCreation(PlayerInteractEvent e)
|
||||||
|
{
|
||||||
|
if (e.getAction().equals(Action.RIGHT_CLICK_BLOCK) &&
|
||||||
|
e.getItem() != null &&
|
||||||
|
this.getIWM().inWorld(e.getClickedBlock().getWorld()) &&
|
||||||
|
(e.getClickedBlock().getType().equals(Material.COARSE_DIRT) || e.getClickedBlock().getType().equals(Material.PODZOL)) &&
|
||||||
|
e.getItem().getType().name().endsWith("_SHOVEL") &&
|
||||||
|
!Flags.COARSE_DIRT_TILLING.isSetForWorld(e.getClickedBlock().getWorld()))
|
||||||
|
{
|
||||||
|
e.setCancelled(true);
|
||||||
|
User user = User.getInstance(e.getPlayer());
|
||||||
|
user.notify("protection.protected", TextVariables.DESCRIPTION, user.getTranslation(Flags.COARSE_DIRT_TILLING.getHintReference()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If podzol is mined when coarse dirt tilling is not allowed, then it'll just drop podzol and not dirt
|
* If podzol is mined when coarse dirt tilling is not allowed, then it'll just drop podzol and not dirt
|
||||||
* This prevents an exploit where growing big spruce trees can turn gravel into podzol.
|
* This prevents an exploit where growing big spruce trees can turn gravel into podzol.
|
||||||
|
@ -12,6 +12,8 @@ import org.bukkit.event.EventHandler;
|
|||||||
import org.bukkit.event.EventPriority;
|
import org.bukkit.event.EventPriority;
|
||||||
import org.bukkit.event.entity.EntityDamageEvent;
|
import org.bukkit.event.entity.EntityDamageEvent;
|
||||||
import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
|
import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
|
||||||
|
import org.bukkit.event.entity.EntityTargetEvent;
|
||||||
|
import org.bukkit.event.entity.EntityTargetLivingEntityEvent;
|
||||||
import org.bukkit.event.inventory.ClickType;
|
import org.bukkit.event.inventory.ClickType;
|
||||||
|
|
||||||
import world.bentobox.bentobox.BentoBox;
|
import world.bentobox.bentobox.BentoBox;
|
||||||
@ -157,5 +159,28 @@ public class InvincibleVisitorsListener extends FlagListener implements ClickHan
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This listener cancels entity targeting a player if he is a visitor, and visitors are immune to entity damage.
|
||||||
|
* @param e EntityTargetLivingEntityEvent
|
||||||
|
*/
|
||||||
|
@EventHandler(priority = EventPriority.HIGHEST)
|
||||||
|
public void onVisitorTargeting(EntityTargetLivingEntityEvent e)
|
||||||
|
{
|
||||||
|
World world = e.getEntity().getWorld();
|
||||||
|
|
||||||
|
if (!(e.getTarget() instanceof Player p) ||
|
||||||
|
!this.getIWM().inWorld(world) ||
|
||||||
|
e.getTarget().hasMetadata("NPC") ||
|
||||||
|
this.getIslands().userIsOnIsland(world, User.getInstance(e.getTarget())) ||
|
||||||
|
this.PVPAllowed(p.getLocation()) ||
|
||||||
|
e.getReason() == EntityTargetEvent.TargetReason.TARGET_DIED ||
|
||||||
|
!this.getIWM().getIvSettings(world).contains(DamageCause.ENTITY_ATTACK.name()))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cancel targeting event.
|
||||||
|
e.setCancelled(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,9 +2,12 @@ package world.bentobox.bentobox.listeners.flags.worldsettings;
|
|||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.Material;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.EventPriority;
|
import org.bukkit.event.EventPriority;
|
||||||
|
import org.bukkit.event.block.BlockDispenseEvent;
|
||||||
import org.bukkit.event.block.BlockFromToEvent;
|
import org.bukkit.event.block.BlockFromToEvent;
|
||||||
|
|
||||||
import world.bentobox.bentobox.api.flags.FlagListener;
|
import world.bentobox.bentobox.api.flags.FlagListener;
|
||||||
@ -43,4 +46,38 @@ public class LiquidsFlowingOutListener extends FlagListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevents players from dispensing water, lava and powdered snow from dispenser outside island
|
||||||
|
* if Flags.LIQUIDS_FLOWING_OUT is disabled.
|
||||||
|
* @param event BlockDispenseEvent
|
||||||
|
*/
|
||||||
|
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
||||||
|
public void onDispenserLiquid(BlockDispenseEvent event)
|
||||||
|
{
|
||||||
|
Location from = event.getBlock().getLocation();
|
||||||
|
|
||||||
|
if (!this.getIWM().inWorld(from) || Flags.LIQUIDS_FLOWING_OUT.isSetForWorld(from.getWorld())) {
|
||||||
|
// We do not want to run any check if this is not the right world or if it is allowed.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Location to = event.getVelocity().toLocation(event.getBlock().getWorld());
|
||||||
|
|
||||||
|
if (!event.getItem().getType().equals(Material.WATER_BUCKET) &&
|
||||||
|
!event.getItem().getType().equals(Material.LAVA_BUCKET) &&
|
||||||
|
!event.getItem().getType().equals(Material.POWDER_SNOW_BUCKET))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only prevent if it is flowing into the area between islands or into another island.
|
||||||
|
Optional<Island> fromIsland = this.getIslandsManager().getProtectedIslandAt(from);
|
||||||
|
Optional<Island> toIsland = this.getIslandsManager().getProtectedIslandAt(to);
|
||||||
|
|
||||||
|
if (toIsland.isEmpty() || (fromIsland.isPresent() && !fromIsland.equals(toIsland)))
|
||||||
|
{
|
||||||
|
event.setCancelled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,61 @@
|
|||||||
|
//
|
||||||
|
// Created by BONNe
|
||||||
|
// Copyright - 2022
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
package world.bentobox.bentobox.listeners.flags.worldsettings;
|
||||||
|
|
||||||
|
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
|
import org.bukkit.event.raid.RaidTriggerEvent;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import world.bentobox.bentobox.api.flags.FlagListener;
|
||||||
|
import world.bentobox.bentobox.api.user.User;
|
||||||
|
import world.bentobox.bentobox.database.objects.Island;
|
||||||
|
import world.bentobox.bentobox.lists.Flags;
|
||||||
|
import world.bentobox.bentobox.util.Util;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This listener checks for island visitors that want to start a new raid.
|
||||||
|
*/
|
||||||
|
public class VisitorsStartingRaidListener extends FlagListener
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* This method process raid allowance from visitors.
|
||||||
|
* @param event RaidTriggerEvent
|
||||||
|
*/
|
||||||
|
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||||
|
public void onRaidTrigger(RaidTriggerEvent event)
|
||||||
|
{
|
||||||
|
World world = Util.getWorld(event.getWorld());
|
||||||
|
|
||||||
|
if (!this.getIWM().inWorld(world) || Flags.VISITOR_TRIGGER_RAID.isSetForWorld(world))
|
||||||
|
{
|
||||||
|
// If the player triggers raid non-protected world or VISITOR_TRIGGER_RAID is disabled then do nothing.
|
||||||
|
this.report(User.getInstance(event.getPlayer()),
|
||||||
|
event,
|
||||||
|
event.getPlayer().getLocation(),
|
||||||
|
Flags.VISITOR_TRIGGER_RAID,
|
||||||
|
Why.SETTING_ALLOWED_IN_WORLD);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<Island> island = this.getIslands().getProtectedIslandAt(event.getPlayer().getLocation());
|
||||||
|
|
||||||
|
if (island.isPresent() && !island.get().getMemberSet().contains(event.getPlayer().getUniqueId()))
|
||||||
|
{
|
||||||
|
event.setCancelled(true);
|
||||||
|
this.report(User.getInstance(event.getPlayer()),
|
||||||
|
event,
|
||||||
|
event.getPlayer().getLocation(),
|
||||||
|
Flags.VISITOR_TRIGGER_RAID,
|
||||||
|
Why.SETTING_NOT_ALLOWED_IN_WORLD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,344 @@
|
|||||||
|
//
|
||||||
|
// Created by BONNe
|
||||||
|
// Copyright - 2022
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
package world.bentobox.bentobox.listeners.teleports;
|
||||||
|
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.eclipse.jdt.annotation.NonNull;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import world.bentobox.bentobox.BentoBox;
|
||||||
|
import world.bentobox.bentobox.api.addons.GameModeAddon;
|
||||||
|
import world.bentobox.bentobox.database.objects.Island;
|
||||||
|
import world.bentobox.bentobox.util.Util;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This abstract class contains all common methods for entity and player teleportation.
|
||||||
|
*/
|
||||||
|
public abstract class AbstractTeleportListener
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Instance of Teleportation processor.
|
||||||
|
* @param bentoBox BentoBox plugin.
|
||||||
|
*/
|
||||||
|
AbstractTeleportListener(@NonNull BentoBox bentoBox)
|
||||||
|
{
|
||||||
|
this.plugin = bentoBox;
|
||||||
|
this.inPortal = new HashSet<>();
|
||||||
|
this.inTeleport = new HashSet<>();
|
||||||
|
this.teleportOrigin = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
// Section: Methods
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get island at the given location
|
||||||
|
* @return optional island at given location
|
||||||
|
*/
|
||||||
|
protected Optional<Island> getIsland(Location location)
|
||||||
|
{
|
||||||
|
return this.plugin.getIslandsManager().getProtectedIslandAt(location);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get island for given player at the given world.
|
||||||
|
* @return optional island at given world.
|
||||||
|
*/
|
||||||
|
protected Optional<Island> getIsland(World world, Player player)
|
||||||
|
{
|
||||||
|
return Optional.ofNullable(this.plugin.getIslandsManager().getIsland(world, player.getUniqueId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if vanilla portals should be used
|
||||||
|
*
|
||||||
|
* @param world - game mode world
|
||||||
|
* @param environment - environment
|
||||||
|
* @return true or false
|
||||||
|
*/
|
||||||
|
protected boolean isMakePortals(World world, World.Environment environment)
|
||||||
|
{
|
||||||
|
return this.plugin.getIWM().getAddon(world).
|
||||||
|
map(gameMode -> this.isMakePortals(gameMode, environment)).
|
||||||
|
orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if vanilla portals should be used
|
||||||
|
*
|
||||||
|
* @param gameMode - game mode
|
||||||
|
* @param environment - environment
|
||||||
|
* @return true or false
|
||||||
|
*/
|
||||||
|
protected boolean isMakePortals(GameModeAddon gameMode, World.Environment environment)
|
||||||
|
{
|
||||||
|
return switch (environment) {
|
||||||
|
case NETHER -> gameMode.getWorldSettings().isMakeNetherPortals();
|
||||||
|
case THE_END -> gameMode.getWorldSettings().isMakeEndPortals();
|
||||||
|
default -> false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if nether or end are generated
|
||||||
|
*
|
||||||
|
* @param overWorld - game world
|
||||||
|
* @param environment - environment
|
||||||
|
* @return true or false
|
||||||
|
*/
|
||||||
|
protected boolean isAllowedInConfig(World overWorld, World.Environment environment)
|
||||||
|
{
|
||||||
|
return switch (environment) {
|
||||||
|
case NETHER -> this.plugin.getIWM().isNetherGenerate(overWorld);
|
||||||
|
case THE_END -> this.plugin.getIWM().isEndGenerate(overWorld);
|
||||||
|
default -> true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the default nether or end are allowed by the server settings
|
||||||
|
*
|
||||||
|
* @param environment - environment
|
||||||
|
* @return true or false
|
||||||
|
*/
|
||||||
|
protected boolean isAllowedOnServer(World.Environment environment)
|
||||||
|
{
|
||||||
|
return switch (environment) {
|
||||||
|
case NETHER -> Bukkit.getAllowNether();
|
||||||
|
case THE_END -> Bukkit.getAllowEnd();
|
||||||
|
default -> true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if nether or end islands are generated
|
||||||
|
*
|
||||||
|
* @param overWorld - over world
|
||||||
|
* @param environment - environment
|
||||||
|
* @return true or false
|
||||||
|
*/
|
||||||
|
protected boolean isIslandWorld(World overWorld, World.Environment environment)
|
||||||
|
{
|
||||||
|
return switch (environment) {
|
||||||
|
case NETHER -> this.plugin.getIWM().isNetherIslands(overWorld);
|
||||||
|
case THE_END -> this.plugin.getIWM().isEndIslands(overWorld);
|
||||||
|
default -> true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the nether or end world
|
||||||
|
*
|
||||||
|
* @param overWorld - over world
|
||||||
|
* @param environment - environment
|
||||||
|
* @return nether or end world
|
||||||
|
*/
|
||||||
|
protected World getNetherEndWorld(World overWorld, World.Environment environment)
|
||||||
|
{
|
||||||
|
return switch (environment) {
|
||||||
|
case NETHER -> this.plugin.getIWM().getNetherWorld(overWorld);
|
||||||
|
case THE_END -> this.plugin.getIWM().getEndWorld(overWorld);
|
||||||
|
default -> Util.getWorld(overWorld);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the island has a nether or end island already
|
||||||
|
*
|
||||||
|
* @param island - island
|
||||||
|
* @param environment - environment
|
||||||
|
* @return true or false
|
||||||
|
*/
|
||||||
|
protected boolean hasPartnerIsland(Island island, World.Environment environment)
|
||||||
|
{
|
||||||
|
return switch (environment) {
|
||||||
|
case NETHER -> island.hasNetherIsland();
|
||||||
|
case THE_END -> island.hasEndIsland();
|
||||||
|
default -> true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method calculates the maximal search area for portal.
|
||||||
|
* @param location Location from which search should happen.
|
||||||
|
* @param island Island that contains the search point.
|
||||||
|
* @return Search range for portal.
|
||||||
|
*/
|
||||||
|
protected int calculateSearchRadius(Location location, Island island)
|
||||||
|
{
|
||||||
|
int diff;
|
||||||
|
|
||||||
|
if (island.onIsland(location))
|
||||||
|
{
|
||||||
|
// Find max x or max z
|
||||||
|
int x = Math.abs(island.getProtectionCenter().getBlockX() - location.getBlockX());
|
||||||
|
int z = Math.abs(island.getProtectionCenter().getBlockZ() - location.getBlockZ());
|
||||||
|
|
||||||
|
diff = Math.min(this.plugin.getSettings().getSafeSpotSearchRange(),
|
||||||
|
island.getProtectionRange() - Math.max(x, z));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
diff = this.plugin.getSettings().getSafeSpotSearchRange();
|
||||||
|
}
|
||||||
|
|
||||||
|
return diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method calculates location for portal.
|
||||||
|
* @param fromLocation Location from which teleportation happens.
|
||||||
|
* @param fromWorld World from which teleportation happens.
|
||||||
|
* @param toWorld The target world.
|
||||||
|
* @param environment Portal variant.
|
||||||
|
* @param canCreatePortal Indicates if portal should be created or not.
|
||||||
|
* @return Location for new portal.
|
||||||
|
*/
|
||||||
|
protected Location calculateLocation(Location fromLocation,
|
||||||
|
World fromWorld,
|
||||||
|
World toWorld,
|
||||||
|
World.Environment environment,
|
||||||
|
boolean canCreatePortal)
|
||||||
|
{
|
||||||
|
// Null check - not that useful
|
||||||
|
if (fromWorld == null || toWorld == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Location toLocation = fromLocation.toVector().toLocation(toWorld);
|
||||||
|
|
||||||
|
if (!this.isMakePortals(fromWorld, environment))
|
||||||
|
{
|
||||||
|
toLocation = this.getIsland(fromLocation).
|
||||||
|
map(island -> island.getSpawnPoint(toWorld.getEnvironment())).
|
||||||
|
orElse(toLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit Y to the min/max world height.
|
||||||
|
toLocation.setY(Math.max(Math.min(toLocation.getY(), toWorld.getMaxHeight()), toWorld.getMinHeight()));
|
||||||
|
|
||||||
|
if (!canCreatePortal)
|
||||||
|
{
|
||||||
|
// Legacy portaling
|
||||||
|
return toLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make portals
|
||||||
|
// For anywhere other than the end - it is the player's location that is used
|
||||||
|
if (!environment.equals(World.Environment.THE_END))
|
||||||
|
{
|
||||||
|
return toLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the-end then we want the platform to always be generated in the same place no matter where
|
||||||
|
// they enter the portal
|
||||||
|
final int x = fromLocation.getBlockX();
|
||||||
|
final int z = fromLocation.getBlockZ();
|
||||||
|
final int y = fromLocation.getBlockY();
|
||||||
|
int i = x;
|
||||||
|
int j = z;
|
||||||
|
int k = y;
|
||||||
|
|
||||||
|
// If the from is not a portal, then we have to find it
|
||||||
|
if (!fromLocation.getBlock().getType().equals(Material.END_PORTAL))
|
||||||
|
{
|
||||||
|
// Find the portal - due to speed, it is possible that the player will be below or above the portal
|
||||||
|
for (k = toWorld.getMinHeight(); (k < fromWorld.getMaxHeight()) &&
|
||||||
|
!fromWorld.getBlockAt(x, k, z).getType().equals(Material.END_PORTAL); k++);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the maximum x and z corner
|
||||||
|
for (; (i < x + 5) && fromWorld.getBlockAt(i, k, z).getType().equals(Material.END_PORTAL); i++) ;
|
||||||
|
for (; (j < z + 5) && fromWorld.getBlockAt(x, k, j).getType().equals(Material.END_PORTAL); j++) ;
|
||||||
|
|
||||||
|
// Mojang end platform generation is:
|
||||||
|
// AIR
|
||||||
|
// AIR
|
||||||
|
// OBSIDIAN
|
||||||
|
// and player is placed on second air block above obsidian.
|
||||||
|
// If Y coordinate is below 2, then obsidian platform is not generated and player falls in void.
|
||||||
|
return new Location(toWorld, i, Math.max(toWorld.getMinHeight() + 2, k), j);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns spawn location for given world.
|
||||||
|
* @param world World which spawn point must be returned.
|
||||||
|
* @return Spawn location for world or null.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
protected Location getSpawnLocation(World world)
|
||||||
|
{
|
||||||
|
return this.plugin.getIslandsManager().getSpawn(world).map(island ->
|
||||||
|
island.getSpawnPoint(World.Environment.NORMAL) == null ?
|
||||||
|
island.getCenter() :
|
||||||
|
island.getSpawnPoint(World.Environment.NORMAL)).
|
||||||
|
orElse(this.plugin.getIslands().isSafeLocation(world.getSpawnLocation()) ?
|
||||||
|
world.getSpawnLocation() : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns if missing islands should be generated uppon teleportation.
|
||||||
|
* Can happen only in non-custom generators.
|
||||||
|
* @param overWorld OverWorld
|
||||||
|
* @return {@code true} if missing islands must be pasted, {@code false} otherwise.
|
||||||
|
*/
|
||||||
|
protected boolean isPastingMissingIslands(World overWorld)
|
||||||
|
{
|
||||||
|
return this.plugin.getIWM().isPasteMissingIslands(overWorld) &&
|
||||||
|
!this.plugin.getIWM().isUseOwnGenerator(overWorld);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
// Section: Variables
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BentoBox plugin instance.
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
protected final BentoBox plugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set of entities that currently is inside portal.
|
||||||
|
*/
|
||||||
|
protected final Set<UUID> inPortal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map that links entities origin of teleportation. Used for respawning.
|
||||||
|
*/
|
||||||
|
protected final Map<UUID, World> teleportOrigin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set of entities that currently is in teleportation.
|
||||||
|
*/
|
||||||
|
protected final Set<UUID> inTeleport;
|
||||||
|
}
|
@ -0,0 +1,408 @@
|
|||||||
|
//
|
||||||
|
// Created by BONNe
|
||||||
|
// Copyright - 2022
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
package world.bentobox.bentobox.listeners.teleports;
|
||||||
|
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.entity.EntityPortalEnterEvent;
|
||||||
|
import org.bukkit.event.entity.EntityPortalEvent;
|
||||||
|
import org.bukkit.event.entity.EntityPortalExitEvent;
|
||||||
|
import org.bukkit.util.Vector;
|
||||||
|
import org.eclipse.jdt.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import world.bentobox.bentobox.BentoBox;
|
||||||
|
import world.bentobox.bentobox.lists.Flags;
|
||||||
|
import world.bentobox.bentobox.util.Util;
|
||||||
|
import world.bentobox.bentobox.util.teleport.ClosestSafeSpotTeleport;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class handles entity teleportation between dimensions.
|
||||||
|
*
|
||||||
|
* @author BONNe
|
||||||
|
*/
|
||||||
|
public class EntityTeleportListener extends AbstractTeleportListener implements Listener
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Instance of Teleportation processor.
|
||||||
|
*
|
||||||
|
* @param bentoBox BentoBox plugin.
|
||||||
|
*/
|
||||||
|
public EntityTeleportListener(@NonNull BentoBox bentoBox)
|
||||||
|
{
|
||||||
|
super(bentoBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This listener checks entity portal events and triggers appropriate methods to transfer
|
||||||
|
* entities to the correct location in other dimension.
|
||||||
|
*
|
||||||
|
* This event is triggered when entity is about to being teleported because of contact with the
|
||||||
|
* nether portal or end gateway portal (exit portal triggers respawn).
|
||||||
|
*
|
||||||
|
* This event is not called if nether/end is disabled in server settings.
|
||||||
|
*
|
||||||
|
* @param event the entity portal event.
|
||||||
|
*/
|
||||||
|
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
|
||||||
|
public void onEntityPortal(EntityPortalEvent event)
|
||||||
|
{
|
||||||
|
World fromWorld = event.getFrom().getWorld();
|
||||||
|
World overWorld = Util.getWorld(fromWorld);
|
||||||
|
|
||||||
|
if (overWorld == null || !this.plugin.getIWM().inWorld(overWorld) || event.getTo() == null)
|
||||||
|
{
|
||||||
|
// Not a bentobox world.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Flags.ENTITY_PORTAL_TELEPORT.isSetForWorld(overWorld))
|
||||||
|
{
|
||||||
|
// Teleportation is disabled. Cancel event.
|
||||||
|
event.setCancelled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trigger event processor.
|
||||||
|
this.portalProcess(event, event.getTo().getWorld().getEnvironment());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fires the event if nether or end is disabled at the system level
|
||||||
|
*
|
||||||
|
* @param event - EntityPortalEnterEvent
|
||||||
|
*/
|
||||||
|
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
|
||||||
|
public void onEntityEnterPortal(EntityPortalEnterEvent event)
|
||||||
|
{
|
||||||
|
if (EntityType.PLAYER.equals(event.getEntity().getType()))
|
||||||
|
{
|
||||||
|
// This handles only non-players.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity entity = event.getEntity();
|
||||||
|
Material type = event.getLocation().getBlock().getType();
|
||||||
|
UUID uuid = entity.getUniqueId();
|
||||||
|
|
||||||
|
if (this.inPortal.contains(uuid))
|
||||||
|
{
|
||||||
|
// Already in process.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
World fromWorld = event.getLocation().getWorld();
|
||||||
|
World overWorld = Util.getWorld(fromWorld);
|
||||||
|
|
||||||
|
if (overWorld == null || !this.plugin.getIWM().inWorld(overWorld))
|
||||||
|
{
|
||||||
|
// Not a bentobox world.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Flags.ENTITY_PORTAL_TELEPORT.isSetForWorld(overWorld))
|
||||||
|
{
|
||||||
|
// Teleportation is disabled. Cancel processing.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.inPortal.add(uuid);
|
||||||
|
// Add original world for respawning.
|
||||||
|
this.teleportOrigin.put(uuid, fromWorld);
|
||||||
|
|
||||||
|
// Entities are teleported instantly.
|
||||||
|
if (!Bukkit.getAllowNether() && type.equals(Material.NETHER_PORTAL))
|
||||||
|
{
|
||||||
|
if (fromWorld == overWorld)
|
||||||
|
{
|
||||||
|
this.portalProcess(
|
||||||
|
new EntityPortalEvent(entity, event.getLocation(), event.getLocation(), 0),
|
||||||
|
World.Environment.NETHER);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.portalProcess(
|
||||||
|
new EntityPortalEvent(entity, event.getLocation(), event.getLocation(), 0),
|
||||||
|
World.Environment.NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not process anything else.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entities are teleported instantly.
|
||||||
|
if (!Bukkit.getAllowEnd() && (type.equals(Material.END_PORTAL) || type.equals(Material.END_GATEWAY)))
|
||||||
|
{
|
||||||
|
if (fromWorld == this.getNetherEndWorld(overWorld, World.Environment.THE_END))
|
||||||
|
{
|
||||||
|
this.portalProcess(
|
||||||
|
new EntityPortalEvent(entity, event.getLocation(), event.getLocation(), 0),
|
||||||
|
World.Environment.NORMAL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.portalProcess(
|
||||||
|
new EntityPortalEvent(entity, event.getLocation(), event.getLocation(), 0),
|
||||||
|
World.Environment.THE_END);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove inPortal flag only when entity exits the portal
|
||||||
|
*
|
||||||
|
* @param event entity move event
|
||||||
|
*/
|
||||||
|
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||||
|
public void onEntityExitPortal(EntityPortalExitEvent event)
|
||||||
|
{
|
||||||
|
if (!this.inPortal.contains(event.getEntity().getUniqueId()))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.inPortal.remove(event.getEntity().getUniqueId());
|
||||||
|
this.inTeleport.remove(event.getEntity().getUniqueId());
|
||||||
|
this.teleportOrigin.remove(event.getEntity().getUniqueId());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
// Section: Methods
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method process entity teleportation to a correct dimension.
|
||||||
|
* @param event Event that triggers teleportation.
|
||||||
|
* @param environment Environment of the dimension where entity must appear.
|
||||||
|
*/
|
||||||
|
private void portalProcess(EntityPortalEvent event, World.Environment environment)
|
||||||
|
{
|
||||||
|
World fromWorld = event.getFrom().getWorld();
|
||||||
|
World overWorld = Util.getWorld(fromWorld);
|
||||||
|
|
||||||
|
if (fromWorld == null || overWorld == null)
|
||||||
|
{
|
||||||
|
// Missing worlds.
|
||||||
|
event.setCancelled(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.isAllowedInConfig(overWorld, environment))
|
||||||
|
{
|
||||||
|
// World is disabled in config. Do not teleport player.
|
||||||
|
event.setCancelled(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.isAllowedOnServer(environment))
|
||||||
|
{
|
||||||
|
// World is disabled in bukkit. Event is not triggered, but cancel by chance.
|
||||||
|
event.setCancelled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.inTeleport.contains(event.getEntity().getUniqueId()))
|
||||||
|
{
|
||||||
|
// Entity is already in teleportation.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.inTeleport.add(event.getEntity().getUniqueId());
|
||||||
|
|
||||||
|
// Get target world.
|
||||||
|
World toWorld;
|
||||||
|
|
||||||
|
if (environment.equals(World.Environment.NORMAL))
|
||||||
|
{
|
||||||
|
toWorld = overWorld;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
toWorld = this.getNetherEndWorld(overWorld, environment);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!overWorld.equals(toWorld) && !this.isIslandWorld(overWorld, environment))
|
||||||
|
{
|
||||||
|
// This is not island world. Use standard nether or end world teleportation.
|
||||||
|
this.handleToStandardNetherOrEnd(event, overWorld, toWorld);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!overWorld.equals(fromWorld) && !this.isIslandWorld(overWorld, environment))
|
||||||
|
{
|
||||||
|
// If entering a portal in the other world, teleport to a portal in overworld if
|
||||||
|
// there is one
|
||||||
|
this.handleFromStandardNetherOrEnd(event, overWorld, toWorld.getEnvironment());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the destination location
|
||||||
|
// If portals cannot be created, then destination is the spawn point, otherwise it's the vector
|
||||||
|
event.setTo(this.calculateLocation(event.getFrom(),
|
||||||
|
fromWorld,
|
||||||
|
toWorld,
|
||||||
|
environment,
|
||||||
|
this.isMakePortals(overWorld, environment)));
|
||||||
|
|
||||||
|
// Calculate search radius for portal
|
||||||
|
this.getIsland(event.getTo()).ifPresent(island ->
|
||||||
|
event.setSearchRadius(this.calculateSearchRadius(event.getTo(), island)));
|
||||||
|
|
||||||
|
// Check if there is an island there or not
|
||||||
|
if (this.isPastingMissingIslands(overWorld) &&
|
||||||
|
this.isAllowedInConfig(overWorld, environment) &&
|
||||||
|
this.isIslandWorld(overWorld, environment) &&
|
||||||
|
this.getNetherEndWorld(overWorld, environment) != null &&
|
||||||
|
this.getIsland(event.getTo()).
|
||||||
|
filter(island -> !this.hasPartnerIsland(island, environment)).
|
||||||
|
map(island -> {
|
||||||
|
event.setCancelled(true);
|
||||||
|
return true;
|
||||||
|
}).
|
||||||
|
orElse(false))
|
||||||
|
{
|
||||||
|
// If there is no island, then processor already entity cannot be teleported before player
|
||||||
|
// visit that dimension.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!event.isCancelled())
|
||||||
|
{
|
||||||
|
// Let the server teleport
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (environment.equals(World.Environment.THE_END))
|
||||||
|
{
|
||||||
|
// Prevent death from hitting the ground while calculating location.
|
||||||
|
event.getEntity().setVelocity(new Vector(0,0,0));
|
||||||
|
event.getEntity().setFallDistance(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we do not generate portals, teleportation should happen manually with safe spot builder.
|
||||||
|
// Otherwise, we could end up with situations when player is placed in mid air, if teleportation
|
||||||
|
// is done instantly.
|
||||||
|
// Our safe spot task is triggered in next tick, however, end teleportation happens in the same tick.
|
||||||
|
// It is placed outside THE_END check, as technically it could happen with the nether portal too.
|
||||||
|
|
||||||
|
// If there is a portal to go to already, then the player will go there
|
||||||
|
Bukkit.getScheduler().runTask(this.plugin, () -> {
|
||||||
|
if (!event.getEntity().getWorld().equals(toWorld))
|
||||||
|
{
|
||||||
|
// Else manually teleport entity
|
||||||
|
ClosestSafeSpotTeleport.builder(this.plugin).
|
||||||
|
entity(event.getEntity()).
|
||||||
|
location(event.getTo()).
|
||||||
|
portal().
|
||||||
|
successRunnable(() -> {
|
||||||
|
// Reset velocity just in case.
|
||||||
|
event.getEntity().setVelocity(new Vector(0,0,0));
|
||||||
|
event.getEntity().setFallDistance(0);
|
||||||
|
}).
|
||||||
|
build();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle teleport to standard nether or end
|
||||||
|
* @param event - EntityPortalEvent
|
||||||
|
* @param overWorld - over world
|
||||||
|
* @param toWorld - to world
|
||||||
|
*/
|
||||||
|
private void handleToStandardNetherOrEnd(EntityPortalEvent event, World overWorld, World toWorld)
|
||||||
|
{
|
||||||
|
Location spawnPoint = toWorld.getSpawnLocation();
|
||||||
|
|
||||||
|
// If going to the nether and nether portals are active then just teleport to approx location
|
||||||
|
if (World.Environment.NETHER.equals(toWorld.getEnvironment()) &&
|
||||||
|
this.plugin.getIWM().getWorldSettings(overWorld).isMakeNetherPortals())
|
||||||
|
{
|
||||||
|
spawnPoint = event.getFrom().toVector().toLocation(toWorld);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If spawn is set as 0,63,0 in the End then move it to 100, 50 ,0.
|
||||||
|
if (World.Environment.THE_END.equals(toWorld.getEnvironment()) && spawnPoint.getBlockX() == 0 && spawnPoint.getBlockZ() == 0)
|
||||||
|
{
|
||||||
|
// Set to the default end spawn
|
||||||
|
spawnPoint = new Location(toWorld, 100, 50, 0);
|
||||||
|
toWorld.setSpawnLocation(100, 50, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isAllowedOnServer(toWorld.getEnvironment()))
|
||||||
|
{
|
||||||
|
// To Standard Nether or end
|
||||||
|
event.setTo(spawnPoint);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Teleport to standard nether or end
|
||||||
|
ClosestSafeSpotTeleport.builder(this.plugin).
|
||||||
|
entity(event.getEntity()).
|
||||||
|
location(spawnPoint).
|
||||||
|
portal().
|
||||||
|
build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle teleport from standard nether or end
|
||||||
|
* @param event - EntityPortalEvent
|
||||||
|
* @param overWorld - over world
|
||||||
|
* @param environment - to world environment
|
||||||
|
*/
|
||||||
|
private void handleFromStandardNetherOrEnd(EntityPortalEvent event, World overWorld, World.Environment environment)
|
||||||
|
{
|
||||||
|
if (World.Environment.NETHER.equals(environment) &&
|
||||||
|
this.plugin.getIWM().getWorldSettings(overWorld).isMakeNetherPortals())
|
||||||
|
{
|
||||||
|
// Set to location directly to the from location.
|
||||||
|
event.setTo(event.getFrom().toVector().toLocation(overWorld));
|
||||||
|
|
||||||
|
// Update portal search radius.
|
||||||
|
this.getIsland(event.getTo()).ifPresent(island ->
|
||||||
|
event.setSearchRadius(this.calculateSearchRadius(event.getTo(), island)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Cannot be portal. Should recalculate position.
|
||||||
|
Location spawnLocation = this.getSpawnLocation(overWorld);
|
||||||
|
|
||||||
|
event.setTo(spawnLocation == null ?
|
||||||
|
event.getFrom().toVector().toLocation(overWorld) :
|
||||||
|
spawnLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.isAllowedOnServer(environment))
|
||||||
|
{
|
||||||
|
// Custom portal handling.
|
||||||
|
event.setCancelled(true);
|
||||||
|
|
||||||
|
// Teleport to standard nether or end
|
||||||
|
ClosestSafeSpotTeleport.builder(this.plugin).
|
||||||
|
entity(event.getEntity()).
|
||||||
|
location(event.getTo()).
|
||||||
|
portal().
|
||||||
|
build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,501 @@
|
|||||||
|
//
|
||||||
|
// Created by BONNe
|
||||||
|
// Copyright - 2022
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
package world.bentobox.bentobox.listeners.teleports;
|
||||||
|
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.entity.EntityPortalEnterEvent;
|
||||||
|
import org.bukkit.event.player.PlayerMoveEvent;
|
||||||
|
import org.bukkit.event.player.PlayerPortalEvent;
|
||||||
|
import org.bukkit.event.player.PlayerRespawnEvent;
|
||||||
|
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||||
|
import org.bukkit.util.Vector;
|
||||||
|
import org.eclipse.jdt.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import world.bentobox.bentobox.BentoBox;
|
||||||
|
import world.bentobox.bentobox.blueprints.Blueprint;
|
||||||
|
import world.bentobox.bentobox.blueprints.BlueprintPaster;
|
||||||
|
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBundle;
|
||||||
|
import world.bentobox.bentobox.database.objects.Island;
|
||||||
|
import world.bentobox.bentobox.util.Util;
|
||||||
|
import world.bentobox.bentobox.util.teleport.ClosestSafeSpotTeleport;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class handles player teleportation between dimensions.
|
||||||
|
*
|
||||||
|
* @author tastybento and BONNe
|
||||||
|
*/
|
||||||
|
public class PlayerTeleportListener extends AbstractTeleportListener implements Listener
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Instantiates a new Portal teleportation listener.
|
||||||
|
*
|
||||||
|
* @param plugin the plugin
|
||||||
|
*/
|
||||||
|
public PlayerTeleportListener(@NonNull BentoBox plugin)
|
||||||
|
{
|
||||||
|
super(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
// Section: Listeners
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This listener checks player portal events and triggers appropriate methods to transfer
|
||||||
|
* players to the correct location in other dimension.
|
||||||
|
*
|
||||||
|
* This event is triggered when player is about to being teleported because of contact with the
|
||||||
|
* nether portal or end gateway portal (exit portal triggers respawn).
|
||||||
|
*
|
||||||
|
* This event is not called if nether/end is disabled in server settings.
|
||||||
|
*
|
||||||
|
* @param event the player portal event.
|
||||||
|
*/
|
||||||
|
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
|
||||||
|
public void onPlayerPortalEvent(PlayerPortalEvent event)
|
||||||
|
{
|
||||||
|
switch (event.getCause())
|
||||||
|
{
|
||||||
|
case NETHER_PORTAL -> this.portalProcess(event, World.Environment.NETHER);
|
||||||
|
case END_PORTAL, END_GATEWAY -> this.portalProcess(event, World.Environment.THE_END);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fires the event if nether or end is disabled at the system level
|
||||||
|
*
|
||||||
|
* @param event - EntityPortalEnterEvent
|
||||||
|
*/
|
||||||
|
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
|
||||||
|
public void onPlayerPortal(EntityPortalEnterEvent event)
|
||||||
|
{
|
||||||
|
if (!EntityType.PLAYER.equals(event.getEntity().getType()))
|
||||||
|
{
|
||||||
|
// This handles only players.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity entity = event.getEntity();
|
||||||
|
Material type = event.getLocation().getBlock().getType();
|
||||||
|
UUID uuid = entity.getUniqueId();
|
||||||
|
|
||||||
|
if (this.inPortal.contains(uuid) ||
|
||||||
|
!this.plugin.getIWM().inWorld(Util.getWorld(event.getLocation().getWorld())))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.inPortal.add(uuid);
|
||||||
|
// Add original world for respawning.
|
||||||
|
this.teleportOrigin.put(uuid, event.getLocation().getWorld());
|
||||||
|
|
||||||
|
if (!Bukkit.getAllowNether() && type.equals(Material.NETHER_PORTAL))
|
||||||
|
{
|
||||||
|
// Schedule a time
|
||||||
|
Bukkit.getScheduler().runTaskLater(this.plugin, () ->
|
||||||
|
{
|
||||||
|
// Check again if still in portal
|
||||||
|
if (this.inPortal.contains(uuid))
|
||||||
|
{
|
||||||
|
// Create new PlayerPortalEvent
|
||||||
|
PlayerPortalEvent en = new PlayerPortalEvent((Player) entity,
|
||||||
|
event.getLocation(),
|
||||||
|
null,
|
||||||
|
PlayerTeleportEvent.TeleportCause.NETHER_PORTAL,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
0);
|
||||||
|
|
||||||
|
this.portalProcess(en, World.Environment.NETHER);
|
||||||
|
}
|
||||||
|
}, 40);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// End portals are instant transfer
|
||||||
|
if (!Bukkit.getAllowEnd() && (type.equals(Material.END_PORTAL) || type.equals(Material.END_GATEWAY)))
|
||||||
|
{
|
||||||
|
// Create new PlayerPortalEvent
|
||||||
|
PlayerPortalEvent en = new PlayerPortalEvent((Player) entity,
|
||||||
|
event.getLocation(),
|
||||||
|
null,
|
||||||
|
type.equals(Material.END_PORTAL) ? PlayerTeleportEvent.TeleportCause.END_PORTAL : PlayerTeleportEvent.TeleportCause.END_GATEWAY,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
0);
|
||||||
|
|
||||||
|
this.portalProcess(en, World.Environment.THE_END);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove inPortal flag only when player exits the portal
|
||||||
|
*
|
||||||
|
* @param event player move event
|
||||||
|
*/
|
||||||
|
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||||
|
public void onExitPortal(PlayerMoveEvent event)
|
||||||
|
{
|
||||||
|
if (!this.inPortal.contains(event.getPlayer().getUniqueId()))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.getTo() != null && !event.getTo().getBlock().getType().equals(Material.NETHER_PORTAL))
|
||||||
|
{
|
||||||
|
// Player exits nether portal.
|
||||||
|
this.inPortal.remove(event.getPlayer().getUniqueId());
|
||||||
|
this.inTeleport.remove(event.getPlayer().getUniqueId());
|
||||||
|
this.teleportOrigin.remove(event.getPlayer().getUniqueId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Player respawn event is triggered when player enters exit portal at the end.
|
||||||
|
* This will take over respawn mechanism and place player on island.
|
||||||
|
* @param event player respawn event
|
||||||
|
*/
|
||||||
|
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||||
|
public void onPlayerExitPortal(PlayerRespawnEvent event)
|
||||||
|
{
|
||||||
|
if (!this.teleportOrigin.containsKey(event.getPlayer().getUniqueId()))
|
||||||
|
{
|
||||||
|
// Player is already processed.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
World fromWorld = this.teleportOrigin.get(event.getPlayer().getUniqueId());
|
||||||
|
World overWorld = Util.getWorld(fromWorld);
|
||||||
|
|
||||||
|
if (overWorld == null || !this.plugin.getIWM().inWorld(overWorld))
|
||||||
|
{
|
||||||
|
// Not teleporting from/to bentobox worlds.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getIsland(overWorld, event.getPlayer()).ifPresentOrElse(island -> {
|
||||||
|
if (!island.onIsland(event.getRespawnLocation()))
|
||||||
|
{
|
||||||
|
// If respawn location is outside island protection range, change location to the
|
||||||
|
// spawn in overworld or home location.
|
||||||
|
Location location = island.getSpawnPoint(World.Environment.NORMAL);
|
||||||
|
|
||||||
|
if (location == null)
|
||||||
|
{
|
||||||
|
// No spawn point. Rare thing. Well, use island protection center.
|
||||||
|
location = island.getProtectionCenter();
|
||||||
|
}
|
||||||
|
|
||||||
|
event.setRespawnLocation(location);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
() -> {
|
||||||
|
// Player does not an island. Try to get spawn island, and if that fails, use world spawn point.
|
||||||
|
// If spawn point is not safe, do nothing. Let server handle it.
|
||||||
|
|
||||||
|
Location spawnLocation = this.getSpawnLocation(overWorld);
|
||||||
|
|
||||||
|
if (spawnLocation != null)
|
||||||
|
{
|
||||||
|
event.setRespawnLocation(spawnLocation);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
// Section: Processors
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method process player teleportation to new dimension.
|
||||||
|
* @param event Event that triggers teleportation.
|
||||||
|
* @param environment Environment of portal type.
|
||||||
|
*/
|
||||||
|
private void portalProcess(PlayerPortalEvent event, World.Environment environment)
|
||||||
|
{
|
||||||
|
World fromWorld = event.getFrom().getWorld();
|
||||||
|
World overWorld = Util.getWorld(fromWorld);
|
||||||
|
|
||||||
|
if (overWorld == null || !this.plugin.getIWM().inWorld(overWorld))
|
||||||
|
{
|
||||||
|
// Not teleporting from/to bentobox worlds.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.isAllowedInConfig(overWorld, environment))
|
||||||
|
{
|
||||||
|
// World is disabled in config. Do not teleport player.
|
||||||
|
event.setCancelled(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.isAllowedOnServer(environment))
|
||||||
|
{
|
||||||
|
// World is disabled in bukkit. Event is not triggered, but cancel by chance.
|
||||||
|
event.setCancelled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.inTeleport.contains(event.getPlayer().getUniqueId()))
|
||||||
|
{
|
||||||
|
// Player is already in teleportation.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.inTeleport.add(event.getPlayer().getUniqueId());
|
||||||
|
|
||||||
|
if (fromWorld.equals(overWorld) && !this.isIslandWorld(overWorld, environment))
|
||||||
|
{
|
||||||
|
// This is not island world. Use standard nether or end world teleportation.
|
||||||
|
this.handleToStandardNetherOrEnd(event, overWorld, environment);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fromWorld.equals(overWorld) && !this.isIslandWorld(overWorld, environment))
|
||||||
|
{
|
||||||
|
// If entering a portal in the other world, teleport to a portal in overworld if
|
||||||
|
// there is one
|
||||||
|
this.handleFromStandardNetherOrEnd(event, overWorld, environment);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// To the nether/end or overworld.
|
||||||
|
World toWorld = !fromWorld.getEnvironment().equals(environment) ?
|
||||||
|
this.getNetherEndWorld(overWorld, environment) : overWorld;
|
||||||
|
|
||||||
|
// Set whether portals should be created or not
|
||||||
|
event.setCanCreatePortal(this.isMakePortals(overWorld, environment));
|
||||||
|
// Default 16 is will always end up placing portal as close to X/8 coordinate as possible.
|
||||||
|
// In most situations, 2 block value should be enough... I hope.
|
||||||
|
event.setCreationRadius(2);
|
||||||
|
|
||||||
|
// Set the destination location
|
||||||
|
// If portals cannot be created, then destination is the spawn point, otherwise it's the vector
|
||||||
|
event.setTo(this.calculateLocation(event.getFrom(), fromWorld, toWorld, environment, event.getCanCreatePortal()));
|
||||||
|
|
||||||
|
// Find the distance from edge of island's protection and set the search radius
|
||||||
|
this.getIsland(event.getTo()).ifPresent(island ->
|
||||||
|
event.setSearchRadius(this.calculateSearchRadius(event.getTo(), island)));
|
||||||
|
|
||||||
|
// Check if there is an island there or not
|
||||||
|
if (this.isPastingMissingIslands(overWorld) &&
|
||||||
|
this.isAllowedInConfig(overWorld, environment) &&
|
||||||
|
this.isIslandWorld(overWorld, environment) &&
|
||||||
|
this.getNetherEndWorld(overWorld, environment) != null &&
|
||||||
|
this.getIsland(event.getTo()).
|
||||||
|
filter(island -> !this.hasPartnerIsland(island, environment)).
|
||||||
|
map(island -> {
|
||||||
|
event.setCancelled(true);
|
||||||
|
this.pasteNewIsland(event.getPlayer(), event.getTo(), island, environment);
|
||||||
|
return true;
|
||||||
|
}).
|
||||||
|
orElse(false))
|
||||||
|
{
|
||||||
|
// If there is no island, then processor already created island. Nothing to do more.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!event.isCancelled() && event.getCanCreatePortal())
|
||||||
|
{
|
||||||
|
// Let the server teleport
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (environment.equals(World.Environment.THE_END))
|
||||||
|
{
|
||||||
|
// Prevent death from hitting the ground while calculating location.
|
||||||
|
event.getPlayer().setVelocity(new Vector(0,0,0));
|
||||||
|
event.getPlayer().setFallDistance(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we do not generate portals, teleportation should happen manually with safe spot builder.
|
||||||
|
// Otherwise, we could end up with situations when player is placed in mid air, if teleportation
|
||||||
|
// is done instantly.
|
||||||
|
// Our safe spot task is triggered in next tick, however, end teleportation happens in the same tick.
|
||||||
|
// It is placed outside THE_END check, as technically it could happen with the nether portal too.
|
||||||
|
|
||||||
|
// If there is a portal to go to already, then the player will go there
|
||||||
|
Bukkit.getScheduler().runTask(this.plugin, () -> {
|
||||||
|
if (!event.getPlayer().getWorld().equals(toWorld))
|
||||||
|
{
|
||||||
|
// Else manually teleport entity
|
||||||
|
ClosestSafeSpotTeleport.builder(this.plugin).
|
||||||
|
entity(event.getPlayer()).
|
||||||
|
location(event.getTo()).
|
||||||
|
portal().
|
||||||
|
successRunnable(() -> {
|
||||||
|
// Reset velocity just in case.
|
||||||
|
event.getPlayer().setVelocity(new Vector(0,0,0));
|
||||||
|
event.getPlayer().setFallDistance(0);
|
||||||
|
}).
|
||||||
|
build();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle teleport from or to standard nether or end
|
||||||
|
* @param event - PlayerPortalEvent
|
||||||
|
* @param overWorld - over world
|
||||||
|
* @param environment - environment involved
|
||||||
|
*/
|
||||||
|
private void handleToStandardNetherOrEnd(PlayerPortalEvent event,
|
||||||
|
World overWorld,
|
||||||
|
World.Environment environment)
|
||||||
|
{
|
||||||
|
World toWorld = Objects.requireNonNull(this.getNetherEndWorld(overWorld, environment));
|
||||||
|
Location spawnPoint = toWorld.getSpawnLocation();
|
||||||
|
|
||||||
|
// If going to the nether and nether portals are active then just teleport to approx location
|
||||||
|
if (environment.equals(World.Environment.NETHER) &&
|
||||||
|
this.plugin.getIWM().getWorldSettings(overWorld).isMakeNetherPortals())
|
||||||
|
{
|
||||||
|
spawnPoint = event.getFrom().toVector().toLocation(toWorld);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If spawn is set as 0,63,0 in the End then move it to 100, 50 ,0.
|
||||||
|
if (environment.equals(World.Environment.THE_END) && spawnPoint.getBlockX() == 0 && spawnPoint.getBlockZ() == 0)
|
||||||
|
{
|
||||||
|
// Set to the default end spawn
|
||||||
|
spawnPoint = new Location(toWorld, 100, 50, 0);
|
||||||
|
toWorld.setSpawnLocation(100, 50, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isAllowedOnServer(environment))
|
||||||
|
{
|
||||||
|
// To Standard Nether or end
|
||||||
|
event.setTo(spawnPoint);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Teleport to standard nether or end
|
||||||
|
ClosestSafeSpotTeleport.builder(this.plugin).
|
||||||
|
entity(event.getPlayer()).
|
||||||
|
location(spawnPoint).
|
||||||
|
portal().
|
||||||
|
build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle teleport from or to standard nether or end (end is not possible because EXIT PORTAL triggers RESPAWN event)
|
||||||
|
* @param event - PlayerPortalEvent
|
||||||
|
* @param overWorld - over world
|
||||||
|
* @param environment - environment involved
|
||||||
|
*/
|
||||||
|
private void handleFromStandardNetherOrEnd(PlayerPortalEvent event, World overWorld, World.Environment environment)
|
||||||
|
{
|
||||||
|
if (environment.equals(World.Environment.NETHER) &&
|
||||||
|
this.plugin.getIWM().getWorldSettings(overWorld).isMakeNetherPortals())
|
||||||
|
{
|
||||||
|
// Set to location directly to the from location.
|
||||||
|
event.setTo(event.getFrom().toVector().toLocation(overWorld));
|
||||||
|
|
||||||
|
// Update portal search radius.
|
||||||
|
this.getIsland(event.getTo()).ifPresent(island ->
|
||||||
|
event.setSearchRadius(this.calculateSearchRadius(event.getTo(), island)));
|
||||||
|
|
||||||
|
event.setCanCreatePortal(true);
|
||||||
|
// event.setCreationRadius(16); 16 is default creation radius.
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Cannot be portal. Should recalculate position.
|
||||||
|
// TODO: Currently, it is always spawn location. However, default home must be assigned.
|
||||||
|
Location toLocation = this.getIsland(overWorld, event.getPlayer()).
|
||||||
|
map(island -> island.getSpawnPoint(World.Environment.NORMAL)).
|
||||||
|
orElseGet(() -> {
|
||||||
|
// If player do not have island, try spawn.
|
||||||
|
Location spawnLocation = this.getSpawnLocation(overWorld);
|
||||||
|
return spawnLocation == null ?
|
||||||
|
event.getFrom().toVector().toLocation(overWorld) :
|
||||||
|
spawnLocation;
|
||||||
|
});
|
||||||
|
|
||||||
|
event.setTo(toLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.isAllowedOnServer(environment))
|
||||||
|
{
|
||||||
|
// Custom portal handling.
|
||||||
|
event.setCancelled(true);
|
||||||
|
|
||||||
|
// Teleport to standard nether or end
|
||||||
|
ClosestSafeSpotTeleport.builder(this.plugin).
|
||||||
|
entity(event.getPlayer()).
|
||||||
|
location(event.getTo()).
|
||||||
|
portal().
|
||||||
|
build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pastes the default nether or end island and teleports the player to the island's spawn point
|
||||||
|
* @param player - player to teleport after pasting
|
||||||
|
* @param to - the fallback location if a spawn point is not part of the blueprint
|
||||||
|
* @param island - the island
|
||||||
|
* @param environment - NETHER or THE_END
|
||||||
|
*/
|
||||||
|
private void pasteNewIsland(Player player,
|
||||||
|
Location to,
|
||||||
|
Island island,
|
||||||
|
World.Environment environment)
|
||||||
|
{
|
||||||
|
// Paste then teleport player
|
||||||
|
this.plugin.getIWM().getAddon(island.getWorld()).ifPresent(addon ->
|
||||||
|
{
|
||||||
|
// Get the default bundle's nether or end blueprint
|
||||||
|
BlueprintBundle blueprintBundle = plugin.getBlueprintsManager().getDefaultBlueprintBundle(addon);
|
||||||
|
|
||||||
|
if (blueprintBundle != null)
|
||||||
|
{
|
||||||
|
Blueprint bluePrint = this.plugin.getBlueprintsManager().getBlueprints(addon).
|
||||||
|
get(blueprintBundle.getBlueprint(environment));
|
||||||
|
|
||||||
|
if (bluePrint != null)
|
||||||
|
{
|
||||||
|
new BlueprintPaster(this.plugin, bluePrint, to.getWorld(), island).
|
||||||
|
paste().
|
||||||
|
thenAccept(state -> ClosestSafeSpotTeleport.builder(this.plugin).
|
||||||
|
entity(player).
|
||||||
|
location(island.getSpawnPoint(environment) == null ? to : island.getSpawnPoint(environment)).
|
||||||
|
portal().
|
||||||
|
build());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.plugin.logError("Could not paste default island in nether or end. " +
|
||||||
|
"Is there a nether-island or end-island blueprint?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package world.bentobox.bentobox.lists;
|
package world.bentobox.bentobox.lists;
|
||||||
|
|
||||||
|
import com.google.common.base.Enums;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -13,57 +14,11 @@ import world.bentobox.bentobox.api.flags.clicklisteners.CycleClick;
|
|||||||
import world.bentobox.bentobox.listeners.flags.clicklisteners.CommandRankClickListener;
|
import world.bentobox.bentobox.listeners.flags.clicklisteners.CommandRankClickListener;
|
||||||
import world.bentobox.bentobox.listeners.flags.clicklisteners.GeoLimitClickListener;
|
import world.bentobox.bentobox.listeners.flags.clicklisteners.GeoLimitClickListener;
|
||||||
import world.bentobox.bentobox.listeners.flags.clicklisteners.MobLimitClickListener;
|
import world.bentobox.bentobox.listeners.flags.clicklisteners.MobLimitClickListener;
|
||||||
import world.bentobox.bentobox.listeners.flags.protection.BlockInteractionListener;
|
import world.bentobox.bentobox.listeners.flags.protection.*;
|
||||||
import world.bentobox.bentobox.listeners.flags.protection.BreakBlocksListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.protection.BreedingListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.protection.BucketListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.protection.DyeListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.protection.EggListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.protection.ElytraListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.protection.EntityInteractListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.protection.ExperiencePickupListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.protection.FireListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.protection.HurtingListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.protection.InventoryListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.protection.ItemDropPickUpListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.protection.LeashListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.protection.LecternListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.protection.LockAndBanListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.protection.PaperExperiencePickupListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.protection.PhysicalInteractionListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.protection.PlaceBlocksListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.protection.PortalListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.protection.ShearingListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.protection.TNTListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.protection.TeleportationListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.protection.ThrowingListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.settings.DecayListener;
|
import world.bentobox.bentobox.listeners.flags.settings.DecayListener;
|
||||||
import world.bentobox.bentobox.listeners.flags.settings.MobSpawnListener;
|
import world.bentobox.bentobox.listeners.flags.settings.MobSpawnListener;
|
||||||
import world.bentobox.bentobox.listeners.flags.settings.PVPListener;
|
import world.bentobox.bentobox.listeners.flags.settings.PVPListener;
|
||||||
import world.bentobox.bentobox.listeners.flags.worldsettings.ChestDamageListener;
|
import world.bentobox.bentobox.listeners.flags.worldsettings.*;
|
||||||
import world.bentobox.bentobox.listeners.flags.worldsettings.CleanSuperFlatListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.worldsettings.CoarseDirtTillingListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.worldsettings.CreeperListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.worldsettings.EnderChestListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.worldsettings.EndermanListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.worldsettings.EnterExitListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.worldsettings.GeoLimitMobsListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.worldsettings.InvincibleVisitorsListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.worldsettings.IslandRespawnListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.worldsettings.ItemFrameListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.worldsettings.LimitMobsListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.worldsettings.LiquidsFlowingOutListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.worldsettings.NaturalSpawningOutsideRangeListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.worldsettings.ObsidianScoopingListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.worldsettings.OfflineGrowthListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.worldsettings.OfflineRedstoneListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.worldsettings.PetTeleportListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.worldsettings.PistonPushListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.worldsettings.RemoveMobsListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.worldsettings.SpawnerSpawnEggsListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.worldsettings.TreesGrowingOutsideRangeListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.worldsettings.VisitorKeepInventoryListener;
|
|
||||||
import world.bentobox.bentobox.listeners.flags.worldsettings.WitherListener;
|
|
||||||
import world.bentobox.bentobox.managers.RanksManager;
|
import world.bentobox.bentobox.managers.RanksManager;
|
||||||
import world.bentobox.bentobox.util.Util;
|
import world.bentobox.bentobox.util.Util;
|
||||||
|
|
||||||
@ -174,7 +129,10 @@ public final class Flags {
|
|||||||
public static final Flag BOAT = new Flag.Builder("BOAT", Material.OAK_BOAT).mode(Flag.Mode.BASIC).build();
|
public static final Flag BOAT = new Flag.Builder("BOAT", Material.OAK_BOAT).mode(Flag.Mode.BASIC).build();
|
||||||
public static final Flag TRADING = new Flag.Builder("TRADING", Material.EMERALD).defaultSetting(true).mode(Flag.Mode.BASIC).build();
|
public static final Flag TRADING = new Flag.Builder("TRADING", Material.EMERALD).defaultSetting(true).mode(Flag.Mode.BASIC).build();
|
||||||
public static final Flag NAME_TAG = new Flag.Builder("NAME_TAG", Material.NAME_TAG).mode(Flag.Mode.ADVANCED).build();
|
public static final Flag NAME_TAG = new Flag.Builder("NAME_TAG", Material.NAME_TAG).mode(Flag.Mode.ADVANCED).build();
|
||||||
|
/**
|
||||||
|
* @since 1.21
|
||||||
|
*/
|
||||||
|
public static final Flag ALLAY = new Flag.Builder("ALLAY", Material.AMETHYST_SHARD).mode(Flag.Mode.ADVANCED).build();
|
||||||
// Breeding
|
// Breeding
|
||||||
public static final Flag BREEDING = new Flag.Builder("BREEDING", Material.CARROT).listener(new BreedingListener()).mode(Flag.Mode.ADVANCED).build();
|
public static final Flag BREEDING = new Flag.Builder("BREEDING", Material.CARROT).listener(new BreedingListener()).mode(Flag.Mode.ADVANCED).build();
|
||||||
|
|
||||||
@ -182,8 +140,16 @@ public final class Flags {
|
|||||||
public static final Flag BUCKET = new Flag.Builder("BUCKET", Material.BUCKET).listener(new BucketListener()).mode(Flag.Mode.BASIC).build();
|
public static final Flag BUCKET = new Flag.Builder("BUCKET", Material.BUCKET).listener(new BucketListener()).mode(Flag.Mode.BASIC).build();
|
||||||
public static final Flag COLLECT_LAVA = new Flag.Builder("COLLECT_LAVA", Material.LAVA_BUCKET).build();
|
public static final Flag COLLECT_LAVA = new Flag.Builder("COLLECT_LAVA", Material.LAVA_BUCKET).build();
|
||||||
public static final Flag COLLECT_WATER = new Flag.Builder("COLLECT_WATER", Material.WATER_BUCKET).mode(Flag.Mode.ADVANCED).build();
|
public static final Flag COLLECT_WATER = new Flag.Builder("COLLECT_WATER", Material.WATER_BUCKET).mode(Flag.Mode.ADVANCED).build();
|
||||||
|
/**
|
||||||
|
* @since 1.21
|
||||||
|
*/
|
||||||
|
public static final Flag COLLECT_POWDERED_SNOW = new Flag.Builder("COLLECT_POWDERED_SNOW", Material.POWDER_SNOW_BUCKET).mode(Flag.Mode.ADVANCED).build();
|
||||||
public static final Flag MILKING = new Flag.Builder("MILKING", Material.MILK_BUCKET).mode(Flag.Mode.ADVANCED).build();
|
public static final Flag MILKING = new Flag.Builder("MILKING", Material.MILK_BUCKET).mode(Flag.Mode.ADVANCED).build();
|
||||||
public static final Flag FISH_SCOOPING = new Flag.Builder("FISH_SCOOPING", Material.TROPICAL_FISH_BUCKET).build();
|
public static final Flag FISH_SCOOPING = new Flag.Builder("FISH_SCOOPING", Material.TROPICAL_FISH_BUCKET).build();
|
||||||
|
/**
|
||||||
|
* @since 1.21
|
||||||
|
*/
|
||||||
|
public static final Flag AXOLOTL_SCOOPING = new Flag.Builder("AXOLOTL_SCOOPING", Material.AXOLOTL_BUCKET).build();
|
||||||
|
|
||||||
// Chorus Fruit and Enderpearls
|
// Chorus Fruit and Enderpearls
|
||||||
public static final Flag CHORUS_FRUIT = new Flag.Builder("CHORUS_FRUIT", Material.CHORUS_FRUIT).listener(new TeleportationListener()).build();
|
public static final Flag CHORUS_FRUIT = new Flag.Builder("CHORUS_FRUIT", Material.CHORUS_FRUIT).listener(new TeleportationListener()).build();
|
||||||
@ -314,6 +280,32 @@ public final class Flags {
|
|||||||
.clickHandler(new CycleClick("CHANGE_SETTINGS", RanksManager.MEMBER_RANK, RanksManager.OWNER_RANK))
|
.clickHandler(new CycleClick("CHANGE_SETTINGS", RanksManager.MEMBER_RANK, RanksManager.OWNER_RANK))
|
||||||
.mode(Flag.Mode.TOP_ROW).build();
|
.mode(Flag.Mode.TOP_ROW).build();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This flag allows choosing which island member group can activate sculk sensors.
|
||||||
|
* TODO: Enums#getIfPresent is used to support 1.18
|
||||||
|
* @since 1.21.0
|
||||||
|
*/
|
||||||
|
public static final Flag SCULK_SENSOR = new Flag.Builder("SCULK_SENSOR", Enums.getIfPresent(Material.class, "SCULK_SENSOR").or(Material.BARRIER)).
|
||||||
|
listener(new SculkSensorListener()).
|
||||||
|
type(Type.PROTECTION).
|
||||||
|
defaultSetting(true).
|
||||||
|
defaultRank(RanksManager.MEMBER_RANK).
|
||||||
|
clickHandler(new CycleClick("SCULK_SENSOR", RanksManager.VISITOR_RANK, RanksManager.MEMBER_RANK)).
|
||||||
|
build();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This flag allows choosing which island member group can activate sculk shrieker.
|
||||||
|
* TODO: Enums#getIfPresent is used to support 1.18
|
||||||
|
* @since 1.21.0
|
||||||
|
*/
|
||||||
|
public static final Flag SCULK_SHRIEKER = new Flag.Builder("SCULK_SHRIEKER", Enums.getIfPresent(Material.class, "SCULK_SHRIEKER").or(Material.BARRIER)).
|
||||||
|
listener(new SculkShriekerListener()).
|
||||||
|
type(Type.PROTECTION).
|
||||||
|
defaultSetting(true).
|
||||||
|
defaultRank(RanksManager.MEMBER_RANK).
|
||||||
|
clickHandler(new CycleClick("SCULK_SHRIEKER", RanksManager.VISITOR_RANK, RanksManager.MEMBER_RANK)).
|
||||||
|
build();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Settings flags (not protection flags)
|
* Settings flags (not protection flags)
|
||||||
*/
|
*/
|
||||||
@ -571,6 +563,20 @@ public final class Flags {
|
|||||||
*/
|
*/
|
||||||
public static final Flag VISITOR_KEEP_INVENTORY = new Flag.Builder("VISITOR_KEEP_INVENTORY", Material.TOTEM_OF_UNDYING).listener(new VisitorKeepInventoryListener()).type(Type.WORLD_SETTING).defaultSetting(false).build();
|
public static final Flag VISITOR_KEEP_INVENTORY = new Flag.Builder("VISITOR_KEEP_INVENTORY", Material.TOTEM_OF_UNDYING).listener(new VisitorKeepInventoryListener()).type(Type.WORLD_SETTING).defaultSetting(false).build();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggles whether island visitors can trigger to start a raid on another player's island.
|
||||||
|
* @since 1.21.0
|
||||||
|
* @see VisitorsStartingRaidListener
|
||||||
|
*/
|
||||||
|
public static final Flag VISITOR_TRIGGER_RAID = new Flag.Builder("VISITOR_TRIGGER_RAID", Material.RAVAGER_SPAWN_EGG).listener(new VisitorsStartingRaidListener()).type(Type.WORLD_SETTING).defaultSetting(true).build();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggles whether entities can teleport between dimensions using portals.
|
||||||
|
* @since 1.21.0
|
||||||
|
* @see world.bentobox.bentobox.listeners.teleports.EntityTeleportListener
|
||||||
|
*/
|
||||||
|
public static final Flag ENTITY_PORTAL_TELEPORT = new Flag.Builder("ENTITY_PORTAL_TELEPORT", Material.ENDER_EYE).type(Type.WORLD_SETTING).defaultSetting(false).build();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a list of all the Flag instances contained in this class using reflection.
|
* Provides a list of all the Flag instances contained in this class using reflection.
|
||||||
* Deprecated Flags are ignored.
|
* Deprecated Flags are ignored.
|
||||||
|
@ -7,6 +7,7 @@ import java.io.FileOutputStream;
|
|||||||
import java.io.FileReader;
|
import java.io.FileReader;
|
||||||
import java.io.FileWriter;
|
import java.io.FileWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
@ -86,24 +87,24 @@ public class BlueprintClipboardManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a blueprint
|
* Loads a blueprint
|
||||||
* @param fileName - the filename without the suffix
|
* @param fileName - the sanitized filename without the suffix
|
||||||
* @return the blueprint
|
* @return the blueprint
|
||||||
* @throws IOException exception if there's an issue loading or unzipping
|
* @throws IOException exception if there's an issue loading or unzipping
|
||||||
*/
|
*/
|
||||||
public Blueprint loadBlueprint(String fileName) throws IOException {
|
public Blueprint loadBlueprint(String fileName) throws IOException {
|
||||||
File zipFile = new File(blueprintFolder, BlueprintsManager.sanitizeFileName(fileName) + BlueprintsManager.BLUEPRINT_SUFFIX);
|
File zipFile = new File(blueprintFolder, fileName + BlueprintsManager.BLUEPRINT_SUFFIX);
|
||||||
if (!zipFile.exists()) {
|
if (!zipFile.exists()) {
|
||||||
plugin.logError(LOAD_ERROR + zipFile.getName());
|
plugin.logError(LOAD_ERROR + zipFile.getName());
|
||||||
throw new IOException(LOAD_ERROR + zipFile.getName());
|
throw new IOException(LOAD_ERROR + zipFile.getName());
|
||||||
}
|
}
|
||||||
unzip(zipFile.getCanonicalPath());
|
unzip(zipFile.getCanonicalPath());
|
||||||
File file = new File(blueprintFolder, BlueprintsManager.sanitizeFileName(fileName));
|
File file = new File(blueprintFolder, fileName);
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
plugin.logError(LOAD_ERROR + file.getName());
|
plugin.logError(LOAD_ERROR + file.getName());
|
||||||
throw new IOException(LOAD_ERROR + file.getName() + " temp file");
|
throw new IOException(LOAD_ERROR + file.getName() + " temp file");
|
||||||
}
|
}
|
||||||
Blueprint bp;
|
Blueprint bp;
|
||||||
try (FileReader fr = new FileReader(file)) {
|
try (FileReader fr = new FileReader(file, StandardCharsets.UTF_8)) {
|
||||||
bp = gson.fromJson(fr, Blueprint.class);
|
bp = gson.fromJson(fr, Blueprint.class);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
plugin.logError("Blueprint has JSON error: " + zipFile.getName());
|
plugin.logError("Blueprint has JSON error: " + zipFile.getName());
|
||||||
@ -114,7 +115,7 @@ public class BlueprintClipboardManager {
|
|||||||
if (bp.getBedrock() == null) {
|
if (bp.getBedrock() == null) {
|
||||||
bp.setBedrock(new Vector(bp.getxSize() / 2, bp.getySize() / 2, bp.getzSize() / 2));
|
bp.setBedrock(new Vector(bp.getxSize() / 2, bp.getySize() / 2, bp.getzSize() / 2));
|
||||||
bp.getBlocks().put(bp.getBedrock(), new BlueprintBlock(Material.BEDROCK.createBlockData().getAsString()));
|
bp.getBlocks().put(bp.getBedrock(), new BlueprintBlock(Material.BEDROCK.createBlockData().getAsString()));
|
||||||
plugin.logWarning("Blueprint " + BlueprintsManager.sanitizeFileName(fileName) + BlueprintsManager.BLUEPRINT_SUFFIX + " had no bedrock block in it so one was added automatically in the center. You should check it.");
|
plugin.logWarning("Blueprint " + fileName + BlueprintsManager.BLUEPRINT_SUFFIX + " had no bedrock block in it so one was added automatically in the center. You should check it.");
|
||||||
}
|
}
|
||||||
return bp;
|
return bp;
|
||||||
}
|
}
|
||||||
@ -130,7 +131,7 @@ public class BlueprintClipboardManager {
|
|||||||
load(fileName);
|
load(fileName);
|
||||||
} catch (IOException e1) {
|
} catch (IOException e1) {
|
||||||
user.sendMessage("commands.admin.blueprint.could-not-load");
|
user.sendMessage("commands.admin.blueprint.could-not-load");
|
||||||
plugin.logError("Could not load blueprint file: " + BlueprintsManager.sanitizeFileName(fileName) + BlueprintsManager.BLUEPRINT_SUFFIX + " " + e1.getMessage());
|
plugin.logError("Could not load blueprint file: " + fileName + BlueprintsManager.BLUEPRINT_SUFFIX + " " + e1.getMessage());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
user.sendMessage("general.success");
|
user.sendMessage("general.success");
|
||||||
@ -143,14 +144,20 @@ public class BlueprintClipboardManager {
|
|||||||
* @param newName - new name of this blueprint
|
* @param newName - new name of this blueprint
|
||||||
* @return - true if successful, false if error
|
* @return - true if successful, false if error
|
||||||
*/
|
*/
|
||||||
public boolean save(User user, String newName) {
|
public boolean save(User user, String newName, String displayName)
|
||||||
if (clipboard.getBlueprint() != null) {
|
{
|
||||||
clipboard.getBlueprint().setName(newName);
|
if (this.clipboard.isFull())
|
||||||
if (saveBlueprint(clipboard.getBlueprint())) {
|
{
|
||||||
|
this.clipboard.getBlueprint().setName(newName);
|
||||||
|
this.clipboard.getBlueprint().setDisplayName(displayName);
|
||||||
|
|
||||||
|
if (this.saveBlueprint(this.clipboard.getBlueprint()))
|
||||||
|
{
|
||||||
user.sendMessage("general.success");
|
user.sendMessage("general.success");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
user.sendMessage("commands.admin.blueprint.could-not-save", "[message]", "Could not save temp blueprint file.");
|
user.sendMessage("commands.admin.blueprint.could-not-save", "[message]", "Could not save temp blueprint file.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -165,9 +172,9 @@ public class BlueprintClipboardManager {
|
|||||||
plugin.logError("Blueprint name was empty - could not save it");
|
plugin.logError("Blueprint name was empty - could not save it");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
File file = new File(blueprintFolder, BlueprintsManager.sanitizeFileName(blueprint.getName()));
|
File file = new File(blueprintFolder, blueprint.getName());
|
||||||
String toStore = gson.toJson(blueprint, Blueprint.class);
|
String toStore = gson.toJson(blueprint, Blueprint.class);
|
||||||
try (FileWriter fileWriter = new FileWriter(file)) {
|
try (FileWriter fileWriter = new FileWriter(file, StandardCharsets.UTF_8)) {
|
||||||
fileWriter.write(toStore);
|
fileWriter.write(toStore);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
plugin.logError("Could not save temporary blueprint file: " + file.getName());
|
plugin.logError("Could not save temporary blueprint file: " + file.getName());
|
||||||
|
@ -1,22 +1,11 @@
|
|||||||
package world.bentobox.bentobox.managers;
|
package world.bentobox.bentobox.managers;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.*;
|
||||||
import java.io.FileReader;
|
|
||||||
import java.io.FileWriter;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.reflect.ParameterizedType;
|
import java.lang.reflect.ParameterizedType;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.EnumMap;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.jar.JarFile;
|
import java.util.jar.JarFile;
|
||||||
@ -216,13 +205,24 @@ public class BlueprintsManager {
|
|||||||
bpf.mkdirs();
|
bpf.mkdirs();
|
||||||
}
|
}
|
||||||
boolean loaded = false;
|
boolean loaded = false;
|
||||||
File[] bundles = bpf.listFiles((dir, name) -> name.toLowerCase(Locale.ENGLISH).endsWith(BLUEPRINT_BUNDLE_SUFFIX));
|
File[] bundles = bpf.listFiles((dir, name) -> name.endsWith(BLUEPRINT_BUNDLE_SUFFIX));
|
||||||
|
|
||||||
if (bundles == null || bundles.length == 0) {
|
if (bundles == null || bundles.length == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (File file : bundles) {
|
for (File file : bundles) {
|
||||||
try {
|
|
||||||
BlueprintBundle bb = gson.fromJson(new FileReader(file), BlueprintBundle.class);
|
try (FileReader fileReader = new FileReader(file, StandardCharsets.UTF_8))
|
||||||
|
{
|
||||||
|
if (!file.getName().equals(Util.sanitizeInput(file.getName())))
|
||||||
|
{
|
||||||
|
// fail on all blueprints with incorrect names.
|
||||||
|
throw new InputMismatchException(file.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
BlueprintBundle bb = gson.fromJson(fileReader, BlueprintBundle.class);
|
||||||
|
|
||||||
if (bb != null) {
|
if (bb != null) {
|
||||||
// Make sure there is no existing bundle with the same uniqueId
|
// Make sure there is no existing bundle with the same uniqueId
|
||||||
if (blueprintBundles.get(addon).stream().noneMatch(bundle -> bundle.getUniqueId().equals(bb.getUniqueId()))) {
|
if (blueprintBundles.get(addon).stream().noneMatch(bundle -> bundle.getUniqueId().equals(bb.getUniqueId()))) {
|
||||||
@ -299,13 +299,17 @@ public class BlueprintsManager {
|
|||||||
plugin.logError("There is no blueprint folder for addon " + addon.getDescription().getName());
|
plugin.logError("There is no blueprint folder for addon " + addon.getDescription().getName());
|
||||||
bpf.mkdirs();
|
bpf.mkdirs();
|
||||||
}
|
}
|
||||||
File[] bps = bpf.listFiles((dir, name) -> name.toLowerCase(Locale.ENGLISH).endsWith(BLUEPRINT_SUFFIX));
|
File[] bps = bpf.listFiles((dir, name) -> name.endsWith(BLUEPRINT_SUFFIX));
|
||||||
|
|
||||||
if (bps == null || bps.length == 0) {
|
if (bps == null || bps.length == 0) {
|
||||||
plugin.logError("No blueprints found for " + addon.getDescription().getName());
|
plugin.logError("No blueprints found for " + addon.getDescription().getName());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (File file : bps) {
|
for (File file : bps) {
|
||||||
String fileName = file.getName().substring(0, file.getName().length() - BLUEPRINT_SUFFIX.length());
|
|
||||||
|
// Input sanitization is required for weirdos that edit files manually.
|
||||||
|
String fileName = Util.sanitizeInput(file.getName().substring(0, file.getName().length() - BLUEPRINT_SUFFIX.length()));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Blueprint bp = new BlueprintClipboardManager(plugin, bpf).loadBlueprint(fileName);
|
Blueprint bp = new BlueprintClipboardManager(plugin, bpf).loadBlueprint(fileName);
|
||||||
bp.setName(fileName);
|
bp.setName(fileName);
|
||||||
@ -354,9 +358,9 @@ public class BlueprintsManager {
|
|||||||
if (!bpf.exists()) {
|
if (!bpf.exists()) {
|
||||||
bpf.mkdirs();
|
bpf.mkdirs();
|
||||||
}
|
}
|
||||||
File fileName = new File(bpf, sanitizeFileName(bb.getUniqueId()) + BLUEPRINT_BUNDLE_SUFFIX);
|
File fileName = new File(bpf, bb.getUniqueId() + BLUEPRINT_BUNDLE_SUFFIX);
|
||||||
String toStore = gson.toJson(bb, BlueprintBundle.class);
|
String toStore = gson.toJson(bb, BlueprintBundle.class);
|
||||||
try (FileWriter fileWriter = new FileWriter(fileName)) {
|
try (FileWriter fileWriter = new FileWriter(fileName, StandardCharsets.UTF_8)) {
|
||||||
fileWriter.write(toStore);
|
fileWriter.write(toStore);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
plugin.logError("Could not save blueprint bundle file: " + e.getMessage());
|
plugin.logError("Could not save blueprint bundle file: " + e.getMessage());
|
||||||
@ -364,20 +368,6 @@ public class BlueprintsManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sanitizes a filename as much as possible retaining the original name
|
|
||||||
* @param name - filename to sanitize
|
|
||||||
* @return sanitized name
|
|
||||||
*/
|
|
||||||
public static String sanitizeFileName(String name) {
|
|
||||||
return name
|
|
||||||
.chars()
|
|
||||||
.mapToObj(i -> (char) i)
|
|
||||||
.map(c -> Character.isWhitespace(c) ? '_' : c)
|
|
||||||
.filter(c -> Character.isLetterOrDigit(c) || c == '-' || c == '_')
|
|
||||||
.map(String::valueOf)
|
|
||||||
.collect(Collectors.joining());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves all the blueprint bundles
|
* Saves all the blueprint bundles
|
||||||
@ -405,26 +395,35 @@ public class BlueprintsManager {
|
|||||||
* @param name name of the Blueprint to delete
|
* @param name name of the Blueprint to delete
|
||||||
* @since 1.9.0
|
* @since 1.9.0
|
||||||
*/
|
*/
|
||||||
public void deleteBlueprint(GameModeAddon addon, String name) {
|
public void deleteBlueprint(GameModeAddon addon, String name)
|
||||||
List<Blueprint> addonBlueprints = blueprints.get(addon);
|
{
|
||||||
|
List<Blueprint> addonBlueprints = this.blueprints.get(addon);
|
||||||
Iterator<Blueprint> it = addonBlueprints.iterator();
|
Iterator<Blueprint> it = addonBlueprints.iterator();
|
||||||
while (it.hasNext()) {
|
|
||||||
Blueprint b = it.next();
|
|
||||||
if (b.getName().equalsIgnoreCase(name)) {
|
|
||||||
it.remove();
|
|
||||||
blueprints.put(addon, addonBlueprints);
|
|
||||||
|
|
||||||
File file = new File(getBlueprintsFolder(addon), b.getName() + BLUEPRINT_SUFFIX);
|
while (it.hasNext())
|
||||||
|
{
|
||||||
|
Blueprint b = it.next();
|
||||||
|
|
||||||
|
if (b.getName().equalsIgnoreCase(name))
|
||||||
|
{
|
||||||
|
it.remove();
|
||||||
|
|
||||||
|
File file = new File(this.getBlueprintsFolder(addon), b.getName() + BLUEPRINT_SUFFIX);
|
||||||
|
|
||||||
// Delete the file
|
// Delete the file
|
||||||
try {
|
try
|
||||||
|
{
|
||||||
Files.deleteIfExists(file.toPath());
|
Files.deleteIfExists(file.toPath());
|
||||||
} catch (IOException e) {
|
}
|
||||||
plugin.logError("Could not delete Blueprint " + e.getLocalizedMessage());
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
this.plugin.logError("Could not delete Blueprint " + e.getLocalizedMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Paste the islands to world
|
* Paste the islands to world
|
||||||
*
|
*
|
||||||
@ -450,7 +449,7 @@ public class BlueprintsManager {
|
|||||||
plugin.logError("Tried to paste '" + name + "' but the bundle is not loaded!");
|
plugin.logError("Tried to paste '" + name + "' but the bundle is not loaded!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
BlueprintBundle bb = getBlueprintBundles(addon).get(name.toLowerCase(Locale.ENGLISH));
|
BlueprintBundle bb = getBlueprintBundles(addon).get(name.toLowerCase());
|
||||||
if (!blueprints.containsKey(addon) || blueprints.get(addon).isEmpty()) {
|
if (!blueprints.containsKey(addon) || blueprints.get(addon).isEmpty()) {
|
||||||
plugin.logError("No blueprints loaded for bundle '" + name + "'!");
|
plugin.logError("No blueprints loaded for bundle '" + name + "'!");
|
||||||
return false;
|
return false;
|
||||||
@ -467,7 +466,7 @@ public class BlueprintsManager {
|
|||||||
// Paste
|
// Paste
|
||||||
if (bp != null) {
|
if (bp != null) {
|
||||||
new BlueprintPaster(plugin, bp, addon.getOverWorld(), island).paste().thenAccept(b -> pasteNether(addon, bb, island).thenAccept(b2 ->
|
new BlueprintPaster(plugin, bp, addon.getOverWorld(), island).paste().thenAccept(b -> pasteNether(addon, bb, island).thenAccept(b2 ->
|
||||||
pasteEnd(addon, bb, island).thenAccept(b3 -> Bukkit.getScheduler().runTask(plugin, task))));
|
pasteEnd(addon, bb, island).thenAccept(message -> sendMessage(island)).thenAccept(b3 -> Bukkit.getScheduler().runTask(plugin, task))));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@ -500,6 +499,18 @@ public class BlueprintsManager {
|
|||||||
return CompletableFuture.completedFuture(false);
|
return CompletableFuture.completedFuture(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method just sends a message to the island owner that island creating is completed.
|
||||||
|
* @param island Island which owner must receive a message.
|
||||||
|
*/
|
||||||
|
private void sendMessage(Island island) {
|
||||||
|
if (island != null && island.getOwner() != null) {
|
||||||
|
final Optional<User> owner = Optional.of(island).map(i -> User.getInstance(i.getOwner()));
|
||||||
|
owner.ifPresent(user -> user.sendMessage("commands.island.create.pasting.done"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate if the bundle name is valid or not
|
* Validate if the bundle name is valid or not
|
||||||
*
|
*
|
||||||
@ -511,7 +522,7 @@ public class BlueprintsManager {
|
|||||||
if (name == null) {
|
if (name == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (blueprintBundles.containsKey(addon) && getBlueprintBundles(addon).containsKey(name.toLowerCase(Locale.ENGLISH))) {
|
if (blueprintBundles.containsKey(addon) && getBlueprintBundles(addon).containsKey(name)) {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@ -543,7 +554,7 @@ public class BlueprintsManager {
|
|||||||
// Permission
|
// Permission
|
||||||
String permission = addon.getPermissionPrefix() + "island.create." + name;
|
String permission = addon.getPermissionPrefix() + "island.create." + name;
|
||||||
// Get Blueprint bundle
|
// Get Blueprint bundle
|
||||||
BlueprintBundle bb = getBlueprintBundles((GameModeAddon) addon).get(name.toLowerCase(Locale.ENGLISH));
|
BlueprintBundle bb = getBlueprintBundles((GameModeAddon) addon).get(name.toLowerCase());
|
||||||
if (bb == null || (bb.isRequirePermission() && !name.equals(DEFAULT_BUNDLE_NAME) && !user.hasPermission(permission))) {
|
if (bb == null || (bb.isRequirePermission() && !name.equals(DEFAULT_BUNDLE_NAME) && !user.hasPermission(permission))) {
|
||||||
user.sendMessage("general.errors.no-permission", TextVariables.PERMISSION, permission);
|
user.sendMessage("general.errors.no-permission", TextVariables.PERMISSION, permission);
|
||||||
return false;
|
return false;
|
||||||
@ -562,7 +573,7 @@ public class BlueprintsManager {
|
|||||||
blueprintBundles.get(addon).removeIf(k -> k.getUniqueId().equals(bb.getUniqueId()));
|
blueprintBundles.get(addon).removeIf(k -> k.getUniqueId().equals(bb.getUniqueId()));
|
||||||
}
|
}
|
||||||
File bpf = getBlueprintsFolder(addon);
|
File bpf = getBlueprintsFolder(addon);
|
||||||
File fileName = new File(bpf, sanitizeFileName(bb.getUniqueId()) + BLUEPRINT_BUNDLE_SUFFIX);
|
File fileName = new File(bpf, bb.getUniqueId() + BLUEPRINT_BUNDLE_SUFFIX);
|
||||||
try {
|
try {
|
||||||
Files.deleteIfExists(fileName.toPath());
|
Files.deleteIfExists(fileName.toPath());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@ -576,25 +587,39 @@ public class BlueprintsManager {
|
|||||||
* @param addon - Game Mode Addon
|
* @param addon - Game Mode Addon
|
||||||
* @param bp - blueprint
|
* @param bp - blueprint
|
||||||
* @param name - new name
|
* @param name - new name
|
||||||
|
* @param displayName - display name for blueprint
|
||||||
*/
|
*/
|
||||||
public void renameBlueprint(GameModeAddon addon, Blueprint bp, String name) {
|
public void renameBlueprint(GameModeAddon addon, Blueprint bp, String name, String displayName)
|
||||||
if (bp.getName().equalsIgnoreCase(name)) {
|
{
|
||||||
|
if (bp.getName().equalsIgnoreCase(name))
|
||||||
|
{
|
||||||
// If the name is the same, do not do anything
|
// If the name is the same, do not do anything
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
File bpf = getBlueprintsFolder(addon);
|
|
||||||
// Get the filename
|
|
||||||
File fileName = new File(bpf, sanitizeFileName(bp.getName()) + BLUEPRINT_SUFFIX);
|
|
||||||
// Delete the old file
|
|
||||||
try {
|
|
||||||
Files.deleteIfExists(fileName.toPath());
|
|
||||||
} catch (IOException e) {
|
|
||||||
plugin.logError("Could not delete old Blueprint " + e.getLocalizedMessage());
|
|
||||||
}
|
|
||||||
// Set new name
|
|
||||||
bp.setName(name.toLowerCase(Locale.ENGLISH));
|
|
||||||
// Save it
|
|
||||||
saveBlueprint(addon, bp);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
File bpf = this.getBlueprintsFolder(addon);
|
||||||
|
// Get the filename
|
||||||
|
File fileName = new File(bpf, bp.getName() + BLUEPRINT_SUFFIX);
|
||||||
|
// Delete the old file
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Files.deleteIfExists(fileName.toPath());
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
this.plugin.logError("Could not delete old Blueprint " + e.getLocalizedMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove blueprint from the blueprints.
|
||||||
|
this.blueprints.get(addon).remove(bp);
|
||||||
|
|
||||||
|
// Set new name
|
||||||
|
bp.setName(name);
|
||||||
|
bp.setDisplayName(displayName);
|
||||||
|
|
||||||
|
// Save it
|
||||||
|
this.saveBlueprint(addon, bp);
|
||||||
|
this.addBlueprint(addon, bp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,10 +171,13 @@ public class IslandWorldManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set default island settings
|
// Set default island settings
|
||||||
plugin.getFlagsManager().getFlags().stream().filter(f -> f.getType().equals(Flag.Type.PROTECTION))
|
plugin.getFlagsManager().getFlags().stream().
|
||||||
.forEach(f -> settings.getDefaultIslandFlags().putIfAbsent(f, f.getDefaultRank()));
|
filter(f -> f.getType().equals(Flag.Type.PROTECTION)).
|
||||||
plugin.getFlagsManager().getFlags().stream().filter(f -> f.getType().equals(Flag.Type.SETTING))
|
forEach(f -> settings.getDefaultIslandFlagNames().putIfAbsent(f.getID(), f.getDefaultRank()));
|
||||||
.forEach(f -> settings.getDefaultIslandSettings().putIfAbsent(f, f.getDefaultRank()));
|
plugin.getFlagsManager().getFlags().stream().
|
||||||
|
filter(f -> f.getType().equals(Flag.Type.SETTING)).
|
||||||
|
forEach(f -> settings.getDefaultIslandSettingNames().putIfAbsent(f.getID(), f.getDefaultRank()));
|
||||||
|
|
||||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||||
// Set world difficulty
|
// Set world difficulty
|
||||||
Difficulty diff = settings.getDifficulty();
|
Difficulty diff = settings.getDifficulty();
|
||||||
@ -229,13 +232,13 @@ public class IslandWorldManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Value will always be greater than 0 and less than the world's max height.
|
* Value will always be greater than the world's min height and less than the world's max height.
|
||||||
* @return the islandHeight
|
* @return the islandHeight
|
||||||
*/
|
*/
|
||||||
public int getIslandHeight(@NonNull World world) {
|
public int getIslandHeight(@NonNull World world) {
|
||||||
if (gameModes.containsKey(world) && world.getMaxHeight() > 0) {
|
if (gameModes.containsKey(world) && world.getMaxHeight() > 0) {
|
||||||
return Math.min(world.getMaxHeight() - 1,
|
return Math.min(world.getMaxHeight() - 1,
|
||||||
Math.max(0, gameModes.get(world).getWorldSettings().getIslandHeight()));
|
Math.max(world.getMinHeight(), gameModes.get(world).getWorldSettings().getIslandHeight()));
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -477,7 +480,9 @@ public class IslandWorldManager {
|
|||||||
* @return Friendly name or world name if world is not a game world
|
* @return Friendly name or world name if world is not a game world
|
||||||
*/
|
*/
|
||||||
public String getFriendlyName(@NonNull World world) {
|
public String getFriendlyName(@NonNull World world) {
|
||||||
return gameModes.containsKey(world) ? gameModes.get(world).getWorldSettings().getFriendlyName() : world.getName();
|
return gameModes.containsKey(world) ?
|
||||||
|
gameModes.get(world).getWorldSettings().getFriendlyName() :
|
||||||
|
world.getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -699,8 +704,11 @@ public class IslandWorldManager {
|
|||||||
* @param world - world
|
* @param world - world
|
||||||
* @return default rank settings for new islands.
|
* @return default rank settings for new islands.
|
||||||
*/
|
*/
|
||||||
public Map<Flag, Integer> getDefaultIslandFlags(@NonNull World world) {
|
public Map<Flag, Integer> getDefaultIslandFlags(@NonNull World world)
|
||||||
return gameModes.containsKey(world) ? gameModes.get(world).getWorldSettings().getDefaultIslandFlags() : Collections.emptyMap();
|
{
|
||||||
|
return this.gameModes.containsKey(world) ?
|
||||||
|
this.convertToFlags(this.gameModes.get(world).getWorldSettings().getDefaultIslandFlagNames()) :
|
||||||
|
Collections.emptyMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -715,12 +723,14 @@ public class IslandWorldManager {
|
|||||||
/**
|
/**
|
||||||
* Return island setting defaults for world
|
* Return island setting defaults for world
|
||||||
*
|
*
|
||||||
* @param world
|
* @param world - world
|
||||||
* - world
|
|
||||||
* @return default settings for new islands
|
* @return default settings for new islands
|
||||||
*/
|
*/
|
||||||
public Map<Flag, Integer> getDefaultIslandSettings(@NonNull World world) {
|
public Map<Flag, Integer> getDefaultIslandSettings(@NonNull World world)
|
||||||
return gameModes.containsKey(world) ? gameModes.get(world).getWorldSettings().getDefaultIslandSettings() : Collections.emptyMap();
|
{
|
||||||
|
return this.gameModes.containsKey(world) ?
|
||||||
|
this.convertToFlags(this.gameModes.get(world).getWorldSettings().getDefaultIslandSettingNames()) :
|
||||||
|
Collections.emptyMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isUseOwnGenerator(@NonNull World world) {
|
public boolean isUseOwnGenerator(@NonNull World world) {
|
||||||
@ -921,4 +931,18 @@ public class IslandWorldManager {
|
|||||||
return gameModes.containsKey(world) && gameModes.get(world).getWorldSettings().isTeleportPlayerToIslandUponIslandCreation();
|
return gameModes.containsKey(world) && gameModes.get(world).getWorldSettings().isTeleportPlayerToIslandUponIslandCreation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method migrates Map of String, Integer to Map of Flag, Integer.
|
||||||
|
* @param flagNamesMap Map that contains flag names to their values.
|
||||||
|
* @return Flag objects to their values.
|
||||||
|
* @since 1.21
|
||||||
|
*/
|
||||||
|
private Map<Flag, Integer> convertToFlags(Map<String, Integer> flagNamesMap)
|
||||||
|
{
|
||||||
|
Map<Flag, Integer> flagMap = new HashMap<>();
|
||||||
|
flagNamesMap.forEach((key, value) ->
|
||||||
|
this.plugin.getFlagsManager().getFlag(key).ifPresent(flag -> flagMap.put(flag, value)));
|
||||||
|
return flagMap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1333,7 +1333,7 @@ public class IslandsManager {
|
|||||||
} else {
|
} else {
|
||||||
// Successful load
|
// Successful load
|
||||||
// Clean any null flags out of the island - these can occur for various reasons
|
// Clean any null flags out of the island - these can occur for various reasons
|
||||||
island.getFlags().keySet().removeIf(f -> f.getID().startsWith("NULL_FLAG"));
|
island.getFlags().keySet().removeIf(f -> f.startsWith("NULL_FLAG"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,49 +0,0 @@
|
|||||||
package world.bentobox.bentobox.nms;
|
|
||||||
|
|
||||||
import org.bukkit.Chunk;
|
|
||||||
import org.bukkit.block.data.BlockData;
|
|
||||||
import org.bukkit.generator.ChunkGenerator;
|
|
||||||
import org.bukkit.util.BoundingBox;
|
|
||||||
|
|
||||||
public interface NMSAbstraction {
|
|
||||||
/**
|
|
||||||
* Copy the chunk data and biome grid to the given chunk.
|
|
||||||
* @param chunk - chunk to copy to
|
|
||||||
* @param chunkData - chunk data to copy
|
|
||||||
* @param biomeGrid - biome grid to copy to
|
|
||||||
* @param limitBox - bounding box to limit the copying
|
|
||||||
*/
|
|
||||||
default void copyChunkDataToChunk(Chunk chunk, ChunkGenerator.ChunkData chunkData, ChunkGenerator.BiomeGrid biomeGrid, BoundingBox limitBox) {
|
|
||||||
double baseX = chunk.getX() << 4;
|
|
||||||
double baseZ = chunk.getZ() << 4;
|
|
||||||
int minHeight = chunk.getWorld().getMinHeight();
|
|
||||||
int maxHeight = chunk.getWorld().getMaxHeight();
|
|
||||||
for (int x = 0; x < 16; x++) {
|
|
||||||
for (int z = 0; z < 16; z++) {
|
|
||||||
if (!limitBox.contains(baseX + x, 0, baseZ + z)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (int y = minHeight; y < maxHeight; y++) {
|
|
||||||
setBlockInNativeChunk(chunk, x, y, z, chunkData.getBlockData(x, y, z), false);
|
|
||||||
// 3D biomes, 4 blocks separated
|
|
||||||
if (x % 4 == 0 && y % 4 == 0 && z % 4 == 0) {
|
|
||||||
chunk.getBlock(x, y, z).setBiome(biomeGrid.getBiome(x, y, z));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the low-level chunk information for the given block to the new block ID and data. This
|
|
||||||
* change will not be propagated to clients until the chunk is refreshed to them.
|
|
||||||
* @param chunk - chunk to be changed
|
|
||||||
* @param x - x coordinate within chunk 0 - 15
|
|
||||||
* @param y - y coordinate within chunk 0 - world height, e.g. 255
|
|
||||||
* @param z - z coordinate within chunk 0 - 15
|
|
||||||
* @param blockData - block data to set the block
|
|
||||||
* @param applyPhysics - apply physics or not
|
|
||||||
*/
|
|
||||||
void setBlockInNativeChunk(Chunk chunk, int x, int y, int z, BlockData blockData, boolean applyPhysics);
|
|
||||||
|
|
||||||
}
|
|
36
src/main/java/world/bentobox/bentobox/nms/PasteHandler.java
Normal file
36
src/main/java/world/bentobox/bentobox/nms/PasteHandler.java
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package world.bentobox.bentobox.nms;
|
||||||
|
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBlock;
|
||||||
|
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity;
|
||||||
|
import world.bentobox.bentobox.database.objects.Island;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper class for {@link world.bentobox.bentobox.blueprints.BlueprintPaster}
|
||||||
|
*/
|
||||||
|
public interface PasteHandler {
|
||||||
|
/**
|
||||||
|
* Create a future to paste the blocks
|
||||||
|
*
|
||||||
|
* @param island the island
|
||||||
|
* @param world the world
|
||||||
|
* @param blockMap the block map
|
||||||
|
* @return the future
|
||||||
|
*/
|
||||||
|
CompletableFuture<Void> pasteBlocks(Island island, World world, Map<Location, BlueprintBlock> blockMap);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a future to paste the entities
|
||||||
|
*
|
||||||
|
* @param island the island
|
||||||
|
* @param world the world
|
||||||
|
* @param entityMap the entities map
|
||||||
|
* @return the future
|
||||||
|
*/
|
||||||
|
CompletableFuture<Void> pasteEntities(Island island, World world, Map<Location, List<BlueprintEntity>> entityMap);
|
||||||
|
}
|
@ -0,0 +1,137 @@
|
|||||||
|
package world.bentobox.bentobox.nms;
|
||||||
|
|
||||||
|
import io.papermc.lib.PaperLib;
|
||||||
|
import org.bukkit.Chunk;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.block.data.BlockData;
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.generator.ChunkGenerator;
|
||||||
|
import org.bukkit.inventory.InventoryHolder;
|
||||||
|
import org.bukkit.scheduler.BukkitRunnable;
|
||||||
|
import org.bukkit.util.BoundingBox;
|
||||||
|
import world.bentobox.bentobox.BentoBox;
|
||||||
|
import world.bentobox.bentobox.api.addons.GameModeAddon;
|
||||||
|
import world.bentobox.bentobox.database.objects.IslandDeletion;
|
||||||
|
import world.bentobox.bentobox.util.MyBiomeGrid;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
public abstract class SimpleWorldRegenerator implements WorldRegenerator {
|
||||||
|
private final BentoBox plugin;
|
||||||
|
|
||||||
|
protected SimpleWorldRegenerator() {
|
||||||
|
this.plugin = BentoBox.getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the low-level chunk information for the given block to the new block ID and data. This
|
||||||
|
* change will not be propagated to clients until the chunk is refreshed to them.
|
||||||
|
*
|
||||||
|
* @param chunk - chunk to be changed
|
||||||
|
* @param x - x coordinate within chunk 0 - 15
|
||||||
|
* @param y - y coordinate within chunk 0 - world height, e.g. 255
|
||||||
|
* @param z - z coordinate within chunk 0 - 15
|
||||||
|
* @param blockData - block data to set the block
|
||||||
|
* @param applyPhysics - apply physics or not
|
||||||
|
*/
|
||||||
|
protected abstract void setBlockInNativeChunk(Chunk chunk, int x, int y, int z, BlockData blockData, boolean applyPhysics);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<Void> regenerate(GameModeAddon gm, IslandDeletion di, World world) {
|
||||||
|
CompletableFuture<Void> bigFuture = new CompletableFuture<>();
|
||||||
|
new BukkitRunnable() {
|
||||||
|
private int chunkX = di.getMinXChunk();
|
||||||
|
private int chunkZ = di.getMinZChunk();
|
||||||
|
CompletableFuture<Void> currentTask = CompletableFuture.completedFuture(null);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (!currentTask.isDone()) return;
|
||||||
|
if (isEnded(chunkX)) {
|
||||||
|
cancel();
|
||||||
|
bigFuture.complete(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<CompletableFuture<Void>> newTasks = new ArrayList<>();
|
||||||
|
for (int i = 0; i < plugin.getSettings().getDeleteSpeed(); i++) {
|
||||||
|
if (isEnded(chunkX)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
final int x = chunkX;
|
||||||
|
final int z = chunkZ;
|
||||||
|
newTasks.add(regenerateChunk(gm, di, world, x, z));
|
||||||
|
chunkZ++;
|
||||||
|
if (chunkZ > di.getMaxZChunk()) {
|
||||||
|
chunkZ = di.getMinZChunk();
|
||||||
|
chunkX++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentTask = CompletableFuture.allOf(newTasks.toArray(new CompletableFuture[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isEnded(int chunkX) {
|
||||||
|
return chunkX > di.getMaxXChunk();
|
||||||
|
}
|
||||||
|
}.runTaskTimer(plugin, 0L, 20L);
|
||||||
|
return bigFuture;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
private CompletableFuture<Void> regenerateChunk(GameModeAddon gm, IslandDeletion di, World world, int chunkX, int chunkZ) {
|
||||||
|
CompletableFuture<Chunk> chunkFuture = PaperLib.getChunkAtAsync(world, chunkX, chunkZ);
|
||||||
|
CompletableFuture<Void> invFuture = chunkFuture.thenAccept(chunk ->
|
||||||
|
Arrays.stream(chunk.getTileEntities()).filter(InventoryHolder.class::isInstance)
|
||||||
|
.filter(te -> di.inBounds(te.getLocation().getBlockX(), te.getLocation().getBlockZ()))
|
||||||
|
.forEach(te -> ((InventoryHolder) te).getInventory().clear())
|
||||||
|
);
|
||||||
|
CompletableFuture<Void> entitiesFuture = chunkFuture.thenAccept(chunk -> {
|
||||||
|
for (Entity e : chunk.getEntities()) {
|
||||||
|
if (!(e instanceof Player)) {
|
||||||
|
e.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
CompletableFuture<Chunk> copyFuture = chunkFuture.thenApply(chunk -> {
|
||||||
|
// Reset blocks
|
||||||
|
MyBiomeGrid grid = new MyBiomeGrid(chunk.getWorld().getEnvironment());
|
||||||
|
ChunkGenerator cg = gm.getDefaultWorldGenerator(chunk.getWorld().getName(), "delete");
|
||||||
|
// Will be null if use-own-generator is set to true
|
||||||
|
if (cg != null) {
|
||||||
|
ChunkGenerator.ChunkData cd = cg.generateChunkData(chunk.getWorld(), new Random(), chunk.getX(), chunk.getZ(), grid);
|
||||||
|
copyChunkDataToChunk(chunk, cd, grid, di.getBox());
|
||||||
|
}
|
||||||
|
return chunk;
|
||||||
|
});
|
||||||
|
CompletableFuture<Void> postCopyFuture = copyFuture.thenAccept(chunk -> {
|
||||||
|
// Remove all entities in chunk, including any dropped items as a result of clearing the blocks above
|
||||||
|
Arrays.stream(chunk.getEntities()).filter(e -> !(e instanceof Player) && di.inBounds(e.getLocation().getBlockX(), e.getLocation().getBlockZ())).forEach(Entity::remove);
|
||||||
|
});
|
||||||
|
return CompletableFuture.allOf(invFuture, entitiesFuture, postCopyFuture);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copyChunkDataToChunk(Chunk chunk, ChunkGenerator.ChunkData chunkData, ChunkGenerator.BiomeGrid biomeGrid, BoundingBox limitBox) {
|
||||||
|
double baseX = chunk.getX() << 4;
|
||||||
|
double baseZ = chunk.getZ() << 4;
|
||||||
|
int minHeight = chunk.getWorld().getMinHeight();
|
||||||
|
int maxHeight = chunk.getWorld().getMaxHeight();
|
||||||
|
for (int x = 0; x < 16; x++) {
|
||||||
|
for (int z = 0; z < 16; z++) {
|
||||||
|
if (!limitBox.contains(baseX + x, 0, baseZ + z)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (int y = minHeight; y < maxHeight; y++) {
|
||||||
|
setBlockInNativeChunk(chunk, x, y, z, chunkData.getBlockData(x, y, z), false);
|
||||||
|
// 3D biomes, 4 blocks separated
|
||||||
|
if (x % 4 == 0 && y % 4 == 0 && z % 4 == 0) {
|
||||||
|
chunk.getBlock(x, y, z).setBiome(biomeGrid.getBiome(x, y, z));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package world.bentobox.bentobox.nms;
|
||||||
|
|
||||||
|
import org.bukkit.World;
|
||||||
|
import world.bentobox.bentobox.api.addons.GameModeAddon;
|
||||||
|
import world.bentobox.bentobox.database.objects.IslandDeletion;
|
||||||
|
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A world generator used by {@link world.bentobox.bentobox.util.DeleteIslandChunks}
|
||||||
|
*/
|
||||||
|
public interface WorldRegenerator {
|
||||||
|
/**
|
||||||
|
* Create a future to regenerate the regions of the island.
|
||||||
|
*
|
||||||
|
* @param gm the game mode
|
||||||
|
* @param di the island deletion
|
||||||
|
* @param world the world
|
||||||
|
* @return the completable future
|
||||||
|
*/
|
||||||
|
CompletableFuture<Void> regenerate(GameModeAddon gm, IslandDeletion di, World world);
|
||||||
|
}
|
@ -1,20 +0,0 @@
|
|||||||
package world.bentobox.bentobox.nms.fallback;
|
|
||||||
|
|
||||||
import org.bukkit.Chunk;
|
|
||||||
import org.bukkit.block.data.BlockData;
|
|
||||||
|
|
||||||
import world.bentobox.bentobox.nms.NMSAbstraction;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author tastybento
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class NMSHandler implements NMSAbstraction {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setBlockInNativeChunk(Chunk chunk, int x, int y, int z, BlockData blockData, boolean applyPhysics) {
|
|
||||||
chunk.getBlock(x, y, z).setBlockData(blockData, applyPhysics);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,40 @@
|
|||||||
|
package world.bentobox.bentobox.nms.fallback;
|
||||||
|
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBlock;
|
||||||
|
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity;
|
||||||
|
import world.bentobox.bentobox.database.objects.Island;
|
||||||
|
import world.bentobox.bentobox.nms.PasteHandler;
|
||||||
|
import world.bentobox.bentobox.util.DefaultPasteUtil;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class PasteHandlerImpl implements PasteHandler {
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<Void> pasteBlocks(Island island, World world, Map<Location, BlueprintBlock> blockMap) {
|
||||||
|
return blockMap.entrySet().parallelStream()
|
||||||
|
.map(entry -> DefaultPasteUtil.setBlock(island, entry.getKey(), entry.getValue()))
|
||||||
|
.collect(
|
||||||
|
Collectors.collectingAndThen(
|
||||||
|
Collectors.toList(),
|
||||||
|
list -> CompletableFuture.allOf(list.toArray(new CompletableFuture[0]))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<Void> pasteEntities(Island island, World world, Map<Location, List<BlueprintEntity>> entityMap) {
|
||||||
|
return entityMap.entrySet().parallelStream()
|
||||||
|
.map(entry -> DefaultPasteUtil.setEntity(island, entry.getKey(), entry.getValue()))
|
||||||
|
.collect(
|
||||||
|
Collectors.collectingAndThen(
|
||||||
|
Collectors.toList(),
|
||||||
|
list -> CompletableFuture.allOf(list.toArray(new CompletableFuture[0]))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package world.bentobox.bentobox.nms.fallback;
|
||||||
|
|
||||||
|
import org.bukkit.Chunk;
|
||||||
|
import org.bukkit.block.data.BlockData;
|
||||||
|
|
||||||
|
import world.bentobox.bentobox.nms.SimpleWorldRegenerator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author tastybento
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class WorldRegeneratorImpl extends SimpleWorldRegenerator {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setBlockInNativeChunk(Chunk chunk, int x, int y, int z, BlockData blockData, boolean applyPhysics) {
|
||||||
|
chunk.getBlock(x, y, z).setBlockData(blockData, applyPhysics);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -1,19 +1,19 @@
|
|||||||
package world.bentobox.bentobox.nms.v1_18_R1;
|
package world.bentobox.bentobox.nms.v1_19_R1;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
import org.bukkit.craftbukkit.v1_18_R1.CraftWorld;
|
import org.bukkit.craftbukkit.v1_19_R1.CraftWorld;
|
||||||
import org.bukkit.craftbukkit.v1_18_R1.block.data.CraftBlockData;
|
import org.bukkit.craftbukkit.v1_19_R1.block.data.CraftBlockData;
|
||||||
|
|
||||||
import net.minecraft.core.BlockPosition;
|
import net.minecraft.core.BlockPosition;
|
||||||
import net.minecraft.world.level.World;
|
import net.minecraft.world.level.World;
|
||||||
import net.minecraft.world.level.block.state.IBlockData;
|
import net.minecraft.world.level.block.state.IBlockData;
|
||||||
import net.minecraft.world.level.chunk.Chunk;
|
import net.minecraft.world.level.chunk.Chunk;
|
||||||
import world.bentobox.bentobox.nms.NMSAbstraction;
|
import world.bentobox.bentobox.nms.SimpleWorldRegenerator;
|
||||||
|
|
||||||
|
|
||||||
public class NMSHandler implements NMSAbstraction {
|
public class WorldRegeneratorImpl extends SimpleWorldRegenerator {
|
||||||
|
|
||||||
private static final IBlockData AIR = ((CraftBlockData) Bukkit.createBlockData(Material.AIR)).getState();
|
private static final IBlockData AIR = ((CraftBlockData) Bukkit.createBlockData(Material.AIR)).getState();
|
||||||
|
|
238
src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java
Normal file
238
src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
package world.bentobox.bentobox.util;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.block.*;
|
||||||
|
import org.bukkit.block.data.BlockData;
|
||||||
|
import org.bukkit.block.data.type.WallSign;
|
||||||
|
import org.bukkit.entity.LivingEntity;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.inventory.Inventory;
|
||||||
|
import org.bukkit.inventory.InventoryHolder;
|
||||||
|
import world.bentobox.bentobox.BentoBox;
|
||||||
|
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||||
|
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.database.objects.Island;
|
||||||
|
import world.bentobox.bentobox.nms.PasteHandler;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A utility class for {@link PasteHandler}
|
||||||
|
*
|
||||||
|
* @author tastybento
|
||||||
|
*/
|
||||||
|
public class DefaultPasteUtil {
|
||||||
|
private static final String MINECRAFT = "minecraft:";
|
||||||
|
private static final Map<String, String> BLOCK_CONVERSION = Map.of("sign", "oak_sign", "wall_sign", "oak_wall_sign");
|
||||||
|
private static final BentoBox plugin;
|
||||||
|
|
||||||
|
static {
|
||||||
|
plugin = BentoBox.getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the block to the location
|
||||||
|
*
|
||||||
|
* @param island - island
|
||||||
|
* @param location - location
|
||||||
|
* @param bpBlock - blueprint block
|
||||||
|
*/
|
||||||
|
public static CompletableFuture<Void> setBlock(Island island, Location location, BlueprintBlock bpBlock) {
|
||||||
|
return Util.getChunkAtAsync(location).thenRun(() -> {
|
||||||
|
Block block = location.getBlock();
|
||||||
|
// Set the block data - default is AIR
|
||||||
|
BlockData bd = createBlockData(bpBlock);
|
||||||
|
block.setBlockData(bd, false);
|
||||||
|
setBlockState(island, block, bpBlock);
|
||||||
|
// Set biome
|
||||||
|
if (bpBlock.getBiome() != null) {
|
||||||
|
block.setBiome(bpBlock.getBiome());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a block data from the blueprint
|
||||||
|
*
|
||||||
|
* @param block - blueprint block
|
||||||
|
* @return the block data
|
||||||
|
*/
|
||||||
|
public static BlockData createBlockData(BlueprintBlock block) {
|
||||||
|
try {
|
||||||
|
return Bukkit.createBlockData(block.getBlockData());
|
||||||
|
} catch (Exception e) {
|
||||||
|
return convertBlockData(block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert the blueprint to block data
|
||||||
|
*
|
||||||
|
* @param block - the blueprint block
|
||||||
|
* @return the block data
|
||||||
|
*/
|
||||||
|
public static BlockData convertBlockData(BlueprintBlock block) {
|
||||||
|
BlockData blockData = Bukkit.createBlockData(Material.AIR);
|
||||||
|
try {
|
||||||
|
for (Map.Entry<String, String> en : BLOCK_CONVERSION.entrySet()) {
|
||||||
|
if (block.getBlockData().startsWith(MINECRAFT + en.getKey())) {
|
||||||
|
blockData = Bukkit.createBlockData(block.getBlockData().replace(MINECRAFT + en.getKey(), MINECRAFT + en.getValue()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
// This may happen if the block type is no longer supported by the server
|
||||||
|
plugin.logWarning("Blueprint references materials not supported on this server version.");
|
||||||
|
plugin.logWarning("Load blueprint manually, check and save to fix for this server version.");
|
||||||
|
plugin.logWarning("Failed block data: " + block.getBlockData());
|
||||||
|
}
|
||||||
|
return blockData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles signs, chests and mob spawner blocks
|
||||||
|
*
|
||||||
|
* @param island - island
|
||||||
|
* @param block - block
|
||||||
|
* @param bpBlock - config
|
||||||
|
*/
|
||||||
|
public static void setBlockState(Island island, Block block, BlueprintBlock bpBlock) {
|
||||||
|
// Get the block state
|
||||||
|
BlockState bs = block.getState();
|
||||||
|
// Signs
|
||||||
|
if (bs instanceof Sign) {
|
||||||
|
writeSign(island, block, bpBlock.getSignLines(), bpBlock.isGlowingText());
|
||||||
|
}
|
||||||
|
// Chests, in general
|
||||||
|
else if (bs instanceof InventoryHolder holder) {
|
||||||
|
Inventory ih = holder.getInventory();
|
||||||
|
// Double chests are pasted as two blocks so inventory is filled twice.
|
||||||
|
// This code stops over-filling for the first block.
|
||||||
|
bpBlock.getInventory().forEach(ih::setItem);
|
||||||
|
}
|
||||||
|
// Mob spawners
|
||||||
|
else if (bs instanceof CreatureSpawner spawner) {
|
||||||
|
setSpawner(spawner, bpBlock.getCreatureSpawner());
|
||||||
|
}
|
||||||
|
// Banners
|
||||||
|
else if (bs instanceof Banner banner && bpBlock.getBannerPatterns() != null) {
|
||||||
|
bpBlock.getBannerPatterns().removeIf(Objects::isNull);
|
||||||
|
banner.setPatterns(bpBlock.getBannerPatterns());
|
||||||
|
banner.update(true, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the spawner setting from the blueprint
|
||||||
|
*
|
||||||
|
* @param spawner - spawner
|
||||||
|
* @param s - blueprint spawner
|
||||||
|
*/
|
||||||
|
public static void setSpawner(CreatureSpawner spawner, BlueprintCreatureSpawner s) {
|
||||||
|
spawner.setSpawnedType(s.getSpawnedType());
|
||||||
|
spawner.setMaxNearbyEntities(s.getMaxNearbyEntities());
|
||||||
|
spawner.setMaxSpawnDelay(s.getMaxSpawnDelay());
|
||||||
|
spawner.setMinSpawnDelay(s.getMinSpawnDelay());
|
||||||
|
spawner.setDelay(s.getDelay());
|
||||||
|
spawner.setRequiredPlayerRange(s.getRequiredPlayerRange());
|
||||||
|
spawner.setSpawnRange(s.getSpawnRange());
|
||||||
|
spawner.update(true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spawn the blueprint entities to the location
|
||||||
|
*
|
||||||
|
* @param island - island
|
||||||
|
* @param location - location
|
||||||
|
* @param list - blueprint entities
|
||||||
|
*/
|
||||||
|
public static CompletableFuture<Void> setEntity(Island island, Location location, List<BlueprintEntity> list) {
|
||||||
|
World world = location.getWorld();
|
||||||
|
assert world != null;
|
||||||
|
return Util.getChunkAtAsync(location).thenRun(() -> list.stream().filter(k -> k.getType() != null).forEach(k -> {
|
||||||
|
LivingEntity e = (LivingEntity) location.getWorld().spawnEntity(location, k.getType());
|
||||||
|
if (k.getCustomName() != null) {
|
||||||
|
String customName = k.getCustomName();
|
||||||
|
|
||||||
|
if (island != null) {
|
||||||
|
// Parse any placeholders in the entity's name, if the owner's connected (he should)
|
||||||
|
Optional<Player> owner = Optional.ofNullable(island.getOwner())
|
||||||
|
.map(User::getInstance)
|
||||||
|
.map(User::getPlayer);
|
||||||
|
if (owner.isPresent()) {
|
||||||
|
// Parse for the player's name first (in case placeholders might need it)
|
||||||
|
customName = customName.replace(TextVariables.NAME, owner.get().getName());
|
||||||
|
// Now parse the placeholders
|
||||||
|
customName = plugin.getPlaceholdersManager().replacePlaceholders(owner.get(), customName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actually set the custom name
|
||||||
|
e.setCustomName(customName);
|
||||||
|
}
|
||||||
|
k.configureEntity(e);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write the lines to the sign at the block
|
||||||
|
*
|
||||||
|
* @param island - island
|
||||||
|
* @param block - block
|
||||||
|
* @param lines - lines
|
||||||
|
* @param glow - is sign glowing?
|
||||||
|
*/
|
||||||
|
public static void writeSign(Island island, final Block block, final List<String> lines, boolean glow) {
|
||||||
|
BlockFace bf;
|
||||||
|
if (block.getType().name().contains("WALL_SIGN")) {
|
||||||
|
WallSign wallSign = (WallSign) block.getBlockData();
|
||||||
|
bf = wallSign.getFacing();
|
||||||
|
} else {
|
||||||
|
org.bukkit.block.data.type.Sign sign = (org.bukkit.block.data.type.Sign) block.getBlockData();
|
||||||
|
bf = sign.getRotation();
|
||||||
|
}
|
||||||
|
// Handle spawn sign
|
||||||
|
if (island != null && !lines.isEmpty() && lines.get(0).equalsIgnoreCase(TextVariables.SPAWN_HERE)) {
|
||||||
|
block.setType(Material.AIR);
|
||||||
|
// Orient to face same direction as sign
|
||||||
|
Location spawnPoint = new Location(block.getWorld(), block.getX() + 0.5D, block.getY(),
|
||||||
|
block.getZ() + 0.5D, Util.blockFaceToFloat(bf.getOppositeFace()), 30F);
|
||||||
|
island.setSpawnPoint(block.getWorld().getEnvironment(), spawnPoint);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Get the name of the player
|
||||||
|
String name = "";
|
||||||
|
if (island != null) {
|
||||||
|
name = plugin.getPlayers().getName(island.getOwner());
|
||||||
|
}
|
||||||
|
// Handle locale text for starting sign
|
||||||
|
org.bukkit.block.Sign s = (org.bukkit.block.Sign) block.getState();
|
||||||
|
// Sign text must be stored under the addon's name.sign.line0,1,2,3 in the yaml file
|
||||||
|
if (island != null && !lines.isEmpty() && lines.get(0).equalsIgnoreCase(TextVariables.START_TEXT)) {
|
||||||
|
// Get the addon that is operating in this world
|
||||||
|
String addonName = plugin.getIWM().getAddon(island.getWorld()).map(addon -> addon.getDescription().getName().toLowerCase(Locale.ENGLISH)).orElse("");
|
||||||
|
Optional<User> user = Optional.ofNullable(island.getOwner()).map(User::getInstance);
|
||||||
|
if (user.isPresent()) {
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
s.setLine(i, Util.translateColorCodes(plugin.getLocalesManager().getOrDefault(user.get(),
|
||||||
|
addonName + ".sign.line" + i, "").replace(TextVariables.NAME, name)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Just paste
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
s.setLine(i, lines.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.setGlowingText(glow);
|
||||||
|
// Update the sign
|
||||||
|
s.update();
|
||||||
|
}
|
||||||
|
}
|
@ -1,29 +1,16 @@
|
|||||||
package world.bentobox.bentobox.util;
|
package world.bentobox.bentobox.util;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Random;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.Chunk;
|
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.scheduler.BukkitRunnable;
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.bukkit.generator.ChunkGenerator;
|
|
||||||
import org.bukkit.generator.ChunkGenerator.ChunkData;
|
|
||||||
import org.bukkit.inventory.InventoryHolder;
|
|
||||||
import org.bukkit.scheduler.BukkitTask;
|
|
||||||
|
|
||||||
import io.papermc.lib.PaperLib;
|
|
||||||
import world.bentobox.bentobox.BentoBox;
|
import world.bentobox.bentobox.BentoBox;
|
||||||
import world.bentobox.bentobox.api.addons.GameModeAddon;
|
import world.bentobox.bentobox.api.addons.GameModeAddon;
|
||||||
import world.bentobox.bentobox.api.events.island.IslandEvent;
|
import world.bentobox.bentobox.api.events.island.IslandEvent;
|
||||||
import world.bentobox.bentobox.api.events.island.IslandEvent.Reason;
|
import world.bentobox.bentobox.api.events.island.IslandEvent.Reason;
|
||||||
import world.bentobox.bentobox.database.objects.IslandDeletion;
|
import world.bentobox.bentobox.database.objects.IslandDeletion;
|
||||||
import world.bentobox.bentobox.nms.NMSAbstraction;
|
import world.bentobox.bentobox.nms.WorldRegenerator;
|
||||||
|
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes islands chunk by chunk
|
* Deletes islands chunk by chunk
|
||||||
@ -37,16 +24,10 @@ public class DeleteIslandChunks {
|
|||||||
private final World netherWorld;
|
private final World netherWorld;
|
||||||
private final World endWorld;
|
private final World endWorld;
|
||||||
private final AtomicBoolean completed;
|
private final AtomicBoolean completed;
|
||||||
private final NMSAbstraction nms;
|
private final WorldRegenerator regenerator;
|
||||||
private int chunkX;
|
|
||||||
private int chunkZ;
|
|
||||||
private BukkitTask task;
|
|
||||||
private CompletableFuture<Void> currentTask = CompletableFuture.completedFuture(null);
|
|
||||||
|
|
||||||
public DeleteIslandChunks(BentoBox plugin, IslandDeletion di) {
|
public DeleteIslandChunks(BentoBox plugin, IslandDeletion di) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.chunkX = di.getMinXChunk();
|
|
||||||
this.chunkZ = di.getMinZChunk();
|
|
||||||
this.di = di;
|
this.di = di;
|
||||||
completed = new AtomicBoolean(false);
|
completed = new AtomicBoolean(false);
|
||||||
// Nether
|
// Nether
|
||||||
@ -61,9 +42,9 @@ public class DeleteIslandChunks {
|
|||||||
} else {
|
} else {
|
||||||
endWorld = null;
|
endWorld = null;
|
||||||
}
|
}
|
||||||
// NMS
|
// Regenerator
|
||||||
this.nms = Util.getNMS();
|
this.regenerator = Util.getRegenerator();
|
||||||
if (nms == null) {
|
if (regenerator == null) {
|
||||||
plugin.logError("Could not delete chunks because of NMS error");
|
plugin.logError("Could not delete chunks because of NMS error");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -75,37 +56,23 @@ public class DeleteIslandChunks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void regenerateChunks() {
|
private void regenerateChunks() {
|
||||||
// Run through all chunks of the islands and regenerate them.
|
CompletableFuture<Void> all = plugin.getIWM().getAddon(di.getWorld())
|
||||||
task = Bukkit.getScheduler().runTaskTimer(plugin, () -> {
|
.map(gm -> new CompletableFuture[]{
|
||||||
if (!currentTask.isDone()) return;
|
processWorld(gm, di.getWorld()), // Overworld
|
||||||
if (isEnded(chunkX)) {
|
processWorld(gm, netherWorld), // Nether
|
||||||
finish();
|
processWorld(gm, endWorld) // End
|
||||||
return;
|
})
|
||||||
}
|
.map(CompletableFuture::allOf)
|
||||||
List<CompletableFuture<Void>> newTasks = new ArrayList<>();
|
.orElseGet(() -> CompletableFuture.completedFuture(null));
|
||||||
for (int i = 0; i < plugin.getSettings().getDeleteSpeed(); i++) {
|
new BukkitRunnable() {
|
||||||
if (isEnded(chunkX)) {
|
@Override
|
||||||
break;
|
public void run() {
|
||||||
}
|
if (all.isDone()) {
|
||||||
final int x = chunkX;
|
finish();
|
||||||
final int z = chunkZ;
|
cancel();
|
||||||
plugin.getIWM().getAddon(di.getWorld()).ifPresent(gm -> {
|
|
||||||
newTasks.add(processChunk(gm, di.getWorld(), x, z)); // Overworld
|
|
||||||
newTasks.add(processChunk(gm, netherWorld, x, z)); // Nether
|
|
||||||
newTasks.add(processChunk(gm, endWorld, x, z)); // End
|
|
||||||
});
|
|
||||||
chunkZ++;
|
|
||||||
if (chunkZ > di.getMaxZChunk()) {
|
|
||||||
chunkZ = di.getMinZChunk();
|
|
||||||
chunkX++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
currentTask = CompletableFuture.allOf(newTasks.toArray(new CompletableFuture[0]));
|
}.runTaskTimer(plugin, 0, 20);
|
||||||
}, 0L, 20L);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isEnded(int chunkX) {
|
|
||||||
return chunkX > di.getMaxXChunk();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void finish() {
|
private void finish() {
|
||||||
@ -113,44 +80,16 @@ public class DeleteIslandChunks {
|
|||||||
IslandEvent.builder().deletedIslandInfo(di).reason(Reason.DELETED).build();
|
IslandEvent.builder().deletedIslandInfo(di).reason(Reason.DELETED).build();
|
||||||
// We're done
|
// We're done
|
||||||
completed.set(true);
|
completed.set(true);
|
||||||
task.cancel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private CompletableFuture<Void> processChunk(GameModeAddon gm, World world, int x, int z) {
|
private CompletableFuture<Void> processWorld(GameModeAddon gm, World world) {
|
||||||
if (world != null) {
|
if (world != null) {
|
||||||
return PaperLib.getChunkAtAsync(world, x, z).thenAccept(chunk -> regenerateChunk(gm, chunk, x, z));
|
return regenerator.regenerate(gm, di, world);
|
||||||
} else {
|
} else {
|
||||||
return CompletableFuture.completedFuture(null);
|
return CompletableFuture.completedFuture(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void regenerateChunk(GameModeAddon gm, Chunk chunk, int x, int z) {
|
|
||||||
// Clear all inventories
|
|
||||||
Arrays.stream(chunk.getTileEntities()).filter(InventoryHolder.class::isInstance)
|
|
||||||
.filter(te -> di.inBounds(te.getLocation().getBlockX(), te.getLocation().getBlockZ()))
|
|
||||||
.forEach(te -> ((InventoryHolder) te).getInventory().clear());
|
|
||||||
// Remove all entities
|
|
||||||
for (Entity e : chunk.getEntities()) {
|
|
||||||
if (!(e instanceof Player)) {
|
|
||||||
e.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Reset blocks
|
|
||||||
MyBiomeGrid grid = new MyBiomeGrid(chunk.getWorld().getEnvironment());
|
|
||||||
ChunkGenerator cg = gm.getDefaultWorldGenerator(chunk.getWorld().getName(), "delete");
|
|
||||||
// Will be null if use-own-generator is set to true
|
|
||||||
if (cg != null) {
|
|
||||||
ChunkData cd = cg.generateChunkData(chunk.getWorld(), new Random(), chunk.getX(), chunk.getZ(), grid);
|
|
||||||
createChunk(cd, chunk, grid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createChunk(ChunkData cd, Chunk chunk, MyBiomeGrid grid) {
|
|
||||||
nms.copyChunkDataToChunk(chunk, cd, grid, di.getBox());
|
|
||||||
// Remove all entities in chunk, including any dropped items as a result of clearing the blocks above
|
|
||||||
Arrays.stream(chunk.getEntities()).filter(e -> !(e instanceof Player) && di.inBounds(e.getLocation().getBlockX(), e.getLocation().getBlockZ())).forEach(Entity::remove);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isCompleted() {
|
public boolean isCompleted() {
|
||||||
return completed.get();
|
return completed.get();
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package world.bentobox.bentobox.util;
|
package world.bentobox.bentobox.util;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.MissingFormatArgumentException;
|
import java.util.MissingFormatArgumentException;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
@ -56,38 +58,78 @@ public class ItemParser {
|
|||||||
return defaultItemStack;
|
return defaultItemStack;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ItemStack returnValue = defaultItemStack;
|
||||||
|
|
||||||
String[] part = text.split(":");
|
String[] part = text.split(":");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Because I am lazy, and do not want to rewrite every parser, I will just add custom data as
|
||||||
|
// parameter and remove that array part form input data.
|
||||||
|
Optional<String> first = Arrays.stream(part).filter(field -> field.matches("(CMD-[0-9]*)")).findFirst();
|
||||||
|
Integer customModelData = null;
|
||||||
|
|
||||||
|
if (first.isPresent()) {
|
||||||
|
// Ugly and fast way how to get rid of customData field.
|
||||||
|
String[] copyParts = new String[part.length - 1];
|
||||||
|
int j = 0;
|
||||||
|
|
||||||
|
for (String field : part) {
|
||||||
|
if (!field.matches("(CMD-[0-9]*)")) {
|
||||||
|
copyParts[j++] = field;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace original parts with the copy parts that does not have any CMD values.
|
||||||
|
part = copyParts;
|
||||||
|
// Now use value from Custom Data Model and parse it as integer.
|
||||||
|
customModelData = Integer.valueOf(first.get().replaceFirst("CMD-", ""));
|
||||||
|
}
|
||||||
|
|
||||||
// Check if there are more properties for the item stack
|
// Check if there are more properties for the item stack
|
||||||
if (part.length == 1) {
|
if (part.length == 1) {
|
||||||
// Parse material directly. It does not have any extra properties.
|
// Parse material directly. It does not have any extra properties.
|
||||||
return new ItemStack(Material.valueOf(text.toUpperCase()));
|
returnValue = new ItemStack(Material.valueOf(part[0].toUpperCase()));
|
||||||
}
|
}
|
||||||
// Material-specific handling
|
// Material-specific handling
|
||||||
else if (part[0].contains("POTION") || part[0].equalsIgnoreCase("TIPPED_ARROW")) {
|
else if (part[0].contains("POTION") || part[0].equalsIgnoreCase("TIPPED_ARROW")) {
|
||||||
// Parse Potions and Tipped Arrows
|
// Parse Potions and Tipped Arrows
|
||||||
return parsePotion(part);
|
returnValue = parsePotion(part);
|
||||||
} else if (part[0].contains("BANNER")) {
|
} else if (part[0].contains("BANNER")) {
|
||||||
// Parse Banners
|
// Parse Banners
|
||||||
return parseBanner(part);
|
returnValue = parseBanner(part);
|
||||||
} else if (part[0].equalsIgnoreCase("PLAYER_HEAD")) {
|
} else if (part[0].equalsIgnoreCase("PLAYER_HEAD")) {
|
||||||
// Parse Player Heads
|
// Parse Player Heads
|
||||||
return parsePlayerHead(part);
|
returnValue = parsePlayerHead(part);
|
||||||
}
|
}
|
||||||
// Generic handling
|
// Generic handling
|
||||||
else if (part.length == 2) {
|
else if (part.length == 2) {
|
||||||
// Material:Qty
|
// Material:Qty
|
||||||
return parseItemQuantity(part);
|
returnValue = parseItemQuantity(part);
|
||||||
} else if (part.length == 3) {
|
} else if (part.length == 3) {
|
||||||
// Material:Durability:Qty
|
// Material:Durability:Qty
|
||||||
return parseItemDurabilityAndQuantity(part);
|
returnValue = parseItemDurabilityAndQuantity(part);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (returnValue != null) {
|
||||||
|
// If wrapper is just for code-style null-pointer checks.
|
||||||
|
if (customModelData != null) {
|
||||||
|
// We have custom data model. Now assign it to the item-stack.
|
||||||
|
ItemMeta itemMeta = returnValue.getItemMeta();
|
||||||
|
|
||||||
|
// Another null-pointer check for materials that does not have item meta.
|
||||||
|
if (itemMeta != null) {
|
||||||
|
itemMeta.setCustomModelData(customModelData);
|
||||||
|
// Update meta to the return item.
|
||||||
|
returnValue.setItemMeta(itemMeta);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception exception) {
|
} catch (Exception exception) {
|
||||||
BentoBox.getInstance().logError("Could not parse item " + text + " " + exception.getLocalizedMessage());
|
BentoBox.getInstance().logError("Could not parse item " + text + " " + exception.getLocalizedMessage());
|
||||||
|
returnValue = defaultItemStack;
|
||||||
}
|
}
|
||||||
|
|
||||||
return defaultItemStack;
|
return returnValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,11 +2,7 @@ package world.bentobox.bentobox.util;
|
|||||||
|
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Enumeration;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.jar.JarEntry;
|
import java.util.jar.JarEntry;
|
||||||
import java.util.jar.JarFile;
|
import java.util.jar.JarFile;
|
||||||
@ -25,19 +21,7 @@ import org.bukkit.World.Environment;
|
|||||||
import org.bukkit.attribute.Attribute;
|
import org.bukkit.attribute.Attribute;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
import org.bukkit.block.BlockFace;
|
import org.bukkit.block.BlockFace;
|
||||||
import org.bukkit.entity.Animals;
|
import org.bukkit.entity.*;
|
||||||
import org.bukkit.entity.Bat;
|
|
||||||
import org.bukkit.entity.EnderDragon;
|
|
||||||
import org.bukkit.entity.Entity;
|
|
||||||
import org.bukkit.entity.Flying;
|
|
||||||
import org.bukkit.entity.IronGolem;
|
|
||||||
import org.bukkit.entity.Monster;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.bukkit.entity.PufferFish;
|
|
||||||
import org.bukkit.entity.Shulker;
|
|
||||||
import org.bukkit.entity.Slime;
|
|
||||||
import org.bukkit.entity.Snowman;
|
|
||||||
import org.bukkit.entity.WaterMob;
|
|
||||||
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
|
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
|
||||||
import org.bukkit.util.Vector;
|
import org.bukkit.util.Vector;
|
||||||
import org.eclipse.jdt.annotation.NonNull;
|
import org.eclipse.jdt.annotation.NonNull;
|
||||||
@ -47,7 +31,10 @@ import io.papermc.lib.PaperLib;
|
|||||||
import io.papermc.lib.features.blockstatesnapshot.BlockStateSnapshotResult;
|
import io.papermc.lib.features.blockstatesnapshot.BlockStateSnapshotResult;
|
||||||
import world.bentobox.bentobox.BentoBox;
|
import world.bentobox.bentobox.BentoBox;
|
||||||
import world.bentobox.bentobox.api.user.User;
|
import world.bentobox.bentobox.api.user.User;
|
||||||
import world.bentobox.bentobox.nms.NMSAbstraction;
|
import world.bentobox.bentobox.nms.PasteHandler;
|
||||||
|
import world.bentobox.bentobox.nms.WorldRegenerator;
|
||||||
|
import world.bentobox.bentobox.versions.ServerCompatibility;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A set of utility methods
|
* A set of utility methods
|
||||||
@ -64,7 +51,8 @@ public class Util {
|
|||||||
private static final String THE_END = "_the_end";
|
private static final String THE_END = "_the_end";
|
||||||
private static String serverVersion = null;
|
private static String serverVersion = null;
|
||||||
private static BentoBox plugin = BentoBox.getInstance();
|
private static BentoBox plugin = BentoBox.getInstance();
|
||||||
private static NMSAbstraction nms = null;
|
private static PasteHandler pasteHandler = null;
|
||||||
|
private static WorldRegenerator regenerator = null;
|
||||||
|
|
||||||
private Util() {}
|
private Util() {}
|
||||||
|
|
||||||
@ -353,8 +341,19 @@ public class Util {
|
|||||||
// Bat extends Mob
|
// Bat extends Mob
|
||||||
// Most of passive mobs extends Animals
|
// Most of passive mobs extends Animals
|
||||||
|
|
||||||
return entity instanceof Animals || entity instanceof IronGolem || entity instanceof Snowman ||
|
if (ServerCompatibility.getInstance().isVersion(ServerCompatibility.ServerVersion.V1_18,
|
||||||
|
ServerCompatibility.ServerVersion.V1_18_1,
|
||||||
|
ServerCompatibility.ServerVersion.V1_18_2))
|
||||||
|
{
|
||||||
|
return entity instanceof Animals || entity instanceof IronGolem || entity instanceof Snowman ||
|
||||||
entity instanceof WaterMob && !(entity instanceof PufferFish) || entity instanceof Bat;
|
entity instanceof WaterMob && !(entity instanceof PufferFish) || entity instanceof Bat;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return entity instanceof Animals || entity instanceof IronGolem || entity instanceof Snowman ||
|
||||||
|
entity instanceof WaterMob && !(entity instanceof PufferFish) || entity instanceof Bat ||
|
||||||
|
entity instanceof Allay;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -689,23 +688,56 @@ public class Util {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the NMS handler the plugin will use
|
* Set the regenerator the plugin will use
|
||||||
* @param nms the NMS handler
|
* @param regenerator the regenerator
|
||||||
*/
|
*/
|
||||||
public static void setNms(NMSAbstraction nms) {
|
public static void setRegenerator(WorldRegenerator regenerator) {
|
||||||
Util.nms = nms;
|
Util.regenerator = regenerator;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the NMS handler the plugin will use
|
* Get the regenerator the plugin will use
|
||||||
|
* @return an accelerated regenerator class for this server
|
||||||
|
*/
|
||||||
|
public static WorldRegenerator getRegenerator() {
|
||||||
|
if (regenerator == null) {
|
||||||
|
String serverPackageName = Bukkit.getServer().getClass().getPackage().getName();
|
||||||
|
String pluginPackageName = plugin.getClass().getPackage().getName();
|
||||||
|
String version = serverPackageName.substring(serverPackageName.lastIndexOf('.') + 1);
|
||||||
|
WorldRegenerator handler;
|
||||||
|
try {
|
||||||
|
Class<?> clazz = Class.forName(pluginPackageName + ".nms." + version + ".WorldRegeneratorImpl");
|
||||||
|
if (WorldRegenerator.class.isAssignableFrom(clazz)) {
|
||||||
|
handler = (WorldRegenerator) clazz.getConstructor().newInstance();
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException("Class " + clazz.getName() + " does not implement WorldRegenerator");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
plugin.logWarning("No Regenerator found for " + version + ", falling back to Bukkit API.");
|
||||||
|
handler = new world.bentobox.bentobox.nms.fallback.WorldRegeneratorImpl();
|
||||||
|
}
|
||||||
|
setRegenerator(handler);
|
||||||
|
}
|
||||||
|
return regenerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the paste handler the plugin will use
|
||||||
|
* @param pasteHandler the NMS paster
|
||||||
|
*/
|
||||||
|
public static void setPasteHandler(PasteHandler pasteHandler) {
|
||||||
|
Util.pasteHandler = pasteHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the paste handler the plugin will use
|
||||||
* @return an NMS accelerated class for this server
|
* @return an NMS accelerated class for this server
|
||||||
*/
|
*/
|
||||||
public static NMSAbstraction getNMS() {
|
public static PasteHandler getPasteHandler() {
|
||||||
if (nms == null) {
|
if (pasteHandler == null) {
|
||||||
plugin.log("No NMS Handler was set, falling back to Bukkit API.");
|
setPasteHandler(new world.bentobox.bentobox.nms.fallback.PasteHandlerImpl());
|
||||||
setNms(new world.bentobox.bentobox.nms.fallback.NMSHandler());
|
|
||||||
}
|
}
|
||||||
return nms;
|
return pasteHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -725,4 +757,19 @@ public class Util {
|
|||||||
}
|
}
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method removes all special characters that are not allowed in filenames (windows).
|
||||||
|
* It also includes any white-spaces, as for some reason, I do like it more without them.
|
||||||
|
* Also, all cases are lower cased for easier blueprint mapping.
|
||||||
|
* @param input Input that need to be sanitized.
|
||||||
|
* @return A sanitized input without illegal characters in names.
|
||||||
|
*/
|
||||||
|
public static String sanitizeInput(String input)
|
||||||
|
{
|
||||||
|
return ChatColor.stripColor(
|
||||||
|
Util.translateColorCodes(input.replaceAll("[\\\\/:*?\"<>|\s]", "_"))).
|
||||||
|
toLowerCase();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,887 @@
|
|||||||
|
//
|
||||||
|
// Created by BONNe
|
||||||
|
// Copyright - 2022
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
package world.bentobox.bentobox.util.teleport;
|
||||||
|
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Chunk;
|
||||||
|
import org.bukkit.ChunkSnapshot;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.block.Block;
|
||||||
|
import org.bukkit.block.BlockFace;
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.scheduler.BukkitTask;
|
||||||
|
import org.bukkit.util.BoundingBox;
|
||||||
|
import org.bukkit.util.Vector;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.PriorityQueue;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
import world.bentobox.bentobox.BentoBox;
|
||||||
|
import world.bentobox.bentobox.api.user.User;
|
||||||
|
import world.bentobox.bentobox.database.objects.Island;
|
||||||
|
import world.bentobox.bentobox.util.Pair;
|
||||||
|
import world.bentobox.bentobox.util.Util;
|
||||||
|
|
||||||
|
|
||||||
|
public class ClosestSafeSpotTeleport
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Teleports and entity to a safe spot on island
|
||||||
|
*
|
||||||
|
* @param builder - safe spot teleport builder
|
||||||
|
*/
|
||||||
|
ClosestSafeSpotTeleport(Builder builder)
|
||||||
|
{
|
||||||
|
this.plugin = builder.getPlugin();
|
||||||
|
this.entity = builder.getEntity();
|
||||||
|
this.location = builder.getLocation();
|
||||||
|
this.portal = builder.isPortal();
|
||||||
|
|
||||||
|
this.successRunnable = builder.getSuccessRunnable();
|
||||||
|
this.failRunnable = builder.getFailRunnable();
|
||||||
|
|
||||||
|
this.failureMessage = builder.getFailureMessage();
|
||||||
|
|
||||||
|
this.result = builder.getResult();
|
||||||
|
this.world = Objects.requireNonNull(this.location.getWorld());
|
||||||
|
|
||||||
|
this.cancelIfFail = builder.isCancelIfFail();
|
||||||
|
|
||||||
|
// Try starting location
|
||||||
|
Util.getChunkAtAsync(this.location).thenRun(this::checkLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is main method that triggers safe spot search.
|
||||||
|
* It starts with the given location and afterwards checks all blocks in required area.
|
||||||
|
*/
|
||||||
|
private void checkLocation()
|
||||||
|
{
|
||||||
|
if (this.plugin.getIslandsManager().isSafeLocation(this.location))
|
||||||
|
{
|
||||||
|
if (!this.portal)
|
||||||
|
{
|
||||||
|
// If this is not a portal teleport, then go to the safe location immediately
|
||||||
|
this.teleportEntity(this.location);
|
||||||
|
// Position search is completed. Quit faster.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Players should not be teleported outside protection range if they already are in it.
|
||||||
|
this.boundingBox = this.plugin.getIslandsManager().getIslandAt(this.location).
|
||||||
|
map(Island::getProtectionBoundingBox).
|
||||||
|
orElseGet(() -> {
|
||||||
|
int protectionRange = this.plugin.getIWM().getIslandProtectionRange(this.world);
|
||||||
|
|
||||||
|
return new BoundingBox(this.location.getBlockX() - protectionRange,
|
||||||
|
Math.max(this.world.getMinHeight(), this.location.getBlockY() - protectionRange),
|
||||||
|
this.location.getBlockZ() - protectionRange,
|
||||||
|
this.location.getBlockX() + protectionRange,
|
||||||
|
Math.min(this.world.getMaxHeight(), this.location.getBlockY() + protectionRange),
|
||||||
|
this.location.getBlockZ() + protectionRange);
|
||||||
|
});
|
||||||
|
|
||||||
|
// The maximal range of search.
|
||||||
|
this.range = Math.min(this.plugin.getSettings().getSafeSpotSearchRange(), (int) this.boundingBox.getWidthX() / 2);
|
||||||
|
|
||||||
|
// The block queue contains all possible positions where player can be teleported. The queue will not be populated
|
||||||
|
// with all blocks, as the validation would not allow it.ss
|
||||||
|
this.blockQueue = new PriorityQueue<>(this.range * 2, ClosestSafeSpotTeleport.POSITION_COMPARATOR);
|
||||||
|
|
||||||
|
// Get chunks to scan
|
||||||
|
this.chunksToScanIterator = this.getChunksToScan().iterator();
|
||||||
|
|
||||||
|
// Start a recurring task until done or cancelled
|
||||||
|
this.task = Bukkit.getScheduler().runTaskTimer(this.plugin, this::gatherChunks, 0L, CHUNK_LOAD_SPEED);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method loads all chunks in async and populates blockQueue with all blocks.
|
||||||
|
*/
|
||||||
|
private void gatherChunks()
|
||||||
|
{
|
||||||
|
// Set a flag so this is only run if it's not already in progress
|
||||||
|
if (this.checking.get())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.checking.set(true);
|
||||||
|
|
||||||
|
if (!this.portal && !this.blockQueue.isEmpty() && this.blockQueue.peek().distance() < 5)
|
||||||
|
{
|
||||||
|
// Position is found? Well most likely (not in all situations) position in block queue is already
|
||||||
|
// the best position. The only bad situations could happen if position is on chunk borders.
|
||||||
|
this.finishTask();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.chunksToScanIterator.hasNext())
|
||||||
|
{
|
||||||
|
// Chunk scanning has completed. Now check positions.
|
||||||
|
this.finishTask();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the chunk
|
||||||
|
Pair<Integer, Integer> chunkPair = this.chunksToScanIterator.next();
|
||||||
|
this.chunksToScanIterator.remove();
|
||||||
|
|
||||||
|
// Get the chunk snapshot and scan it
|
||||||
|
Util.getChunkAtAsync(this.world, chunkPair.x, chunkPair.z).
|
||||||
|
thenApply(Chunk::getChunkSnapshot).
|
||||||
|
whenCompleteAsync((snapshot, e) ->
|
||||||
|
{
|
||||||
|
if (snapshot != null)
|
||||||
|
{
|
||||||
|
// Find best spot based on collected information chunks.
|
||||||
|
this.scanAndPopulateBlockQueue(snapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.checking.set(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a set of chunk coordinates that will be scanned.
|
||||||
|
*
|
||||||
|
* @return - list of chunk coordinates to be scanned
|
||||||
|
*/
|
||||||
|
private List<Pair<Integer, Integer>> getChunksToScan()
|
||||||
|
{
|
||||||
|
List<Pair<Integer, Integer>> chunksToScan = new ArrayList<>();
|
||||||
|
|
||||||
|
int x = this.location.getBlockX();
|
||||||
|
int z = this.location.getBlockZ();
|
||||||
|
|
||||||
|
int range = 20;
|
||||||
|
|
||||||
|
// Normalize block coordinates to chunk coordinates and add extra 1 for visiting.
|
||||||
|
int numberOfChunks = (((x + range) >> 4) - ((x - range) >> 4) + 1) *
|
||||||
|
(((z + range) >> 4) - ((z - range) >> 4) + 1);
|
||||||
|
|
||||||
|
// Ideally it would be if visitor switch from clockwise to counter-clockwise if X % 16 < 8 and
|
||||||
|
// up to down if Z % 16 < 8.
|
||||||
|
|
||||||
|
int offsetX = 0;
|
||||||
|
int offsetZ = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < numberOfChunks; ++i)
|
||||||
|
{
|
||||||
|
int locationX = x + (offsetX << 4);
|
||||||
|
int locationZ = z + (offsetZ << 4);
|
||||||
|
|
||||||
|
this.addChunk(chunksToScan, new Pair<>(locationX, locationZ), new Pair<>(locationX >> 4, locationZ >> 4));
|
||||||
|
|
||||||
|
if (Math.abs(offsetX) <= Math.abs(offsetZ) && (offsetX != offsetZ || offsetX >= 0))
|
||||||
|
{
|
||||||
|
offsetX += ((offsetZ >= 0) ? 1 : -1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
offsetZ += ((offsetX >= 0) ? -1 : 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return chunksToScan;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method adds chunk coordinates to the given chunksToScan list.
|
||||||
|
* The limitation is that if location is in island, then block coordinate must also be in island space.
|
||||||
|
* @param chunksToScan List of chunks that will be scanned.
|
||||||
|
* @param blockCoord Block coordinates that must be in island.
|
||||||
|
* @param chunkCoord Chunk coordinate.
|
||||||
|
*/
|
||||||
|
private void addChunk(List<Pair<Integer, Integer>> chunksToScan,
|
||||||
|
Pair<Integer, Integer> blockCoord,
|
||||||
|
Pair<Integer, Integer> chunkCoord)
|
||||||
|
{
|
||||||
|
if (!chunksToScan.contains(chunkCoord) &&
|
||||||
|
this.plugin.getIslandsManager().getIslandAt(this.location).
|
||||||
|
map(is -> is.inIslandSpace(blockCoord)).orElse(true))
|
||||||
|
{
|
||||||
|
chunksToScan.add(chunkCoord);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method populates block queue with all blocks that player can be teleported to.
|
||||||
|
* Add only positions that are inside BoundingBox and is safe for teleportation.
|
||||||
|
* @param chunkSnapshot Spigot Chunk Snapshot with blocks.
|
||||||
|
*/
|
||||||
|
private void scanAndPopulateBlockQueue(ChunkSnapshot chunkSnapshot)
|
||||||
|
{
|
||||||
|
int startY = this.location.getBlockY();
|
||||||
|
int minY = this.world.getMinHeight();
|
||||||
|
int maxY = this.world.getMaxHeight();
|
||||||
|
|
||||||
|
Vector blockVector = new Vector(this.location.getBlockX(), this.location.getBlockY(), this.location.getBlockZ());
|
||||||
|
|
||||||
|
int chunkX = chunkSnapshot.getX() << 4;
|
||||||
|
int chunkZ = chunkSnapshot.getZ() << 4;
|
||||||
|
|
||||||
|
for (int x = 0; x < 16; x++)
|
||||||
|
{
|
||||||
|
for (int z = 0; z < 16; z++)
|
||||||
|
{
|
||||||
|
for (int y = Math.max(minY, startY - this.range); y < Math.min(maxY, startY + this.range); y++)
|
||||||
|
{
|
||||||
|
Vector positionVector = new Vector(chunkX + x, y, chunkZ + z);
|
||||||
|
|
||||||
|
if (this.boundingBox.contains(positionVector))
|
||||||
|
{
|
||||||
|
// Process positions that are inside bounding box of search area.
|
||||||
|
|
||||||
|
PositionData positionData = new PositionData(
|
||||||
|
positionVector,
|
||||||
|
chunkSnapshot.getBlockType(x, y - 1, z),
|
||||||
|
y < maxY ? chunkSnapshot.getBlockType(x, y, z) : null,
|
||||||
|
y + 1 < maxY ? chunkSnapshot.getBlockType(x, y + 1, z) : null,
|
||||||
|
blockVector.distanceSquared(positionVector));
|
||||||
|
|
||||||
|
if (this.plugin.getIslandsManager().checkIfSafe(this.world,
|
||||||
|
positionData.block,
|
||||||
|
positionData.spaceOne,
|
||||||
|
positionData.spaceTwo))
|
||||||
|
{
|
||||||
|
// Add only safe locations to the queue.
|
||||||
|
this.blockQueue.add(positionData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method finishes the chunk loading task and checks from all remaining positions in block queue
|
||||||
|
* to find the best location for teleportation.
|
||||||
|
*
|
||||||
|
* This method stops position finding task and process teleporation.
|
||||||
|
*/
|
||||||
|
private void finishTask()
|
||||||
|
{
|
||||||
|
// Still Async!
|
||||||
|
// Nothing left to check and still not canceled
|
||||||
|
this.task.cancel();
|
||||||
|
|
||||||
|
if (this.scanBlockQueue())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.portal && this.noPortalPosition != null)
|
||||||
|
{
|
||||||
|
this.teleportEntity(this.noPortalPosition);
|
||||||
|
}
|
||||||
|
else if (this.entity instanceof Player player)
|
||||||
|
{
|
||||||
|
// Return to main thread and teleport the player
|
||||||
|
Bukkit.getScheduler().runTask(this.plugin, () ->
|
||||||
|
{
|
||||||
|
// Failed, no safe spot
|
||||||
|
if (!this.failureMessage.isEmpty())
|
||||||
|
{
|
||||||
|
User.getInstance(this.entity).notify(this.failureMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check highest block
|
||||||
|
Block highestBlock = this.world.getHighestBlockAt(this.location);
|
||||||
|
|
||||||
|
if (highestBlock.getType().isSolid() &&
|
||||||
|
this.plugin.getIslandsManager().isSafeLocation(highestBlock.getLocation()))
|
||||||
|
{
|
||||||
|
// Try to teleport player to the highest block.
|
||||||
|
this.asyncTeleport(highestBlock.getLocation().add(new Vector(0.5D, 0D, 0.5D)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (!this.plugin.getIWM().inWorld(this.entity.getLocation()))
|
||||||
|
{
|
||||||
|
// Last resort
|
||||||
|
player.performCommand("spawn");
|
||||||
|
}
|
||||||
|
else if (!this.cancelIfFail)
|
||||||
|
{
|
||||||
|
// Create a spot for the player to be
|
||||||
|
if (this.world.getEnvironment().equals(World.Environment.NETHER))
|
||||||
|
{
|
||||||
|
this.makeAndTeleport(Material.NETHERRACK);
|
||||||
|
}
|
||||||
|
else if (this.world.getEnvironment().equals(World.Environment.THE_END))
|
||||||
|
{
|
||||||
|
this.makeAndTeleport(Material.END_STONE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.makeAndTeleport(Material.COBBLESTONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.failRunnable != null)
|
||||||
|
{
|
||||||
|
Bukkit.getScheduler().runTask(this.plugin, this.failRunnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.result.complete(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We do not teleport entities if position failed.
|
||||||
|
|
||||||
|
if (this.failRunnable != null)
|
||||||
|
{
|
||||||
|
Bukkit.getScheduler().runTask(this.plugin, this.failRunnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.result.complete(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method creates a spot in start location for player to be teleported to. It creates 2 base material blocks
|
||||||
|
* above location and fills the space between them with air.
|
||||||
|
* @param baseMaterial Material that will be for top and bottom block.
|
||||||
|
*/
|
||||||
|
private void makeAndTeleport(Material baseMaterial)
|
||||||
|
{
|
||||||
|
this.location.getBlock().getRelative(BlockFace.DOWN).setType(baseMaterial, false);
|
||||||
|
this.location.getBlock().setType(Material.AIR, false);
|
||||||
|
this.location.getBlock().getRelative(BlockFace.UP).setType(Material.AIR, false);
|
||||||
|
this.location.getBlock().getRelative(BlockFace.UP).getRelative(BlockFace.UP).setType(baseMaterial, false);
|
||||||
|
|
||||||
|
// Teleport player to the location of the empty space.
|
||||||
|
this.asyncTeleport(this.location.clone().add(new Vector(0.5D, 0D, 0.5D)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method scans all populated positions and returns true if position is found, or false, if not.
|
||||||
|
* @return {@code true} if safe position is found, otherwise false.
|
||||||
|
*/
|
||||||
|
private boolean scanBlockQueue()
|
||||||
|
{
|
||||||
|
boolean blockFound = false;
|
||||||
|
|
||||||
|
while (!this.blockQueue.isEmpty() && !blockFound)
|
||||||
|
{
|
||||||
|
blockFound = this.checkPosition(this.blockQueue.poll());
|
||||||
|
}
|
||||||
|
|
||||||
|
return blockFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method triggers a task that will teleport entity in a main thread.
|
||||||
|
*/
|
||||||
|
private void teleportEntity(final Location location)
|
||||||
|
{
|
||||||
|
// Return to main thread and teleport the player
|
||||||
|
Bukkit.getScheduler().runTask(this.plugin, () -> this.asyncTeleport(location));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method performs async teleportation and runs end tasks for spot-finder.
|
||||||
|
* @param location Location where player should be teleported.
|
||||||
|
*/
|
||||||
|
private void asyncTeleport(final Location location)
|
||||||
|
{
|
||||||
|
Util.teleportAsync(this.entity, location).thenRun(() ->
|
||||||
|
{
|
||||||
|
if (this.successRunnable != null)
|
||||||
|
{
|
||||||
|
Bukkit.getScheduler().runTask(this.plugin, this.successRunnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.result.complete(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method checks if given position is valid for teleportation.
|
||||||
|
* If query should find portal, then it marks first best position as noPortalPosition and continues
|
||||||
|
* to search for a valid portal.
|
||||||
|
* If query is not in portal mode, then return first valid position.
|
||||||
|
* @param positionData Position data that must be checked.
|
||||||
|
* @return {@code true} if position is found and no extra processing required, {@code false} otherwise.
|
||||||
|
*/
|
||||||
|
private boolean checkPosition(PositionData positionData)
|
||||||
|
{
|
||||||
|
if (this.portal)
|
||||||
|
{
|
||||||
|
if (Material.NETHER_PORTAL.equals(positionData.spaceOne()) ||
|
||||||
|
Material.NETHER_PORTAL.equals(positionData.spaceTwo()))
|
||||||
|
{
|
||||||
|
// Portal is found. Teleport entity to the portal location.
|
||||||
|
this.teleportEntity(new Location(this.world,
|
||||||
|
positionData.vector().getBlockX() + 0.5,
|
||||||
|
positionData.vector().getBlockY() + 0.1,
|
||||||
|
positionData.vector().getBlockZ() + 0.5,
|
||||||
|
this.location.getYaw(),
|
||||||
|
this.location.getPitch()));
|
||||||
|
|
||||||
|
// Position found and player can is already teleported to it.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (this.noPortalPosition == null)
|
||||||
|
{
|
||||||
|
// Mark first incoming position as the best for teleportation.
|
||||||
|
this.noPortalPosition = new Location(this.world,
|
||||||
|
positionData.vector().getBlockX() + 0.5,
|
||||||
|
positionData.vector().getBlockY() + 0.1,
|
||||||
|
positionData.vector().getBlockZ() + 0.5,
|
||||||
|
this.location.getYaw(),
|
||||||
|
this.location.getPitch());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// First best position should be valid for teleportation.
|
||||||
|
this.teleportEntity(new Location(this.world,
|
||||||
|
positionData.vector().getBlockX() + 0.5,
|
||||||
|
positionData.vector().getBlockY() + 0.1,
|
||||||
|
positionData.vector().getBlockZ() + 0.5,
|
||||||
|
this.location.getYaw(),
|
||||||
|
this.location.getPitch()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PositionData record holds information about position where player will be teleported.
|
||||||
|
* @param vector Vector of the position.
|
||||||
|
* @param distance Distance till the position.
|
||||||
|
* @param block Block on which player will be placed.
|
||||||
|
* @param spaceOne One block above block.
|
||||||
|
* @param spaceTwo Two blocks above block.
|
||||||
|
*/
|
||||||
|
private record PositionData(Vector vector, Material block, Material spaceOne, Material spaceTwo, double distance) {}
|
||||||
|
|
||||||
|
|
||||||
|
public static Builder builder(BentoBox plugin)
|
||||||
|
{
|
||||||
|
return new Builder(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
// Section: Builder
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
public static class Builder
|
||||||
|
{
|
||||||
|
private Builder(BentoBox plugin)
|
||||||
|
{
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.result = new CompletableFuture<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
// Section: Builders
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set who or what is going to teleport
|
||||||
|
*
|
||||||
|
* @param entity entity to teleport
|
||||||
|
* @return Builder
|
||||||
|
*/
|
||||||
|
public Builder entity(Entity entity)
|
||||||
|
{
|
||||||
|
this.entity = entity;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the desired location
|
||||||
|
*
|
||||||
|
* @param location the location
|
||||||
|
* @return Builder
|
||||||
|
*/
|
||||||
|
public Builder location(Location location)
|
||||||
|
{
|
||||||
|
this.location = location;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a portal teleportation
|
||||||
|
*
|
||||||
|
* @return Builder
|
||||||
|
*/
|
||||||
|
public Builder portal()
|
||||||
|
{
|
||||||
|
this.portal = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a successRunnable for teleportation
|
||||||
|
*
|
||||||
|
* @return Builder
|
||||||
|
*/
|
||||||
|
public Builder successRunnable(Runnable successRunnable)
|
||||||
|
{
|
||||||
|
this.successRunnable = successRunnable;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to teleport the player
|
||||||
|
*
|
||||||
|
* @return ClosestSafeSpotTeleport
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public ClosestSafeSpotTeleport build()
|
||||||
|
{
|
||||||
|
// Error checking
|
||||||
|
if (this.entity == null)
|
||||||
|
{
|
||||||
|
this.plugin.logError("Attempt to safe teleport a null entity!");
|
||||||
|
this.result.complete(null);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.location == null)
|
||||||
|
{
|
||||||
|
this.plugin.logError("Attempt to safe teleport to a null location!");
|
||||||
|
this.result.complete(null);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.location.getWorld() == null)
|
||||||
|
{
|
||||||
|
this.plugin.logError("Attempt to safe teleport to a null world!");
|
||||||
|
this.result.complete(null);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.failureMessage.isEmpty() && this.entity instanceof Player)
|
||||||
|
{
|
||||||
|
this.failureMessage = "general.errors.no-safe-location-found";
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ClosestSafeSpotTeleport(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
// Section: Getters
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets plugin.
|
||||||
|
*
|
||||||
|
* @return the plugin
|
||||||
|
*/
|
||||||
|
public BentoBox getPlugin()
|
||||||
|
{
|
||||||
|
return this.plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets result.
|
||||||
|
*
|
||||||
|
* @return the result
|
||||||
|
*/
|
||||||
|
public CompletableFuture<Boolean> getResult()
|
||||||
|
{
|
||||||
|
return this.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets entity.
|
||||||
|
*
|
||||||
|
* @return the entity
|
||||||
|
*/
|
||||||
|
public Entity getEntity()
|
||||||
|
{
|
||||||
|
return this.entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets location.
|
||||||
|
*
|
||||||
|
* @return the location
|
||||||
|
*/
|
||||||
|
public Location getLocation()
|
||||||
|
{
|
||||||
|
return this.location;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets world.
|
||||||
|
*
|
||||||
|
* @return the world
|
||||||
|
*/
|
||||||
|
public World getWorld()
|
||||||
|
{
|
||||||
|
return this.world;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets success runnable.
|
||||||
|
*
|
||||||
|
* @return the success runnable
|
||||||
|
*/
|
||||||
|
public Runnable getSuccessRunnable()
|
||||||
|
{
|
||||||
|
return this.successRunnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets fail runnable.
|
||||||
|
*
|
||||||
|
* @return the fail runnable
|
||||||
|
*/
|
||||||
|
public Runnable getFailRunnable()
|
||||||
|
{
|
||||||
|
return this.failRunnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets failure message.
|
||||||
|
*
|
||||||
|
* @return the failure message
|
||||||
|
*/
|
||||||
|
public String getFailureMessage()
|
||||||
|
{
|
||||||
|
return this.failureMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is portal boolean.
|
||||||
|
*
|
||||||
|
* @return the boolean
|
||||||
|
*/
|
||||||
|
public boolean isPortal()
|
||||||
|
{
|
||||||
|
return this.portal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is cancel if fail boolean.
|
||||||
|
*
|
||||||
|
* @return the boolean
|
||||||
|
*/
|
||||||
|
public boolean isCancelIfFail()
|
||||||
|
{
|
||||||
|
return this.cancelIfFail;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
// Section: Variables
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BentoBox plugin instance.
|
||||||
|
*/
|
||||||
|
private final BentoBox plugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CompletableFuture that is triggered upon finishing position searching.
|
||||||
|
*/
|
||||||
|
private final CompletableFuture<Boolean> result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entity that will be teleported.
|
||||||
|
*/
|
||||||
|
private Entity entity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start location of teleportation.
|
||||||
|
*/
|
||||||
|
private Location location;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* World where teleportation happens.
|
||||||
|
*/
|
||||||
|
private World world;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runnable that will be triggered after successful teleportation.
|
||||||
|
*/
|
||||||
|
private Runnable successRunnable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runnable that will be triggered after failing teleportation.
|
||||||
|
*/
|
||||||
|
private Runnable failRunnable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the failure message that is sent to a player.
|
||||||
|
*/
|
||||||
|
private String failureMessage = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Boolean that indicates if teleportation should search for portal.
|
||||||
|
*/
|
||||||
|
private boolean portal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Boolean that indicates if failing teleport should cancel it or create spot for player.
|
||||||
|
*/
|
||||||
|
private boolean cancelIfFail;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
// Section: Constants
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This comparator sorts position data based in order:
|
||||||
|
* - the smallest distance value
|
||||||
|
* - the smallest x value
|
||||||
|
* - the smallest z value
|
||||||
|
* - the smallest y value
|
||||||
|
*/
|
||||||
|
private final static Comparator<PositionData> POSITION_COMPARATOR = Comparator.comparingDouble(PositionData::distance).
|
||||||
|
thenComparingInt(position -> position.vector().getBlockX()).
|
||||||
|
thenComparingInt(position -> position.vector().getBlockZ()).
|
||||||
|
thenComparingInt(position -> position.vector().getBlockY());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores chunk load speed.
|
||||||
|
*/
|
||||||
|
private static final long CHUNK_LOAD_SPEED = 1;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
// Section: Variables
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BentoBox plugin instance.
|
||||||
|
*/
|
||||||
|
private final BentoBox plugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entity that will be teleported.
|
||||||
|
*/
|
||||||
|
private final Entity entity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start location of teleportation.
|
||||||
|
*/
|
||||||
|
private final Location location;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* World where teleportation happens.
|
||||||
|
*/
|
||||||
|
private final World world;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runnable that will be triggered after successful teleportation.
|
||||||
|
*/
|
||||||
|
private final Runnable successRunnable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runnable that will be triggered after failing teleportation.
|
||||||
|
*/
|
||||||
|
private final Runnable failRunnable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the failure message that is sent to a player.
|
||||||
|
*/
|
||||||
|
private final String failureMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CompletableFuture that is triggered upon finishing position searching.
|
||||||
|
*/
|
||||||
|
private final CompletableFuture<Boolean> result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Boolean that indicates if teleportation should search for portal.
|
||||||
|
*/
|
||||||
|
private final boolean portal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Boolean that indicates if failing teleport should cancel it or create spot for player.
|
||||||
|
*/
|
||||||
|
private final boolean cancelIfFail;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local variable that indicates if current process is running.
|
||||||
|
*/
|
||||||
|
private final AtomicBoolean checking = new AtomicBoolean();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The distance from starting location in all directions where new position will be searched.
|
||||||
|
*/
|
||||||
|
private int range;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Block Queue for all blocks that should be validated.
|
||||||
|
*/
|
||||||
|
private Queue<PositionData> blockQueue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of chunks that will be scanned for positions.
|
||||||
|
*/
|
||||||
|
private Iterator<Pair<Integer, Integer>> chunksToScanIterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BoundingBox where teleportation can happen. Areas outside are illegal.
|
||||||
|
*/
|
||||||
|
private BoundingBox boundingBox;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns first best available spot if portal was not found in search area.
|
||||||
|
*/
|
||||||
|
private Location noPortalPosition;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bukkit task that processes chunks.
|
||||||
|
*/
|
||||||
|
private BukkitTask task;
|
||||||
|
}
|
||||||
|
|
@ -176,28 +176,40 @@ public class ServerCompatibility {
|
|||||||
/**
|
/**
|
||||||
* @since 1.16.0
|
* @since 1.16.0
|
||||||
*/
|
*/
|
||||||
V1_16_5(Compatibility.NOT_SUPPORTED),
|
V1_16_5(Compatibility.INCOMPATIBLE),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @since 1.17.0
|
* @since 1.17.0
|
||||||
*/
|
*/
|
||||||
V1_17(Compatibility.NOT_SUPPORTED),
|
V1_17(Compatibility.INCOMPATIBLE),
|
||||||
/**
|
/**
|
||||||
* @since 1.17.1
|
* @since 1.17.1
|
||||||
*/
|
*/
|
||||||
V1_17_1(Compatibility.SUPPORTED),
|
V1_17_1(Compatibility.INCOMPATIBLE),
|
||||||
/**
|
/**
|
||||||
* @since 1.19.0
|
* @since 1.19.0
|
||||||
*/
|
*/
|
||||||
V1_18(Compatibility.COMPATIBLE),
|
V1_18(Compatibility.SUPPORTED),
|
||||||
/**
|
/**
|
||||||
* @since 1.19.0
|
* @since 1.19.0
|
||||||
*/
|
*/
|
||||||
V1_18_1(Compatibility.COMPATIBLE),
|
V1_18_1(Compatibility.SUPPORTED),
|
||||||
/**
|
/**
|
||||||
* @since 1.20.1
|
* @since 1.20.1
|
||||||
*/
|
*/
|
||||||
V1_18_2(Compatibility.COMPATIBLE),
|
V1_18_2(Compatibility.SUPPORTED),
|
||||||
|
/**
|
||||||
|
* @since 1.21.0
|
||||||
|
*/
|
||||||
|
V1_19(Compatibility.COMPATIBLE),
|
||||||
|
/**
|
||||||
|
* @since 1.21.0
|
||||||
|
*/
|
||||||
|
V1_19_1(Compatibility.COMPATIBLE),
|
||||||
|
/**
|
||||||
|
* @since 1.21.0
|
||||||
|
*/
|
||||||
|
V1_19_2(Compatibility.COMPATIBLE),
|
||||||
;
|
;
|
||||||
|
|
||||||
private final Compatibility compatibility;
|
private final Compatibility compatibility;
|
||||||
|
@ -209,6 +209,11 @@ island:
|
|||||||
# If set to 0 or lower, the plugin will not expand the y-coordinate.
|
# If set to 0 or lower, the plugin will not expand the y-coordinate.
|
||||||
# Added since 1.19.1.
|
# Added since 1.19.1.
|
||||||
safe-spot-search-vertical-range: 400
|
safe-spot-search-vertical-range: 400
|
||||||
|
# By default, if the destination is not safe, the plugin will try to search for a safe spot around the destination.
|
||||||
|
# This allows to change the distance for searching this spot. Larger value will mean longer position search.
|
||||||
|
# This value is also used for valid nether portal linking between dimension.
|
||||||
|
# Added since 1.21.0.
|
||||||
|
safe-spot-search-range: 16
|
||||||
web:
|
web:
|
||||||
github:
|
github:
|
||||||
# Toggle whether BentoBox can connect to GitHub to get data about updates and addons.
|
# Toggle whether BentoBox can connect to GitHub to get data about updates and addons.
|
||||||
|
@ -340,6 +340,7 @@ commands:
|
|||||||
prompt: Napiš jméno, nebo 'quit' ke zrušení
|
prompt: Napiš jméno, nebo 'quit' ke zrušení
|
||||||
too-long: '&c Příliš dlouhé'
|
too-long: '&c Příliš dlouhé'
|
||||||
pick-a-unique-name: Prosím, zvol více jedinečný název
|
pick-a-unique-name: Prosím, zvol více jedinečný název
|
||||||
|
invalid-char-in-unique-name: "Unique name cannot contain, start, or end with special characters, neither contain number! "
|
||||||
success: Povedlo se!
|
success: Povedlo se!
|
||||||
conversation-prefix: '>'
|
conversation-prefix: '>'
|
||||||
description:
|
description:
|
||||||
|
@ -391,6 +391,7 @@ commands:
|
|||||||
prompt: Gib einen Namen ein, oder 'quit' zum Beenden
|
prompt: Gib einen Namen ein, oder 'quit' zum Beenden
|
||||||
too-long: "&c Zu lang"
|
too-long: "&c Zu lang"
|
||||||
pick-a-unique-name: Wähle bitte einen eindeutigeren Namen
|
pick-a-unique-name: Wähle bitte einen eindeutigeren Namen
|
||||||
|
invalid-char-in-unique-name: "Unique name cannot contain, start, or end with special characters, neither contain number! "
|
||||||
success: Erfolg!
|
success: Erfolg!
|
||||||
conversation-prefix: ">"
|
conversation-prefix: ">"
|
||||||
description:
|
description:
|
||||||
|
@ -102,6 +102,7 @@ commands:
|
|||||||
status: "&b [purged] &a islands purged out of &b [purgeable] &7(&b[percentage] %&7)&a."
|
status: "&b [purged] &a islands purged out of &b [purgeable] &7(&b[percentage] %&7)&a."
|
||||||
|
|
||||||
team:
|
team:
|
||||||
|
description: "manage teams"
|
||||||
add:
|
add:
|
||||||
parameters: "<owner> <player>"
|
parameters: "<owner> <player>"
|
||||||
description: "add player to owner's team"
|
description: "add player to owner's team"
|
||||||
@ -331,7 +332,7 @@ commands:
|
|||||||
rename:
|
rename:
|
||||||
parameters: "<blueprint name> <new name>"
|
parameters: "<blueprint name> <new name>"
|
||||||
description: "rename a blueprint"
|
description: "rename a blueprint"
|
||||||
success: "&a Blueprint &b [old] &a has been successfully renamed to &b [name]&a."
|
success: "&a Blueprint &b [old] &a has been successfully renamed to &b [display]&a. Filename now is &b [name]&a."
|
||||||
pick-different-name: "&c Please specify a name that is different from the blueprint's current name."
|
pick-different-name: "&c Please specify a name that is different from the blueprint's current name."
|
||||||
management:
|
management:
|
||||||
back: "Back"
|
back: "Back"
|
||||||
@ -365,8 +366,9 @@ commands:
|
|||||||
name:
|
name:
|
||||||
quit: "quit"
|
quit: "quit"
|
||||||
prompt: "Enter a name, or 'quit' to quit"
|
prompt: "Enter a name, or 'quit' to quit"
|
||||||
too-long: "&c Too long"
|
too-long: "&c Too long name. Only 32 chars are allowed."
|
||||||
pick-a-unique-name: "Please pick a more unique name"
|
pick-a-unique-name: "Please pick a more unique name"
|
||||||
|
stripped-char-in-unique-name: "&c Some chars were removed because they are not allowed. &a New ID will be &b [name]&a."
|
||||||
success: "Success!"
|
success: "Success!"
|
||||||
conversation-prefix: ">"
|
conversation-prefix: ">"
|
||||||
description:
|
description:
|
||||||
@ -421,6 +423,9 @@ commands:
|
|||||||
description: "removes deaths to the player"
|
description: "removes deaths to the player"
|
||||||
parameters: "<player> <deaths>"
|
parameters: "<player> <deaths>"
|
||||||
success: "&a Successfully removed &b [number] &a deaths to &b [name], decreasing the total to &b [total]&a deaths."
|
success: "&a Successfully removed &b [number] &a deaths to &b [name], decreasing the total to &b [total]&a deaths."
|
||||||
|
resetname:
|
||||||
|
description: "reset player island name"
|
||||||
|
success: "&a Successfully reset [name]'s island name."
|
||||||
bentobox:
|
bentobox:
|
||||||
description: "BentoBox admin command"
|
description: "BentoBox admin command"
|
||||||
about:
|
about:
|
||||||
@ -496,6 +501,7 @@ commands:
|
|||||||
estimated-time: "&a Estimated time: &b [number] &a seconds."
|
estimated-time: "&a Estimated time: &b [number] &a seconds."
|
||||||
blocks: "&a Building it block by block: &b [number] &a blocks in all..."
|
blocks: "&a Building it block by block: &b [number] &a blocks in all..."
|
||||||
entities: "&a Filling it with entities: &b [number] &a entities in all..."
|
entities: "&a Filling it with entities: &b [number] &a entities in all..."
|
||||||
|
dimension-done: "&a Island in [world] is constructed."
|
||||||
done: "&a Done! Your island is ready and waiting for you!"
|
done: "&a Done! Your island is ready and waiting for you!"
|
||||||
pick: "&2 Pick an island"
|
pick: "&2 Pick an island"
|
||||||
unknown-blueprint: "&c That blueprint has not been loaded yet."
|
unknown-blueprint: "&c That blueprint has not been loaded yet."
|
||||||
@ -731,6 +737,10 @@ ranks:
|
|||||||
protection:
|
protection:
|
||||||
command-is-banned: "Command is banned for visitors"
|
command-is-banned: "Command is banned for visitors"
|
||||||
flags:
|
flags:
|
||||||
|
ALLAY:
|
||||||
|
name: "Allay interaction"
|
||||||
|
description: "Allow giving and taking items to/from Allay"
|
||||||
|
hint: "Allay interaction disabled"
|
||||||
ANIMAL_NATURAL_SPAWN:
|
ANIMAL_NATURAL_SPAWN:
|
||||||
description: "Toggle natural animal spawning"
|
description: "Toggle natural animal spawning"
|
||||||
name: "Animal natural spawn"
|
name: "Animal natural spawn"
|
||||||
@ -745,6 +755,10 @@ protection:
|
|||||||
description: "Toggle interaction"
|
description: "Toggle interaction"
|
||||||
name: "Armor stands"
|
name: "Armor stands"
|
||||||
hint: "Armor stand use disabled"
|
hint: "Armor stand use disabled"
|
||||||
|
AXOLOTL_SCOOPING:
|
||||||
|
name: "Axolotl Scooping"
|
||||||
|
description: "Allow scooping of axolotl using a bucket"
|
||||||
|
hint: "Axolotl scooping disabled"
|
||||||
BEACON:
|
BEACON:
|
||||||
description: "Toggle interaction"
|
description: "Toggle interaction"
|
||||||
name: "Beacons"
|
name: "Beacons"
|
||||||
@ -889,6 +903,12 @@ protection:
|
|||||||
&a (override Buckets)
|
&a (override Buckets)
|
||||||
name: "Collect water"
|
name: "Collect water"
|
||||||
hint: "Water buckets disabled"
|
hint: "Water buckets disabled"
|
||||||
|
COLLECT_POWDERED_SNOW:
|
||||||
|
description: |-
|
||||||
|
&a Toggle collecting powdered snow
|
||||||
|
&a (override Buckets)
|
||||||
|
name: "Collect powdered snow"
|
||||||
|
hint: "Powdered snow buckets disabled"
|
||||||
COMMAND_RANKS:
|
COMMAND_RANKS:
|
||||||
name: "&e Command Ranks"
|
name: "&e Command Ranks"
|
||||||
description: "&a Configure command ranks"
|
description: "&a Configure command ranks"
|
||||||
@ -1271,6 +1291,18 @@ protection:
|
|||||||
&a using spawn eggs.
|
&a using spawn eggs.
|
||||||
name: "Spawn eggs on spawners"
|
name: "Spawn eggs on spawners"
|
||||||
hint: "changing a spawner's entity type using spawn eggs is not allowed"
|
hint: "changing a spawner's entity type using spawn eggs is not allowed"
|
||||||
|
SCULK_SENSOR:
|
||||||
|
description: |-
|
||||||
|
&a Toggles sculk sensor
|
||||||
|
&a activation.
|
||||||
|
name: "Sculk Sensor"
|
||||||
|
hint: "sculk sensor activation is disabled"
|
||||||
|
SCULK_SHRIEKER:
|
||||||
|
description: |-
|
||||||
|
&a Toggles sculk shrieker
|
||||||
|
&a activation.
|
||||||
|
name: "Sculk Shrieker"
|
||||||
|
hint: "sculk shrieker activation is disabled"
|
||||||
TNT_DAMAGE:
|
TNT_DAMAGE:
|
||||||
description: |-
|
description: |-
|
||||||
&a Allow TNT and TNT minecarts
|
&a Allow TNT and TNT minecarts
|
||||||
@ -1330,6 +1362,20 @@ protection:
|
|||||||
&a
|
&a
|
||||||
&a Island members still lose their items
|
&a Island members still lose their items
|
||||||
&a if they die on their own island!
|
&a if they die on their own island!
|
||||||
|
VISITOR_TRIGGER_RAID:
|
||||||
|
name: "Visitors triggers raids"
|
||||||
|
description: |-
|
||||||
|
&a Toggles if visitors can start
|
||||||
|
&a a raid on an island which they are
|
||||||
|
&a visiting.
|
||||||
|
&a
|
||||||
|
&a Bad Omen effect will be removed!
|
||||||
|
ENTITY_PORTAL_TELEPORT:
|
||||||
|
name: "Entity portal usage"
|
||||||
|
description: |-
|
||||||
|
&a Toggles if entities (non-player) can
|
||||||
|
&a use portals to teleport between
|
||||||
|
&a dimensions
|
||||||
WITHER_DAMAGE:
|
WITHER_DAMAGE:
|
||||||
name: "Toggle wither damage"
|
name: "Toggle wither damage"
|
||||||
description: |-
|
description: |-
|
||||||
|
@ -362,6 +362,7 @@ commands:
|
|||||||
prompt: Ingrese un nombre o 'quit' para salir
|
prompt: Ingrese un nombre o 'quit' para salir
|
||||||
too-long: "&cDemasiado largo"
|
too-long: "&cDemasiado largo"
|
||||||
pick-a-unique-name: Elige un nombre más exclusivo
|
pick-a-unique-name: Elige un nombre más exclusivo
|
||||||
|
invalid-char-in-unique-name: "Unique name cannot contain, start, or end with special characters, neither contain number! "
|
||||||
success: "¡Éxito!"
|
success: "¡Éxito!"
|
||||||
conversation-prefix: ">"
|
conversation-prefix: ">"
|
||||||
description:
|
description:
|
||||||
|
@ -86,6 +86,7 @@ commands:
|
|||||||
name:
|
name:
|
||||||
conversation-prefix: ">"
|
conversation-prefix: ">"
|
||||||
pick-a-unique-name: Veuillez choisir un nom plus unique
|
pick-a-unique-name: Veuillez choisir un nom plus unique
|
||||||
|
invalid-char-in-unique-name: "Unique name cannot contain, start, or end with special characters, neither contain number! "
|
||||||
prompt: Entrez un nom, ou "quitter" pour quitter
|
prompt: Entrez un nom, ou "quitter" pour quitter
|
||||||
quit: quitter
|
quit: quitter
|
||||||
success: Succès !
|
success: Succès !
|
||||||
|
@ -78,6 +78,7 @@ commands:
|
|||||||
name:
|
name:
|
||||||
conversation-prefix: ">"
|
conversation-prefix: ">"
|
||||||
pick-a-unique-name: Scegli un nome unico
|
pick-a-unique-name: Scegli un nome unico
|
||||||
|
invalid-char-in-unique-name: "Unique name cannot contain, start, or end with special characters, neither contain number! "
|
||||||
prompt: Inserisci un nome, o 'quit' per uscire
|
prompt: Inserisci un nome, o 'quit' per uscire
|
||||||
success: Successo!
|
success: Successo!
|
||||||
too-long: "&cTroppo lungo"
|
too-long: "&cTroppo lungo"
|
||||||
|
@ -328,6 +328,7 @@ commands:
|
|||||||
prompt: 名前を入力するか、「quit」で終了します
|
prompt: 名前を入力するか、「quit」で終了します
|
||||||
too-long: "&c長すぎる"
|
too-long: "&c長すぎる"
|
||||||
pick-a-unique-name: よりユニークな名前を選んでください
|
pick-a-unique-name: よりユニークな名前を選んでください
|
||||||
|
invalid-char-in-unique-name: "Unique name cannot contain, start, or end with special characters, neither contain number! "
|
||||||
success: 成功!
|
success: 成功!
|
||||||
conversation-prefix: ">"
|
conversation-prefix: ">"
|
||||||
description:
|
description:
|
||||||
|
@ -337,6 +337,7 @@ commands:
|
|||||||
prompt: 이름을 입력하세요, quit를 입력하여 종료할수 있습니다
|
prompt: 이름을 입력하세요, quit를 입력하여 종료할수 있습니다
|
||||||
too-long: "&c 너무 깁니다"
|
too-long: "&c 너무 깁니다"
|
||||||
pick-a-unique-name: 더 독특한 이름을 선택하십시오
|
pick-a-unique-name: 더 독특한 이름을 선택하십시오
|
||||||
|
invalid-char-in-unique-name: "Unique name cannot contain, start, or end with special characters, neither contain number! "
|
||||||
success: 완료!
|
success: 완료!
|
||||||
conversation-prefix: ">"
|
conversation-prefix: ">"
|
||||||
description:
|
description:
|
||||||
|
@ -90,6 +90,7 @@ commands:
|
|||||||
name:
|
name:
|
||||||
conversation-prefix: ">"
|
conversation-prefix: ">"
|
||||||
pick-a-unique-name: Lūdzu izvēlies unikālu nosaukumu
|
pick-a-unique-name: Lūdzu izvēlies unikālu nosaukumu
|
||||||
|
invalid-char-in-unique-name: "Unique name cannot contain, start, or end with special characters, neither contain number! "
|
||||||
prompt: Ieraksti vārdu vai 'iziet', lai izietu
|
prompt: Ieraksti vārdu vai 'iziet', lai izietu
|
||||||
quit: iziet
|
quit: iziet
|
||||||
success: Izdevās!
|
success: Izdevās!
|
||||||
|
@ -393,6 +393,7 @@ commands:
|
|||||||
prompt: Voer een naam in of 'quit' om te stoppen
|
prompt: Voer een naam in of 'quit' om te stoppen
|
||||||
too-long: "&c Te lang"
|
too-long: "&c Te lang"
|
||||||
pick-a-unique-name: Kies een meer unieke naam
|
pick-a-unique-name: Kies een meer unieke naam
|
||||||
|
invalid-char-in-unique-name: "Unique name cannot contain, start, or end with special characters, neither contain number! "
|
||||||
success: Succes!
|
success: Succes!
|
||||||
conversation-prefix: ">"
|
conversation-prefix: ">"
|
||||||
description:
|
description:
|
||||||
|
@ -345,6 +345,7 @@ commands:
|
|||||||
prompt: Wprowadź nazwę, lub wpisz 'wyjdź', by wyjść
|
prompt: Wprowadź nazwę, lub wpisz 'wyjdź', by wyjść
|
||||||
too-long: '&cNazwa zbyt długa'
|
too-long: '&cNazwa zbyt długa'
|
||||||
pick-a-unique-name: Wybierz bardziej unikalną nazwę
|
pick-a-unique-name: Wybierz bardziej unikalną nazwę
|
||||||
|
invalid-char-in-unique-name: "Unique name cannot contain, start, or end with special characters, neither contain number! "
|
||||||
success: Sukces!
|
success: Sukces!
|
||||||
conversation-prefix: '>'
|
conversation-prefix: '>'
|
||||||
description:
|
description:
|
||||||
|
@ -354,6 +354,7 @@ commands:
|
|||||||
prompt: Digite um nome, ou 'quit' para sair
|
prompt: Digite um nome, ou 'quit' para sair
|
||||||
too-long: '&c Muito comprido'
|
too-long: '&c Muito comprido'
|
||||||
pick-a-unique-name: Por favor escolha um nome único
|
pick-a-unique-name: Por favor escolha um nome único
|
||||||
|
invalid-char-in-unique-name: "Unique name cannot contain, start, or end with special characters, neither contain number! "
|
||||||
success: Sucesso!
|
success: Sucesso!
|
||||||
conversation-prefix: '>'
|
conversation-prefix: '>'
|
||||||
description:
|
description:
|
||||||
|
@ -373,6 +373,7 @@ commands:
|
|||||||
prompt: Introduceți un nume sau „renunțați” pentru a renunța
|
prompt: Introduceți un nume sau „renunțați” pentru a renunța
|
||||||
too-long: "&c Prea mult"
|
too-long: "&c Prea mult"
|
||||||
pick-a-unique-name: Vă rugăm să alegeți un nume mai unic
|
pick-a-unique-name: Vă rugăm să alegeți un nume mai unic
|
||||||
|
invalid-char-in-unique-name: "Unique name cannot contain, start, or end with special characters, neither contain number! "
|
||||||
success: Succes!
|
success: Succes!
|
||||||
conversation-prefix: ">"
|
conversation-prefix: ">"
|
||||||
description:
|
description:
|
||||||
|
@ -384,6 +384,7 @@ commands:
|
|||||||
prompt: İsim gir ya da çıkmak için 'quit' yaz.
|
prompt: İsim gir ya da çıkmak için 'quit' yaz.
|
||||||
too-long: "&cÇok uzun."
|
too-long: "&cÇok uzun."
|
||||||
pick-a-unique-name: Lütfen daha benzersiz bir ad seçin
|
pick-a-unique-name: Lütfen daha benzersiz bir ad seçin
|
||||||
|
invalid-char-in-unique-name: "Unique name cannot contain, start, or end with special characters, neither contain number! "
|
||||||
success: Başarılı!
|
success: Başarılı!
|
||||||
conversation-prefix: ">"
|
conversation-prefix: ">"
|
||||||
description:
|
description:
|
||||||
|
@ -376,6 +376,7 @@ commands:
|
|||||||
prompt: Enter a name, or 'quit' to quit
|
prompt: Enter a name, or 'quit' to quit
|
||||||
too-long: "&c Too long"
|
too-long: "&c Too long"
|
||||||
pick-a-unique-name: Please pick a more unique name
|
pick-a-unique-name: Please pick a more unique name
|
||||||
|
invalid-char-in-unique-name: "Unique name cannot contain, start, or end with special characters, neither contain number! "
|
||||||
success: Success!
|
success: Success!
|
||||||
conversation-prefix: ">"
|
conversation-prefix: ">"
|
||||||
description:
|
description:
|
||||||
|
@ -367,6 +367,7 @@ commands:
|
|||||||
prompt: "&e请输入新名称, 或 “&b quit&e” 来退出编辑。"
|
prompt: "&e请输入新名称, 或 “&b quit&e” 来退出编辑。"
|
||||||
too-long: "&c新名称太长了!"
|
too-long: "&c新名称太长了!"
|
||||||
pick-a-unique-name: "&c这个名称已存在, 请另选一个不同的名称!"
|
pick-a-unique-name: "&c这个名称已存在, 请另选一个不同的名称!"
|
||||||
|
invalid-char-in-unique-name: "Unique name cannot contain, start, or end with special characters, neither contain number! "
|
||||||
success: "&a成功!"
|
success: "&a成功!"
|
||||||
conversation-prefix: "&3> &r"
|
conversation-prefix: "&3> &r"
|
||||||
description:
|
description:
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
name: BentoBox
|
name: BentoBox
|
||||||
main: world.bentobox.bentobox.BentoBox
|
main: world.bentobox.bentobox.BentoBox
|
||||||
version: ${project.version}${build.number}
|
version: ${project.version}${build.number}
|
||||||
api-version: "1.17"
|
api-version: "1.18"
|
||||||
|
|
||||||
authors: [tastybento, Poslovitch]
|
authors: [tastybento, Poslovitch]
|
||||||
contributors: ["The BentoBoxWorld Community"]
|
contributors: ["The BentoBoxWorld Community"]
|
||||||
@ -17,7 +17,6 @@ softdepend:
|
|||||||
- Vault
|
- Vault
|
||||||
- PlaceholderAPI
|
- PlaceholderAPI
|
||||||
- dynmap
|
- dynmap
|
||||||
- WorldEdit
|
|
||||||
- WorldBorderAPI
|
- WorldBorderAPI
|
||||||
- BsbMongo
|
- BsbMongo
|
||||||
- WorldGeneratorApi
|
- WorldGeneratorApi
|
||||||
@ -28,6 +27,11 @@ softdepend:
|
|||||||
- HolographicDisplays
|
- HolographicDisplays
|
||||||
- EconomyPlus
|
- EconomyPlus
|
||||||
|
|
||||||
|
libraries:
|
||||||
|
- mysql:mysql-connector-java:8.0.27
|
||||||
|
- org.mongodb:mongodb-driver:${mongodb.version}
|
||||||
|
- postgresql:postgresql:9.1-901-1.jdbc4
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
bentobox.admin:
|
bentobox.admin:
|
||||||
description: Allows admin command usage
|
description: Allows admin command usage
|
||||||
|
@ -24,6 +24,7 @@ import org.bukkit.Material;
|
|||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.World.Environment;
|
import org.bukkit.World.Environment;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
|
import org.bukkit.util.Vector;
|
||||||
import org.eclipse.jdt.annotation.NonNull;
|
import org.eclipse.jdt.annotation.NonNull;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@ -88,9 +89,11 @@ public class IslandTest {
|
|||||||
when(iwm.getIslandDistance(any())).thenReturn(DISTANCE);
|
when(iwm.getIslandDistance(any())).thenReturn(DISTANCE);
|
||||||
|
|
||||||
// Location
|
// Location
|
||||||
//when(location.getWorld()).thenReturn(world);
|
|
||||||
when(location.clone()).thenReturn(location);
|
when(location.clone()).thenReturn(location);
|
||||||
when(world.getName()).thenReturn("bskyblock_world");
|
when(world.getName()).thenReturn("bskyblock_world");
|
||||||
|
when(location.getWorld()).thenReturn(world);
|
||||||
|
when(world.getEnvironment()).thenReturn(Environment.NORMAL);
|
||||||
|
when(world.toString()).thenReturn(null);
|
||||||
|
|
||||||
// User
|
// User
|
||||||
when(user.getUniqueId()).thenReturn(uuid);
|
when(user.getUniqueId()).thenReturn(uuid);
|
||||||
@ -419,7 +422,7 @@ public class IslandTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testGetWorld() {
|
public void testGetWorld() {
|
||||||
assertNull(i.getWorld());
|
assertEquals(i.getWorld(), world);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -564,6 +567,7 @@ public class IslandTest {
|
|||||||
public void testGetProtectionBoundingBox() {
|
public void testGetProtectionBoundingBox() {
|
||||||
i.setWorld(world);
|
i.setWorld(world);
|
||||||
assertNotNull(i.getProtectionBoundingBox());
|
assertNotNull(i.getProtectionBoundingBox());
|
||||||
|
assertEquals("BoundingBox [minX=-100.0, minY=0.0, minZ=-100.0, maxX=100.0, maxY=0.0, maxZ=100.0]", i.getProtectionBoundingBox().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1018,7 +1022,7 @@ public class IslandTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testSetCooldowns() {
|
public void testSetCooldowns() {
|
||||||
i.setCooldowns(Collections.singletonMap(Flags.BREAK_BLOCKS, 123L));
|
i.setCooldowns(Collections.singletonMap(Flags.BREAK_BLOCKS.getID(), 123L));
|
||||||
assertFalse(i.getCooldowns().isEmpty());
|
assertFalse(i.getCooldowns().isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,13 +110,13 @@ public class GeoMobLimitTabTest {
|
|||||||
assertEquals("AXOLOTL", list.get(0));
|
assertEquals("AXOLOTL", list.get(0));
|
||||||
|
|
||||||
// Click on AXOLOTL
|
// Click on AXOLOTL
|
||||||
tab.onClick(panel, user, ClickType.LEFT, 9);
|
tab.onClick(panel, user, ClickType.LEFT, 10);
|
||||||
list.forEach(System.out::println);
|
list.forEach(System.out::println);
|
||||||
assertEquals(2, list.size());
|
assertEquals(2, list.size());
|
||||||
assertEquals("COW", list.get(1));
|
assertEquals("COW", list.get(1));
|
||||||
assertEquals("BAT", list.get(0));
|
assertEquals("BAT", list.get(0));
|
||||||
// Click on AXOLOTL again to have it added
|
// Click on AXOLOTL again to have it added
|
||||||
tab.onClick(panel, user, ClickType.LEFT, 9);
|
tab.onClick(panel, user, ClickType.LEFT, 10);
|
||||||
assertEquals(3, list.size());
|
assertEquals(3, list.size());
|
||||||
assertEquals("BAT", list.get(0));
|
assertEquals("BAT", list.get(0));
|
||||||
assertEquals("COW", list.get(1));
|
assertEquals("COW", list.get(1));
|
||||||
|
@ -69,7 +69,7 @@ public class BlockInteractionListenerTest extends AbstractCommonSetup {
|
|||||||
clickedBlocks.put(Material.WHITE_BED, Flags.BED);
|
clickedBlocks.put(Material.WHITE_BED, Flags.BED);
|
||||||
when(Tag.BEDS.isTagged(Material.WHITE_BED)).thenReturn(true);
|
when(Tag.BEDS.isTagged(Material.WHITE_BED)).thenReturn(true);
|
||||||
clickedBlocks.put(Material.BREWING_STAND, Flags.BREWING);
|
clickedBlocks.put(Material.BREWING_STAND, Flags.BREWING);
|
||||||
clickedBlocks.put(Material.CAULDRON, Flags.BREWING);
|
clickedBlocks.put(Material.WATER_CAULDRON, Flags.COLLECT_WATER);
|
||||||
clickedBlocks.put(Material.BARREL, Flags.BARREL);
|
clickedBlocks.put(Material.BARREL, Flags.BARREL);
|
||||||
clickedBlocks.put(Material.CHEST, Flags.CHEST);
|
clickedBlocks.put(Material.CHEST, Flags.CHEST);
|
||||||
clickedBlocks.put(Material.CHEST_MINECART, Flags.CHEST);
|
clickedBlocks.put(Material.CHEST_MINECART, Flags.CHEST);
|
||||||
@ -87,6 +87,7 @@ public class BlockInteractionListenerTest extends AbstractCommonSetup {
|
|||||||
clickedBlocks.put(Material.IRON_TRAPDOOR, Flags.TRAPDOOR);
|
clickedBlocks.put(Material.IRON_TRAPDOOR, Flags.TRAPDOOR);
|
||||||
when(Tag.TRAPDOORS.isTagged(Material.IRON_TRAPDOOR)).thenReturn(true);
|
when(Tag.TRAPDOORS.isTagged(Material.IRON_TRAPDOOR)).thenReturn(true);
|
||||||
clickedBlocks.put(Material.SPRUCE_FENCE_GATE, Flags.GATE);
|
clickedBlocks.put(Material.SPRUCE_FENCE_GATE, Flags.GATE);
|
||||||
|
when(Tag.FENCE_GATES.isTagged(Material.SPRUCE_FENCE_GATE)).thenReturn(true);
|
||||||
clickedBlocks.put(Material.BLAST_FURNACE, Flags.FURNACE);
|
clickedBlocks.put(Material.BLAST_FURNACE, Flags.FURNACE);
|
||||||
clickedBlocks.put(Material.CAMPFIRE, Flags.FURNACE);
|
clickedBlocks.put(Material.CAMPFIRE, Flags.FURNACE);
|
||||||
clickedBlocks.put(Material.FURNACE_MINECART, Flags.FURNACE);
|
clickedBlocks.put(Material.FURNACE_MINECART, Flags.FURNACE);
|
||||||
@ -110,7 +111,9 @@ public class BlockInteractionListenerTest extends AbstractCommonSetup {
|
|||||||
clickedBlocks.put(Material.DRAGON_EGG, Flags.DRAGON_EGG);
|
clickedBlocks.put(Material.DRAGON_EGG, Flags.DRAGON_EGG);
|
||||||
clickedBlocks.put(Material.END_PORTAL_FRAME, Flags.PLACE_BLOCKS);
|
clickedBlocks.put(Material.END_PORTAL_FRAME, Flags.PLACE_BLOCKS);
|
||||||
clickedBlocks.put(Material.ITEM_FRAME, Flags.ITEM_FRAME);
|
clickedBlocks.put(Material.ITEM_FRAME, Flags.ITEM_FRAME);
|
||||||
|
clickedBlocks.put(Material.GLOW_ITEM_FRAME, Flags.ITEM_FRAME);
|
||||||
clickedBlocks.put(Material.SWEET_BERRY_BUSH, Flags.BREAK_BLOCKS);
|
clickedBlocks.put(Material.SWEET_BERRY_BUSH, Flags.BREAK_BLOCKS);
|
||||||
|
clickedBlocks.put(Material.CAVE_VINES, Flags.BREAK_BLOCKS);
|
||||||
clickedBlocks.put(Material.CAKE, Flags.CAKE);
|
clickedBlocks.put(Material.CAKE, Flags.CAKE);
|
||||||
clickedBlocks.put(Material.BEEHIVE, Flags.HIVE);
|
clickedBlocks.put(Material.BEEHIVE, Flags.HIVE);
|
||||||
clickedBlocks.put(Material.BEE_NEST, Flags.HIVE);
|
clickedBlocks.put(Material.BEE_NEST, Flags.HIVE);
|
||||||
@ -134,6 +137,8 @@ public class BlockInteractionListenerTest extends AbstractCommonSetup {
|
|||||||
// Nothing in hand right now
|
// Nothing in hand right now
|
||||||
when(item.getType()).thenReturn(Material.AIR);
|
when(item.getType()).thenReturn(Material.AIR);
|
||||||
when(player.getInventory()).thenReturn(inv);
|
when(player.getInventory()).thenReturn(inv);
|
||||||
|
when(inv.getItemInMainHand()).thenReturn(item);
|
||||||
|
when(inv.getItemInOffHand()).thenReturn(new ItemStack(Material.BUCKET));
|
||||||
|
|
||||||
// FlagsManager
|
// FlagsManager
|
||||||
setFlags();
|
setFlags();
|
||||||
|
@ -45,6 +45,8 @@ import world.bentobox.bentobox.managers.FlagsManager;
|
|||||||
import world.bentobox.bentobox.managers.IslandWorldManager;
|
import world.bentobox.bentobox.managers.IslandWorldManager;
|
||||||
import world.bentobox.bentobox.managers.IslandsManager;
|
import world.bentobox.bentobox.managers.IslandsManager;
|
||||||
import world.bentobox.bentobox.util.Util;
|
import world.bentobox.bentobox.util.Util;
|
||||||
|
import world.bentobox.bentobox.versions.ServerCompatibility;
|
||||||
|
|
||||||
|
|
||||||
@RunWith(PowerMockRunner.class)
|
@RunWith(PowerMockRunner.class)
|
||||||
@PrepareForTest( {BentoBox.class, Bukkit.class, Flags.class, Util.class })
|
@PrepareForTest( {BentoBox.class, Bukkit.class, Flags.class, Util.class })
|
||||||
@ -78,6 +80,10 @@ public class MobSpawnListenerTest {
|
|||||||
when(server.getWorld("world")).thenReturn(world);
|
when(server.getWorld("world")).thenReturn(world);
|
||||||
when(server.getVersion()).thenReturn("BSB_Mocking");
|
when(server.getVersion()).thenReturn("BSB_Mocking");
|
||||||
|
|
||||||
|
ServerCompatibility serverCompatibility = mock(ServerCompatibility.class);
|
||||||
|
Whitebox.setInternalState(ServerCompatibility.class, "instance", serverCompatibility);
|
||||||
|
when(serverCompatibility.getServerVersion()).thenReturn(ServerCompatibility.ServerVersion.V1_19);
|
||||||
|
|
||||||
PluginManager pim = mock(PluginManager.class);
|
PluginManager pim = mock(PluginManager.class);
|
||||||
|
|
||||||
ItemFactory itemFactory = mock(ItemFactory.class);
|
ItemFactory itemFactory = mock(ItemFactory.class);
|
||||||
|
@ -19,11 +19,7 @@ import java.util.Map;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.*;
|
||||||
import org.bukkit.Location;
|
|
||||||
import org.bukkit.Material;
|
|
||||||
import org.bukkit.Server;
|
|
||||||
import org.bukkit.World;
|
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
import org.bukkit.entity.Cow;
|
import org.bukkit.entity.Cow;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
@ -50,6 +46,7 @@ import world.bentobox.bentobox.Settings;
|
|||||||
import world.bentobox.bentobox.api.configuration.WorldSettings;
|
import world.bentobox.bentobox.api.configuration.WorldSettings;
|
||||||
import world.bentobox.bentobox.api.user.User;
|
import world.bentobox.bentobox.api.user.User;
|
||||||
import world.bentobox.bentobox.database.objects.Island;
|
import world.bentobox.bentobox.database.objects.Island;
|
||||||
|
import world.bentobox.bentobox.listeners.flags.AbstractCommonSetup;
|
||||||
import world.bentobox.bentobox.lists.Flags;
|
import world.bentobox.bentobox.lists.Flags;
|
||||||
import world.bentobox.bentobox.managers.FlagsManager;
|
import world.bentobox.bentobox.managers.FlagsManager;
|
||||||
import world.bentobox.bentobox.managers.IslandWorldManager;
|
import world.bentobox.bentobox.managers.IslandWorldManager;
|
||||||
@ -64,18 +61,25 @@ import world.bentobox.bentobox.util.Util;
|
|||||||
*/
|
*/
|
||||||
@RunWith(PowerMockRunner.class)
|
@RunWith(PowerMockRunner.class)
|
||||||
@PrepareForTest( {Bukkit.class, BentoBox.class, Flags.class, Util.class} )
|
@PrepareForTest( {Bukkit.class, BentoBox.class, Flags.class, Util.class} )
|
||||||
public class ChestDamageListenerTest {
|
public class ChestDamageListenerTest extends AbstractCommonSetup
|
||||||
|
{
|
||||||
|
|
||||||
private Location location;
|
private Location location;
|
||||||
private BentoBox plugin;
|
private BentoBox plugin;
|
||||||
private World world;
|
private World world;
|
||||||
|
|
||||||
|
@Override
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
|
||||||
// Set up plugin
|
// Set up plugin
|
||||||
plugin = mock(BentoBox.class);
|
plugin = mock(BentoBox.class);
|
||||||
Whitebox.setInternalState(BentoBox.class, "instance", plugin);
|
Whitebox.setInternalState(BentoBox.class, "instance", plugin);
|
||||||
|
|
||||||
|
// Tags
|
||||||
|
when(Tag.SHULKER_BOXES.isTagged(any(Material.class))).thenReturn(false);
|
||||||
|
|
||||||
Server server = mock(Server.class);
|
Server server = mock(Server.class);
|
||||||
world = mock(World.class);
|
world = mock(World.class);
|
||||||
when(server.getLogger()).thenReturn(Logger.getAnonymousLogger());
|
when(server.getLogger()).thenReturn(Logger.getAnonymousLogger());
|
||||||
@ -161,8 +165,6 @@ public class ChestDamageListenerTest {
|
|||||||
// Util
|
// Util
|
||||||
PowerMockito.mockStatic(Util.class);
|
PowerMockito.mockStatic(Util.class);
|
||||||
when(Util.getWorld(Mockito.any())).thenReturn(mock(World.class));
|
when(Util.getWorld(Mockito.any())).thenReturn(mock(World.class));
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
@ -39,6 +39,8 @@ import world.bentobox.bentobox.BentoBox;
|
|||||||
import world.bentobox.bentobox.api.user.User;
|
import world.bentobox.bentobox.api.user.User;
|
||||||
import world.bentobox.bentobox.blueprints.Blueprint;
|
import world.bentobox.bentobox.blueprints.Blueprint;
|
||||||
import world.bentobox.bentobox.blueprints.BlueprintClipboard;
|
import world.bentobox.bentobox.blueprints.BlueprintClipboard;
|
||||||
|
import world.bentobox.bentobox.util.Util;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author tastybento
|
* @author tastybento
|
||||||
@ -345,7 +347,7 @@ public class BlueprintClipboardManagerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link world.bentobox.bentobox.managers.BlueprintClipboardManager#save(world.bentobox.bentobox.api.user.User, java.lang.String)}.
|
* Test method for {@link world.bentobox.bentobox.managers.BlueprintClipboardManager#save(world.bentobox.bentobox.api.user.User, java.lang.String, java.lang.String)}.
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
@ -361,14 +363,14 @@ public class BlueprintClipboardManagerTest {
|
|||||||
BlueprintClipboardManager bcm = new BlueprintClipboardManager(plugin, blueprintFolder);
|
BlueprintClipboardManager bcm = new BlueprintClipboardManager(plugin, blueprintFolder);
|
||||||
bcm.load(BLUEPRINT);
|
bcm.load(BLUEPRINT);
|
||||||
User user = mock(User.class);
|
User user = mock(User.class);
|
||||||
assertTrue(bcm.save(user, "test1234"));
|
assertTrue(bcm.save(user, "test1234", ""));
|
||||||
File bp = new File(blueprintFolder, "test1234.blu");
|
File bp = new File(blueprintFolder, "test1234.blu");
|
||||||
assertTrue(bp.exists());
|
assertTrue(bp.exists());
|
||||||
verify(user).sendMessage("general.success");
|
verify(user).sendMessage("general.success");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link world.bentobox.bentobox.managers.BlueprintClipboardManager#save(world.bentobox.bentobox.api.user.User, java.lang.String)}.
|
* Test method for {@link world.bentobox.bentobox.managers.BlueprintClipboardManager#save(world.bentobox.bentobox.api.user.User, java.lang.String, java.lang.String)}.
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
@ -384,14 +386,14 @@ public class BlueprintClipboardManagerTest {
|
|||||||
BlueprintClipboardManager bcm = new BlueprintClipboardManager(plugin, blueprintFolder);
|
BlueprintClipboardManager bcm = new BlueprintClipboardManager(plugin, blueprintFolder);
|
||||||
bcm.load(BLUEPRINT);
|
bcm.load(BLUEPRINT);
|
||||||
User user = mock(User.class);
|
User user = mock(User.class);
|
||||||
assertTrue(bcm.save(user, "test.1234/../../film"));
|
assertTrue(bcm.save(user, Util.sanitizeInput("test.1234/../../film"), ""));
|
||||||
File bp = new File(blueprintFolder, "test1234film.blu");
|
File bp = new File(blueprintFolder, "test.1234_.._.._film.blu");
|
||||||
assertTrue(bp.exists());
|
assertTrue(bp.exists());
|
||||||
verify(user).sendMessage("general.success");
|
verify(user).sendMessage("general.success");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link world.bentobox.bentobox.managers.BlueprintClipboardManager#save(world.bentobox.bentobox.api.user.User, java.lang.String)}.
|
* Test method for {@link world.bentobox.bentobox.managers.BlueprintClipboardManager#save(world.bentobox.bentobox.api.user.User, java.lang.String, java.lang.String)}.
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
@ -407,14 +409,14 @@ public class BlueprintClipboardManagerTest {
|
|||||||
BlueprintClipboardManager bcm = new BlueprintClipboardManager(plugin, blueprintFolder);
|
BlueprintClipboardManager bcm = new BlueprintClipboardManager(plugin, blueprintFolder);
|
||||||
bcm.load(BLUEPRINT);
|
bcm.load(BLUEPRINT);
|
||||||
User user = mock(User.class);
|
User user = mock(User.class);
|
||||||
assertTrue(bcm.save(user, "日本語の言葉"));
|
assertTrue(bcm.save(user, "日本語の言葉", ""));
|
||||||
File bp = new File(blueprintFolder, "日本語の言葉.blu");
|
File bp = new File(blueprintFolder, "日本語の言葉.blu");
|
||||||
assertTrue(bp.exists());
|
assertTrue(bp.exists());
|
||||||
verify(user).sendMessage("general.success");
|
verify(user).sendMessage("general.success");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link world.bentobox.bentobox.managers.BlueprintClipboardManager#save(world.bentobox.bentobox.api.user.User, java.lang.String)}.
|
* Test method for {@link world.bentobox.bentobox.managers.BlueprintClipboardManager#save(world.bentobox.bentobox.api.user.User, java.lang.String, java.lang.String)}.
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
@ -430,8 +432,9 @@ public class BlueprintClipboardManagerTest {
|
|||||||
BlueprintClipboardManager bcm = new BlueprintClipboardManager(plugin, blueprintFolder);
|
BlueprintClipboardManager bcm = new BlueprintClipboardManager(plugin, blueprintFolder);
|
||||||
bcm.load(BLUEPRINT);
|
bcm.load(BLUEPRINT);
|
||||||
User user = mock(User.class);
|
User user = mock(User.class);
|
||||||
assertTrue(bcm.save(user, "日本語の言葉/../../../config"));
|
|
||||||
File bp = new File(blueprintFolder, "日本語の言葉config.blu");
|
assertTrue(bcm.save(user, Util.sanitizeInput("日本語の言葉/../../../config"), ""));
|
||||||
|
File bp = new File(blueprintFolder, "日本語の言葉_.._.._.._config.blu");
|
||||||
assertTrue(bp.exists());
|
assertTrue(bp.exists());
|
||||||
verify(user).sendMessage("general.success");
|
verify(user).sendMessage("general.success");
|
||||||
}
|
}
|
||||||
|
@ -526,8 +526,6 @@ public class BlueprintsManagerTest {
|
|||||||
BlueprintsManager bpm = new BlueprintsManager(plugin);
|
BlueprintsManager bpm = new BlueprintsManager(plugin);
|
||||||
bpm.addBlueprintBundle(addon, bb);
|
bpm.addBlueprintBundle(addon, bb);
|
||||||
assertEquals("bundle", bpm.validate(addon, "bundle"));
|
assertEquals("bundle", bpm.validate(addon, "bundle"));
|
||||||
// Mixed case
|
|
||||||
assertEquals("buNdle", bpm.validate(addon, "buNdle"));
|
|
||||||
// Not there
|
// Not there
|
||||||
assertNull(bpm.validate(addon, "buNdle2"));
|
assertNull(bpm.validate(addon, "buNdle2"));
|
||||||
}
|
}
|
||||||
@ -651,18 +649,19 @@ public class BlueprintsManagerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link world.bentobox.bentobox.managers.BlueprintsManager#renameBlueprint(world.bentobox.bentobox.api.addons.GameModeAddon, world.bentobox.bentobox.blueprints.Blueprint, java.lang.String)}.
|
* Test method for {@link world.bentobox.bentobox.managers.BlueprintsManager#renameBlueprint(world.bentobox.bentobox.api.addons.GameModeAddon, world.bentobox.bentobox.blueprints.Blueprint, java.lang.String, java.lang.String)}.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testRenameBlueprint() {
|
public void testRenameBlueprint() {
|
||||||
// Save it
|
// Save it
|
||||||
BlueprintsManager bpm = new BlueprintsManager(plugin);
|
BlueprintsManager bpm = new BlueprintsManager(plugin);
|
||||||
bpm.saveBlueprint(addon, defaultBp);
|
bpm.saveBlueprint(addon, defaultBp);
|
||||||
|
bpm.addBlueprint(addon, defaultBp);
|
||||||
File blueprints = new File(dataFolder, BlueprintsManager.FOLDER_NAME);
|
File blueprints = new File(dataFolder, BlueprintsManager.FOLDER_NAME);
|
||||||
File d = new File(blueprints, "bedrock.blu");
|
File d = new File(blueprints, "bedrock.blu");
|
||||||
assertTrue(d.exists());
|
assertTrue(d.exists());
|
||||||
// Rename it
|
// Rename it
|
||||||
bpm.renameBlueprint(addon, defaultBp, "bedrock2");
|
bpm.renameBlueprint(addon, defaultBp, "bedrock2", "");
|
||||||
assertFalse(d.exists());
|
assertFalse(d.exists());
|
||||||
d = new File(blueprints, "bedrock2.blu");
|
d = new File(blueprints, "bedrock2.blu");
|
||||||
assertTrue(d.exists());
|
assertTrue(d.exists());
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user