mirror of
https://github.com/BentoBoxWorld/BentoBox.git
synced 2024-11-01 00:10:40 +01:00
commit
fe888a8d0e
49
pom.xml
49
pom.xml
@ -66,23 +66,29 @@
|
||||
<java.version>17</java.version>
|
||||
<!-- Non-minecraft related dependencies -->
|
||||
<powermock.version>2.0.9</powermock.version>
|
||||
<!-- Database related dependencies -->
|
||||
<mongodb.version>3.12.8</mongodb.version>
|
||||
<mariadb.version>3.0.5</mariadb.version>
|
||||
<mysql.version>8.0.27</mysql.version>
|
||||
<postgresql.version>42.2.18</postgresql.version>
|
||||
<hikaricp.version>5.0.1</hikaricp.version>
|
||||
<!-- More visible way to change dependency versions -->
|
||||
<spigot.version>1.19.2-R0.1-SNAPSHOT</spigot.version>
|
||||
<spigot.version>1.19.3-R0.1-SNAPSHOT</spigot.version>
|
||||
<!-- Might differ from the last Spigot release for short periods
|
||||
of time -->
|
||||
<paper.version>1.19-R0.1-SNAPSHOT</paper.version>
|
||||
<bstats.version>2.2.1</bstats.version>
|
||||
<vault.version>1.7</vault.version>
|
||||
<bstats.version>3.0.0</bstats.version>
|
||||
<vault.version>1.7.1</vault.version>
|
||||
<placeholderapi.version>2.10.9</placeholderapi.version>
|
||||
<githubapi.version>d5f5e0bbd8</githubapi.version>
|
||||
<dynmap.version>3.0-SNAPSHOT</dynmap.version>
|
||||
<myworlds.version>1.19.3-v1</myworlds.version>
|
||||
<!-- Revision variable removes warning about dynamic version -->
|
||||
<revision>${build.version}-SNAPSHOT</revision>
|
||||
<!-- Do not change unless you want different name for local builds. -->
|
||||
<build.number>-LOCAL</build.number>
|
||||
<!-- This allows to change between versions. -->
|
||||
<build.version>1.21.1</build.version>
|
||||
<build.version>1.22.0</build.version>
|
||||
<sonar.organization>bentobox-world</sonar.organization>
|
||||
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
|
||||
</properties>
|
||||
@ -176,6 +182,11 @@
|
||||
<id>nms-repo</id>
|
||||
<url>https://repo.codemc.io/repository/nms/</url>
|
||||
</repository>
|
||||
<!-- Used for MyWorlds hook -->
|
||||
<repository>
|
||||
<id>MG-Dev Jenkins CI Maven Repository</id>
|
||||
<url>https://ci.mg-dev.eu/plugin/repository/everything</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
@ -197,7 +208,7 @@
|
||||
<dependency>
|
||||
<groupId>com.mojang</groupId>
|
||||
<artifactId>authlib</artifactId>
|
||||
<version>3.2.38</version>
|
||||
<version>3.16.29</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- Metrics -->
|
||||
@ -232,10 +243,11 @@
|
||||
<version>${mongodb.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- HikariCP database handler -->
|
||||
<dependency>
|
||||
<groupId>postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>9.1-901-1.jdbc4</version>
|
||||
<groupId>com.zaxxer</groupId>
|
||||
<artifactId>HikariCP</artifactId>
|
||||
<version>${hikaricp.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- Vault: as their maven repo is down, we need to get it from jitpack -->
|
||||
@ -260,6 +272,12 @@
|
||||
<version>${dynmap.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.bergerkiller.bukkit</groupId>
|
||||
<artifactId>MyWorlds</artifactId>
|
||||
<version>${myworlds.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- Shaded APIs -->
|
||||
<dependency>
|
||||
<groupId>com.github.TheBusyBiscuit</groupId>
|
||||
@ -269,7 +287,7 @@
|
||||
<dependency>
|
||||
<groupId>com.github.Marcono1234</groupId>
|
||||
<artifactId>gson-record-type-adapter-factory</artifactId>
|
||||
<version>0.1.0</version>
|
||||
<version>0.3.0</version>
|
||||
</dependency>
|
||||
<!-- Static analysis -->
|
||||
<!-- We are using Eclipse's annotations. If you're using IDEA, update
|
||||
@ -352,6 +370,7 @@
|
||||
<version>3.0.0-M5</version>
|
||||
<configuration>
|
||||
<argLine>
|
||||
${argLine}
|
||||
--add-opens java.base/java.lang=ALL-UNNAMED
|
||||
--add-opens java.base/java.math=ALL-UNNAMED
|
||||
--add-opens java.base/java.io=ALL-UNNAMED
|
||||
@ -390,10 +409,11 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<version>3.4.1</version>
|
||||
<configuration>
|
||||
<source>${java.version}</source>
|
||||
<show>private</show>
|
||||
<quiet>true</quiet>
|
||||
<failOnError>false</failOnError>
|
||||
<additionalJOption>-Xdoclint:none</additionalJOption>
|
||||
<!-- To compile with Java 11, this tag may be required -->
|
||||
@ -495,16 +515,21 @@
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>pre-unit-test</id>
|
||||
<id>prepare-agent</id>
|
||||
<goals>
|
||||
<goal>prepare-agent</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>post-unit-test</id>
|
||||
<id>report</id>
|
||||
<goals>
|
||||
<goal>report</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<formats>
|
||||
<format>XML</format>
|
||||
</formats>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
@ -20,6 +20,7 @@ import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.bentobox.commands.BentoBoxCommand;
|
||||
import world.bentobox.bentobox.database.DatabaseSetup;
|
||||
import world.bentobox.bentobox.hooks.MultiverseCoreHook;
|
||||
import world.bentobox.bentobox.hooks.MyWorldsHook;
|
||||
import world.bentobox.bentobox.hooks.VaultHook;
|
||||
import world.bentobox.bentobox.hooks.placeholders.PlaceholderAPIHook;
|
||||
import world.bentobox.bentobox.listeners.BannedCommands;
|
||||
@ -27,9 +28,9 @@ import world.bentobox.bentobox.listeners.BlockEndDragon;
|
||||
import world.bentobox.bentobox.listeners.DeathListener;
|
||||
import world.bentobox.bentobox.listeners.JoinLeaveListener;
|
||||
import world.bentobox.bentobox.listeners.PanelListenerManager;
|
||||
import world.bentobox.bentobox.listeners.StandardSpawnProtectionListener;
|
||||
import world.bentobox.bentobox.listeners.teleports.EntityTeleportListener;
|
||||
import world.bentobox.bentobox.listeners.teleports.PlayerTeleportListener;
|
||||
import world.bentobox.bentobox.listeners.StandardSpawnProtectionListener;
|
||||
import world.bentobox.bentobox.managers.AddonsManager;
|
||||
import world.bentobox.bentobox.managers.BlueprintsManager;
|
||||
import world.bentobox.bentobox.managers.CommandsManager;
|
||||
@ -225,6 +226,7 @@ public class BentoBox extends JavaPlugin {
|
||||
// Register Multiverse hook - MV loads AFTER BentoBox
|
||||
// Make sure all worlds are already registered to Multiverse.
|
||||
hooksManager.registerHook(new MultiverseCoreHook());
|
||||
hooksManager.registerHook(new MyWorldsHook());
|
||||
islandWorldManager.registerWorldsToMultiverse();
|
||||
|
||||
// TODO: re-enable after implementation
|
||||
|
@ -1,6 +1,8 @@
|
||||
package world.bentobox.bentobox;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bukkit.Material;
|
||||
@ -11,8 +13,10 @@ import world.bentobox.bentobox.api.configuration.ConfigObject;
|
||||
import world.bentobox.bentobox.api.configuration.StoreAt;
|
||||
import world.bentobox.bentobox.database.DatabaseSetup.DatabaseType;
|
||||
|
||||
|
||||
/**
|
||||
* All the plugin settings are here
|
||||
*
|
||||
* @author tastybento
|
||||
*/
|
||||
@StoreAt(filename="config.yml") // Explicitly call out what name this should have.
|
||||
@ -68,6 +72,7 @@ public class Settings implements ConfigObject {
|
||||
@ConfigComment("Transition options enable migration from one database type to another. Use /bbox migrate.")
|
||||
@ConfigComment("YAML and JSON are file-based databases.")
|
||||
@ConfigComment("MYSQL might not work with all implementations: if available, use a dedicated database type (e.g. MARIADB).")
|
||||
@ConfigComment("BentoBox uses HikariCP for connecting with SQL databases.")
|
||||
@ConfigComment("If you use MONGODB, you must also run the BSBMongo plugin (not addon).")
|
||||
@ConfigComment("See https://github.com/tastybento/bsbMongo/releases/.")
|
||||
@ConfigEntry(path = "general.database.type", video = "https://youtu.be/FFzCk5-y7-g")
|
||||
@ -107,6 +112,11 @@ public class Settings implements ConfigObject {
|
||||
@ConfigEntry(path = "general.database.max-saved-islands-per-tick")
|
||||
private int maxSavedIslandsPerTick = 20;
|
||||
|
||||
@ConfigComment("Number of active connections to the SQL database at the same time.")
|
||||
@ConfigComment("Default 10.")
|
||||
@ConfigEntry(path = "general.database.max-pool-size", since = "1.21.0")
|
||||
private int maximumPoolSize = 10;
|
||||
|
||||
@ConfigComment("Enable SSL connection to MongoDB, MariaDB, MySQL and PostgreSQL databases.")
|
||||
@ConfigEntry(path = "general.database.use-ssl", since = "1.12.0")
|
||||
private boolean useSSL = false;
|
||||
@ -118,6 +128,16 @@ public class Settings implements ConfigObject {
|
||||
@ConfigEntry(path = "general.database.prefix-character", since = "1.13.0")
|
||||
private String databasePrefix = "";
|
||||
|
||||
@ConfigComment("Custom connection datasource properties that will be applied to connection pool.")
|
||||
@ConfigComment("Check available values to your SQL driver implementation.")
|
||||
@ConfigComment("Example: ")
|
||||
@ConfigComment(" custom-properties: ")
|
||||
@ConfigComment(" cachePrepStmts: 'true'")
|
||||
@ConfigComment(" prepStmtCacheSize: '250'")
|
||||
@ConfigComment(" prepStmtCacheSqlLimit: '2048'")
|
||||
@ConfigEntry(path = "general.database.custom-properties", since = "1.21.0")
|
||||
private Map<String, String> customPoolProperties = new HashMap<>();
|
||||
|
||||
@ConfigComment("MongoDB client connection URI to override default connection options.")
|
||||
@ConfigComment("See: https://docs.mongodb.com/manual/reference/connection-string/")
|
||||
@ConfigEntry(path = "general.database.mongodb-connection-uri", since = "1.14.0")
|
||||
@ -954,6 +974,17 @@ public class Settings implements ConfigObject {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets maximum pool size.
|
||||
*
|
||||
* @return the maximum pool size
|
||||
*/
|
||||
public int getMaximumPoolSize()
|
||||
{
|
||||
return maximumPoolSize;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets safe spot search range.
|
||||
*
|
||||
@ -965,6 +996,39 @@ public class Settings implements ConfigObject {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets maximum pool size.
|
||||
*
|
||||
* @param maximumPoolSize the maximum pool size
|
||||
*/
|
||||
public void setMaximumPoolSize(int maximumPoolSize)
|
||||
{
|
||||
this.maximumPoolSize = maximumPoolSize;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets custom pool properties.
|
||||
*
|
||||
* @return the custom pool properties
|
||||
*/
|
||||
public Map<String, String> getCustomPoolProperties()
|
||||
{
|
||||
return customPoolProperties;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets custom pool properties.
|
||||
*
|
||||
* @param customPoolProperties the custom pool properties
|
||||
*/
|
||||
public void setCustomPoolProperties(Map<String, String> customPoolProperties)
|
||||
{
|
||||
this.customPoolProperties = customPoolProperties;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets safe spot search range.
|
||||
*
|
||||
|
@ -7,6 +7,7 @@ import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
@ -33,6 +34,19 @@ public class AddonClassLoader extends URLClassLoader {
|
||||
private final Addon addon;
|
||||
private final AddonsManager loader;
|
||||
|
||||
/**
|
||||
* For testing only
|
||||
* @param addon addon
|
||||
* @param loader Addons Manager
|
||||
* @param jarFile Jar File
|
||||
* @throws MalformedURLException exception
|
||||
*/
|
||||
protected AddonClassLoader(Addon addon, AddonsManager loader, File jarFile) throws MalformedURLException {
|
||||
super(new URL[]{jarFile.toURI().toURL()});
|
||||
this.addon = addon;
|
||||
this.loader = loader;
|
||||
}
|
||||
|
||||
public AddonClassLoader(AddonsManager addonsManager, YamlConfiguration data, File jarFile, ClassLoader parent)
|
||||
throws InvalidAddonInheritException,
|
||||
MalformedURLException,
|
||||
@ -79,8 +93,27 @@ public class AddonClassLoader extends URLClassLoader {
|
||||
*/
|
||||
@NonNull
|
||||
public static AddonDescription asDescription(YamlConfiguration data) throws InvalidAddonDescriptionException {
|
||||
AddonDescription.Builder builder = new AddonDescription.Builder(Objects.requireNonNull(data.getString("main")), Objects.requireNonNull(data.getString("name")), Objects.requireNonNull(data.getString("version")))
|
||||
// Validate addon.yml
|
||||
if (!data.contains("main")) {
|
||||
throw new InvalidAddonDescriptionException("Missing 'main' tag. A main class must be listed in addon.yml");
|
||||
}
|
||||
if (!data.contains("name")) {
|
||||
throw new InvalidAddonDescriptionException("Missing 'name' tag. An addon name must be listed in addon.yml");
|
||||
}
|
||||
if (!data.contains("version")) {
|
||||
throw new InvalidAddonDescriptionException("Missing 'version' tag. A version must be listed in addon.yml");
|
||||
}
|
||||
if (!data.contains("authors")) {
|
||||
throw new InvalidAddonDescriptionException("Missing 'authors' tag. At least one author must be listed in addon.yml");
|
||||
}
|
||||
|
||||
AddonDescription.Builder builder = new AddonDescription.Builder(
|
||||
// Mandatory elements
|
||||
Objects.requireNonNull(data.getString("main")),
|
||||
Objects.requireNonNull(data.getString("name")),
|
||||
Objects.requireNonNull(data.getString("version")))
|
||||
.authors(Objects.requireNonNull(data.getString("authors")))
|
||||
// Optional elements
|
||||
.metrics(data.getBoolean("metrics", true))
|
||||
.repository(data.getString("repository", ""));
|
||||
|
||||
@ -92,7 +125,7 @@ public class AddonClassLoader extends URLClassLoader {
|
||||
if (softDepend != null) {
|
||||
builder.softDependencies(Arrays.asList(softDepend.split("\\s*,\\s*")));
|
||||
}
|
||||
builder.icon(Objects.requireNonNull(Material.getMaterial(data.getString("icon", "PAPER"))));
|
||||
builder.icon(Objects.requireNonNull(Material.getMaterial(data.getString("icon", "PAPER").toUpperCase(Locale.ENGLISH))));
|
||||
|
||||
String apiVersion = data.getString("api-version");
|
||||
if (apiVersion != null) {
|
||||
|
@ -287,7 +287,7 @@ public final class AddonDescription {
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AddonDescription [" + (name != null ? "name=" + name + ", " : "")
|
||||
return "AddonDescription [" + "name=" + name + ", "
|
||||
+ "version=" + version + "]";
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.command.Command;
|
||||
@ -514,18 +513,6 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
|
||||
return onlyPlayer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to check if a user is a player
|
||||
* @param user - the User
|
||||
* @return true if sender is a player
|
||||
* @deprecated use {@link User#isPlayer()}
|
||||
* @forRemove 1.18.0
|
||||
*/
|
||||
@Deprecated
|
||||
protected boolean isPlayer(User user) {
|
||||
return user.isPlayer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether this command should only be run by players.
|
||||
* If this is set to {@code true}, this command will only be runnable by objects implementing {@link Player}.
|
||||
@ -663,7 +650,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
|
||||
/* ------------ */
|
||||
|
||||
String lastArg = args.length != 0 ? args[args.length - 1] : "";
|
||||
return Util.tabLimit(options, lastArg).stream().sorted().collect(Collectors.toList());
|
||||
return Util.tabLimit(options, lastArg).stream().sorted().toList();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -677,7 +664,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
|
||||
return command.getSubCommands().values().stream()
|
||||
.filter(cmd -> !cmd.isHidden())
|
||||
.filter(cmd -> !cmd.isOnlyPlayer() || sender.isOp() || (sender instanceof Player && cmd.getPermission() != null && (cmd.getPermission().isEmpty() || sender.hasPermission(cmd.getPermission()))) )
|
||||
.map(CompositeCommand::getLabel).collect(Collectors.toList());
|
||||
.map(CompositeCommand::getLabel).toList();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -83,7 +83,7 @@ public class AdminDeleteCommand extends ConfirmableCommand {
|
||||
// Remove them from this island (it still exists and will be deleted later)
|
||||
getIslands().removePlayer(getWorld(), targetUUID);
|
||||
if (target.isPlayer() && target.isOnline()) {
|
||||
cleanUp(user, target);
|
||||
cleanUp(target);
|
||||
}
|
||||
vector = oldIsland.getCenter().toVector();
|
||||
getIslands().deleteIsland(oldIsland, true, targetUUID);
|
||||
@ -95,7 +95,7 @@ public class AdminDeleteCommand extends ConfirmableCommand {
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanUp(User user, User target) {
|
||||
private void cleanUp(User target) {
|
||||
// Remove money inventory etc.
|
||||
if (getIWM().isOnLeaveResetEnderChest(getWorld())) {
|
||||
target.getPlayer().getEnderChest().clear();
|
||||
@ -122,7 +122,7 @@ public class AdminDeleteCommand extends ConfirmableCommand {
|
||||
}
|
||||
|
||||
// Execute commands when leaving
|
||||
Util.runCommands(target, getIWM().getOnLeaveCommands(getWorld()), "leave");
|
||||
Util.runCommands(target, target.getName(), getIWM().getOnLeaveCommands(getWorld()), "leave");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -55,11 +55,11 @@ public class AdminDeleteHomesCommand extends ConfirmableCommand {
|
||||
return false;
|
||||
}
|
||||
// Confirm
|
||||
askConfirmation(user, user.getTranslation("commands.admin.deletehomes.warning"), () -> deleteHomes(user, targetUUID, island));
|
||||
askConfirmation(user, user.getTranslation("commands.admin.deletehomes.warning"), () -> deleteHomes(user, island));
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean deleteHomes(User user, UUID targetUUID, Island island) {
|
||||
private boolean deleteHomes(User user, Island island) {
|
||||
island.removeHomes();
|
||||
user.sendMessage("general.success");
|
||||
return true;
|
||||
|
@ -3,7 +3,6 @@ package world.bentobox.bentobox.api.commands.admin;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
@ -98,7 +97,7 @@ public class AdminGetrankCommand extends CompositeCommand {
|
||||
return Optional.empty();
|
||||
}
|
||||
String lastArg = args.get(args.size() - 1);
|
||||
List<String> options = Bukkit.getOnlinePlayers().stream().map(Player::getName).collect(Collectors.toList());
|
||||
List<String> options = Bukkit.getOnlinePlayers().stream().map(Player::getName).toList();
|
||||
return Optional.of(Util.tabLimit(options, lastArg));
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package world.bentobox.bentobox.api.commands.admin;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
|
||||
@ -26,7 +25,7 @@ public class AdminResetFlagsCommand extends ConfirmableCommand {
|
||||
super(parent, "resetflags");
|
||||
options = getPlugin().getFlagsManager().getFlags().stream()
|
||||
.filter(f -> f.getType().equals(Type.PROTECTION) || f.getType().equals(Type.SETTING))
|
||||
.map(Flag::getID).collect(Collectors.toList());
|
||||
.map(Flag::getID).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,10 +1,11 @@
|
||||
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 org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
|
@ -50,7 +50,7 @@ public class AdminSetProtectionCenterCommand extends ConfirmableCommand
|
||||
public boolean canExecute(User user, String label, List<String> args) {
|
||||
if (args.size() == 3) {
|
||||
// Get location
|
||||
targetLoc = getLocation(user, args);
|
||||
targetLoc = getLocation(args);
|
||||
} else {
|
||||
targetLoc = new Location(getWorld(), user.getLocation().getBlockX(), user.getLocation().getBlockY(), user.getLocation().getBlockZ());
|
||||
}
|
||||
@ -67,7 +67,7 @@ public class AdminSetProtectionCenterCommand extends ConfirmableCommand
|
||||
return true;
|
||||
}
|
||||
|
||||
private Location getLocation(User user, List<String> args) {
|
||||
private Location getLocation(List<String> args) {
|
||||
try {
|
||||
int x = Integer.parseInt(args.get(0));
|
||||
int y = Integer.parseInt(args.get(1));
|
||||
|
@ -4,7 +4,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
@ -140,7 +139,7 @@ public class AdminSetrankCommand extends CompositeCommand {
|
||||
return Optional.of(getPlugin().getRanksManager().getRanks()
|
||||
.entrySet().stream()
|
||||
.filter(entry -> entry.getValue() > RanksManager.VISITOR_RANK)
|
||||
.map(entry -> user.getTranslation(entry.getKey())).collect(Collectors.toList()));
|
||||
.map(entry -> user.getTranslation(entry.getKey())).toList());
|
||||
}
|
||||
|
||||
// Return the player names again for the optional island owner argument
|
||||
|
@ -8,7 +8,6 @@ import java.util.Locale;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
@ -88,12 +87,10 @@ public class AdminSettingsCommand extends CompositeCommand {
|
||||
}
|
||||
|
||||
private boolean getIsland(User user, List<String> args) {
|
||||
if (args.get(0).equalsIgnoreCase(SPAWN_ISLAND)) {
|
||||
if (getIslands().getSpawn(getWorld()).isPresent()) {
|
||||
if (args.get(0).equalsIgnoreCase(SPAWN_ISLAND) && getIslands().getSpawn(getWorld()).isPresent()) {
|
||||
island = getIslands().getSpawn(getWorld()).get();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Get target player
|
||||
@Nullable UUID targetUUID = Util.getUUID(args.get(0));
|
||||
if (targetUUID == null) {
|
||||
@ -192,19 +189,18 @@ public class AdminSettingsCommand extends CompositeCommand {
|
||||
// Command line setting
|
||||
flag.ifPresent(f -> {
|
||||
switch (f.getType()) {
|
||||
case PROTECTION:
|
||||
case PROTECTION -> {
|
||||
island.setFlag(f, rank);
|
||||
getIslands().save(island);
|
||||
break;
|
||||
case SETTING:
|
||||
}
|
||||
case SETTING -> {
|
||||
island.setSettingsFlag(f, activeState);
|
||||
getIslands().save(island);
|
||||
break;
|
||||
case WORLD_SETTING:
|
||||
f.setSetting(getWorld(), activeState);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
case WORLD_SETTING -> f.setSetting(getWorld(), activeState);
|
||||
default -> {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
});
|
||||
user.sendMessage("general.success");
|
||||
@ -270,7 +266,7 @@ public class AdminSettingsCommand extends CompositeCommand {
|
||||
.getRanks().entrySet().stream()
|
||||
.filter(en -> en.getValue() > RanksManager.BANNED_RANK && en.getValue() <= RanksManager.OWNER_RANK)
|
||||
.map(Entry::getKey)
|
||||
.map(user::getTranslation).collect(Collectors.toList());
|
||||
.map(user::getTranslation).toList();
|
||||
case SETTING -> Arrays.asList(active, disabled);
|
||||
default -> Collections.<String>emptyList();
|
||||
}).orElse(Collections.emptyList());
|
||||
|
@ -9,7 +9,12 @@ 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.range.AdminRangeCommand;
|
||||
import world.bentobox.bentobox.api.commands.admin.resets.AdminResetsCommand;
|
||||
import world.bentobox.bentobox.api.commands.admin.team.*;
|
||||
import world.bentobox.bentobox.api.commands.admin.team.AdminTeamAddCommand;
|
||||
import world.bentobox.bentobox.api.commands.admin.team.AdminTeamCommand;
|
||||
import world.bentobox.bentobox.api.commands.admin.team.AdminTeamDisbandCommand;
|
||||
import world.bentobox.bentobox.api.commands.admin.team.AdminTeamFixCommand;
|
||||
import world.bentobox.bentobox.api.commands.admin.team.AdminTeamKickCommand;
|
||||
import world.bentobox.bentobox.api.commands.admin.team.AdminTeamSetownerCommand;
|
||||
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
|
||||
@ -25,7 +30,7 @@ public abstract class DefaultAdminCommand extends CompositeCommand {
|
||||
*
|
||||
* @param addon - GameMode addon
|
||||
*/
|
||||
public DefaultAdminCommand(GameModeAddon addon) {
|
||||
protected DefaultAdminCommand(GameModeAddon addon) {
|
||||
// Register command with alias from config.
|
||||
super(addon,
|
||||
addon.getWorldSettings().getAdminCommandAliases().split(" ")[0],
|
||||
|
@ -2,7 +2,6 @@ package world.bentobox.bentobox.api.commands.admin.blueprints;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
|
||||
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
|
||||
|
@ -2,13 +2,11 @@ package world.bentobox.bentobox.api.commands.admin.blueprints;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
|
||||
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.bentobox.blueprints.Blueprint;
|
||||
import world.bentobox.bentobox.blueprints.BlueprintClipboard;
|
||||
import world.bentobox.bentobox.managers.BlueprintsManager;
|
||||
import world.bentobox.bentobox.util.Util;
|
||||
|
||||
|
@ -51,7 +51,7 @@ public class AdminBlueprintSaveCommand extends ConfirmableCommand
|
||||
return false;
|
||||
}
|
||||
|
||||
if (clipboard.getBlueprint().getBedrock() == null)
|
||||
if (clipboard.getBlueprint() != null && clipboard.getBlueprint().getBedrock() == null)
|
||||
{
|
||||
// Bedrock is required for all blueprints.
|
||||
user.sendMessage("commands.admin.blueprint.bedrock-required");
|
||||
|
@ -17,8 +17,10 @@ import world.bentobox.bentobox.database.objects.Island;
|
||||
*/
|
||||
public class NamePrompt extends StringPrompt {
|
||||
|
||||
private @NonNull final Island island;
|
||||
private @NonNull final User user;
|
||||
@NonNull
|
||||
private final Island island;
|
||||
@NonNull
|
||||
private final User user;
|
||||
private final String oldName;
|
||||
private final BentoBox plugin;
|
||||
|
||||
@ -30,7 +32,8 @@ public class NamePrompt extends StringPrompt {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull String getPromptText(@NonNull ConversationContext context) {
|
||||
@NonNull
|
||||
public String getPromptText(@NonNull ConversationContext context) {
|
||||
return user.getTranslation("commands.island.renamehome.enter-new-name");
|
||||
}
|
||||
|
||||
|
@ -99,10 +99,10 @@ public class AdminRangeDisplayCommand extends CompositeCommand {
|
||||
// Draw 3 "stages" (one line below, at and above player's y coordinate)
|
||||
for (int stage = -1 ; stage <= 1 ; stage++) {
|
||||
for (int i = -range ; i <= range ; i++) {
|
||||
user.spawnParticle(particle, dustOptions, center.getBlockX() + i, playerY + stage, center.getBlockZ() + range);
|
||||
user.spawnParticle(particle, dustOptions, center.getBlockX() + i, playerY + stage, center.getBlockZ() - range);
|
||||
user.spawnParticle(particle, dustOptions, center.getBlockX() + range, playerY + stage, center.getBlockZ() + i);
|
||||
user.spawnParticle(particle, dustOptions, center.getBlockX() - range, playerY + stage, center.getBlockZ() + i);
|
||||
user.spawnParticle(particle, dustOptions, (double)center.getBlockX() + i, (double)playerY + stage, (double)center.getBlockZ() + range);
|
||||
user.spawnParticle(particle, dustOptions, (double)center.getBlockX() + i, (double)playerY + stage, (double)center.getBlockZ() - range);
|
||||
user.spawnParticle(particle, dustOptions, (double)center.getBlockX() + range, (double)playerY + stage, (double)center.getBlockZ() + i);
|
||||
user.spawnParticle(particle, dustOptions, (double)center.getBlockX() - range, (double)playerY + stage, (double)center.getBlockZ() + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Sound;
|
||||
@ -142,7 +141,7 @@ public class IslandBanCommand extends CompositeCommand {
|
||||
.filter(p -> !p.getUniqueId().equals(user.getUniqueId()))
|
||||
.filter(p -> !island.isBanned(p.getUniqueId()))
|
||||
.filter(p -> user.getPlayer().canSee(p))
|
||||
.map(Player::getName).collect(Collectors.toList());
|
||||
.map(Player::getName).toList();
|
||||
return Optional.of(Util.tabLimit(options, lastArg));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
|
@ -3,7 +3,6 @@ package world.bentobox.bentobox.api.commands.island;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||
@ -57,7 +56,7 @@ public class IslandBanlistCommand extends CompositeCommand {
|
||||
// Title
|
||||
user.sendMessage("commands.island.banlist.the-following");
|
||||
// Create a nicely formatted list
|
||||
List<String> names = island.getBanned().stream().map(u -> getPlayers().getName(u)).sorted().collect(Collectors.toList());
|
||||
List<String> names = island.getBanned().stream().map(u -> getPlayers().getName(u)).sorted().toList();
|
||||
List<String> lines = new ArrayList<>();
|
||||
StringBuilder line = new StringBuilder();
|
||||
// Put the names into lines of no more than 40 characters long, separated by commas
|
||||
|
@ -84,9 +84,9 @@ public class IslandDeletehomeCommand extends ConfirmableCommand {
|
||||
@Override
|
||||
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
|
||||
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
|
||||
Island island = getIslands().getIsland(getWorld(), user.getUniqueId());
|
||||
if (island != null) {
|
||||
return Optional.of(Util.tabLimit(new ArrayList<>(island.getHomes().keySet()), lastArg));
|
||||
Island is = getIslands().getIsland(getWorld(), user.getUniqueId());
|
||||
if (is != null) {
|
||||
return Optional.of(Util.tabLimit(new ArrayList<>(is.getHomes().keySet()), lastArg));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Sound;
|
||||
@ -156,7 +155,7 @@ public class IslandExpelCommand extends CompositeCommand {
|
||||
.filter(p -> !p.isOp()) // Not op
|
||||
.filter(p -> !p.hasPermission(this.getPermissionPrefix() + "admin.noexpel"))
|
||||
.filter(p -> !p.hasPermission(this.getPermissionPrefix() + "mod.bypassexpel"))
|
||||
.map(Player::getName).collect(Collectors.toList());
|
||||
.map(Player::getName).toList();
|
||||
|
||||
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
|
||||
return Optional.of(Util.tabLimit(options, lastArg));
|
||||
|
@ -55,14 +55,12 @@ public class IslandGoCommand extends DelayedTeleportCommand {
|
||||
user.sendMessage(Flags.PREVENT_TELEPORT_WHEN_FALLING.getHintReference());
|
||||
return false;
|
||||
}
|
||||
if (!args.isEmpty()) {
|
||||
if (!getIslands().isHomeLocation(island, String.join(" ", args))) {
|
||||
if (!args.isEmpty() && !getIslands().isHomeLocation(island, String.join(" ", args))) {
|
||||
user.sendMessage("commands.island.go.unknown-home");
|
||||
user.sendMessage("commands.island.sethome.homes-are");
|
||||
island.getHomes().keySet().stream().filter(s -> !s.isEmpty()).forEach(s -> user.sendMessage("commands.island.sethome.home-list-syntax", TextVariables.NAME, s));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -87,9 +87,9 @@ public class IslandRenamehomeCommand extends ConfirmableCommand {
|
||||
@Override
|
||||
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
|
||||
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
|
||||
Island island = getIslands().getIsland(getWorld(), user.getUniqueId());
|
||||
if (island != null) {
|
||||
return Optional.of(Util.tabLimit(new ArrayList<>(island.getHomes().keySet()), lastArg));
|
||||
Island is = getIslands().getIsland(getWorld(), user.getUniqueId());
|
||||
if (is != null) {
|
||||
return Optional.of(Util.tabLimit(new ArrayList<>(is.getHomes().keySet()), lastArg));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
@ -187,7 +187,7 @@ public class IslandResetCommand extends ConfirmableCommand {
|
||||
getIslands().removePlayer(getWorld(), memberUUID);
|
||||
|
||||
// Clean player
|
||||
getPlayers().cleanLeavingPlayer(getWorld(), member, false);
|
||||
getPlayers().cleanLeavingPlayer(getWorld(), member, false, island);
|
||||
|
||||
// Fire event
|
||||
TeamEvent.builder()
|
||||
|
@ -4,7 +4,6 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
@ -112,7 +111,7 @@ public class IslandUnbanCommand extends CompositeCommand {
|
||||
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
|
||||
Island island = getIslands().getIsland(getWorld(), user.getUniqueId());
|
||||
if (island != null) {
|
||||
List<String> options = island.getBanned().stream().map(getPlayers()::getName).collect(Collectors.toList());
|
||||
List<String> options = island.getBanned().stream().map(getPlayers()::getName).toList();
|
||||
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
|
||||
return Optional.of(Util.tabLimit(options, lastArg));
|
||||
} else {
|
||||
|
@ -7,7 +7,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
@ -91,12 +90,11 @@ public class IslandTeamCommand extends CompositeCommand {
|
||||
|
||||
private void showMembers(Island island, User user) {
|
||||
// Gather online members
|
||||
List<UUID> onlineMembers = island
|
||||
long count = island
|
||||
.getMemberSet(RanksManager.MEMBER_RANK)
|
||||
.stream()
|
||||
.filter(uuid -> Bukkit.getOfflinePlayer(uuid)
|
||||
.isOnline())
|
||||
.collect(Collectors.toList());
|
||||
.filter(uuid -> Util.getOnlinePlayerList(user).contains(Bukkit.getOfflinePlayer(uuid).getName()))
|
||||
.count();
|
||||
|
||||
// List of ranks that we will loop through
|
||||
Integer[] ranks = new Integer[]{RanksManager.OWNER_RANK, RanksManager.SUB_OWNER_RANK, RanksManager.MEMBER_RANK, RanksManager.TRUSTED_RANK, RanksManager.COOP_RANK};
|
||||
@ -105,11 +103,11 @@ public class IslandTeamCommand extends CompositeCommand {
|
||||
user.sendMessage("commands.island.team.info.header",
|
||||
"[max]", String.valueOf(getIslands().getMaxMembers(island, RanksManager.MEMBER_RANK)),
|
||||
"[total]", String.valueOf(island.getMemberSet().size()),
|
||||
"[online]", String.valueOf(onlineMembers.size()));
|
||||
"[online]", String.valueOf(count));
|
||||
|
||||
// We now need to get all online "members" of the island - incl. Trusted and coop
|
||||
onlineMembers = island.getMemberSet(RanksManager.COOP_RANK).stream()
|
||||
.filter(uuid -> Util.getOnlinePlayerList(user).contains(Bukkit.getOfflinePlayer(uuid).getName())).collect(Collectors.toList());
|
||||
List<UUID> onlineMembers = island.getMemberSet(RanksManager.COOP_RANK).stream()
|
||||
.filter(uuid -> Util.getOnlinePlayerList(user).contains(Bukkit.getOfflinePlayer(uuid).getName())).toList();
|
||||
|
||||
for (int rank : ranks) {
|
||||
Set<UUID> players = island.getMemberSet(rank, false);
|
||||
@ -123,6 +121,12 @@ public class IslandTeamCommand extends CompositeCommand {
|
||||
TextVariables.RANK, user.getTranslation(getPlugin().getRanksManager().getRank(rank)),
|
||||
TextVariables.NUMBER, String.valueOf(island.getMemberSet(rank, false).size()));
|
||||
}
|
||||
displayOnOffline(user, rank, island, onlineMembers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void displayOnOffline(User user, int rank, Island island, List<UUID> onlineMembers) {
|
||||
for (UUID member : island.getMemberSet(rank, false)) {
|
||||
OfflinePlayer offlineMember = Bukkit.getOfflinePlayer(member);
|
||||
if (onlineMembers.contains(member)) {
|
||||
@ -155,15 +159,14 @@ public class IslandTeamCommand extends CompositeCommand {
|
||||
user.sendMessage("commands.island.team.info.member-layout.offline",
|
||||
TextVariables.NAME, offlineMember.getName(),
|
||||
"[last_seen]", lastSeen);
|
||||
}else{
|
||||
} else {
|
||||
// This will prevent anyone that is trusted or below to not have a last-seen status
|
||||
user.sendMessage("commands.island.team.info.member-layout.offline-not-last-seen",
|
||||
TextVariables.NAME, offlineMember.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private boolean fireEvent(User user) {
|
||||
|
@ -96,7 +96,6 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
|
||||
// Remove the invite
|
||||
itc.removeInvite(playerUUID);
|
||||
User inviter = User.getInstance(invite.getInviter());
|
||||
if (inviter != null) {
|
||||
Island island = getIslands().getIsland(getWorld(), inviter);
|
||||
if (island != null) {
|
||||
if (island.getMemberSet(RanksManager.TRUSTED_RANK, false).size() > getIslands().getMaxMembers(island, RanksManager.TRUSTED_RANK)) {
|
||||
@ -111,7 +110,10 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
|
||||
.reason(IslandEvent.Reason.RANK_CHANGE)
|
||||
.rankChange(island.getRank(user), RanksManager.TRUSTED_RANK)
|
||||
.build();
|
||||
if (inviter.isOnline()) {
|
||||
inviter.sendMessage("commands.island.team.trust.success", TextVariables.NAME, user.getName());
|
||||
}
|
||||
if (inviter.isPlayer()) {
|
||||
user.sendMessage("commands.island.team.trust.you-are-trusted", TextVariables.NAME, inviter.getName());
|
||||
}
|
||||
}
|
||||
@ -121,7 +123,6 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
|
||||
// Remove the invite
|
||||
itc.removeInvite(playerUUID);
|
||||
User inviter = User.getInstance(invite.getInviter());
|
||||
if (inviter != null) {
|
||||
Island island = getIslands().getIsland(getWorld(), inviter);
|
||||
if (island != null) {
|
||||
if (island.getMemberSet(RanksManager.COOP_RANK, false).size() > getIslands().getMaxMembers(island, RanksManager.COOP_RANK)) {
|
||||
@ -136,7 +137,10 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
|
||||
.reason(IslandEvent.Reason.RANK_CHANGE)
|
||||
.rankChange(island.getRank(user), RanksManager.COOP_RANK)
|
||||
.build();
|
||||
if (inviter.isOnline()) {
|
||||
inviter.sendMessage("commands.island.team.coop.success", TextVariables.NAME, user.getName());
|
||||
}
|
||||
if (inviter.isPlayer()) {
|
||||
user.sendMessage("commands.island.team.coop.you-are-a-coop-member", TextVariables.NAME, inviter.getName());
|
||||
}
|
||||
}
|
||||
@ -153,7 +157,7 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
|
||||
user.sendMessage(INVALID_INVITE);
|
||||
return;
|
||||
}
|
||||
if (teamIsland.getMemberSet(RanksManager.MEMBER_RANK, true).size() > getIslands().getMaxMembers(teamIsland, RanksManager.MEMBER_RANK)) {
|
||||
if (teamIsland.getMemberSet(RanksManager.MEMBER_RANK, true).size() >= getIslands().getMaxMembers(teamIsland, RanksManager.MEMBER_RANK)) {
|
||||
user.sendMessage("commands.island.team.invite.errors.island-is-full");
|
||||
return;
|
||||
}
|
||||
@ -172,6 +176,10 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
|
||||
// Put player back into normal mode
|
||||
user.setGameMode(getIWM().getDefaultGameMode(getWorld()));
|
||||
|
||||
// Execute commands
|
||||
String ownerName = this.getPlayers().getName(teamIsland.getOwner());
|
||||
Util.runCommands(user, ownerName, getIWM().getOnJoinCommands(getWorld()), "join");
|
||||
|
||||
});
|
||||
// Reset deaths
|
||||
if (getIWM().isTeamJoinDeathReset(getWorld())) {
|
||||
@ -179,7 +187,7 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
|
||||
}
|
||||
user.sendMessage("commands.island.team.invite.accept.you-joined-island", TextVariables.LABEL, getTopLabel());
|
||||
User inviter = User.getInstance(invite.getInviter());
|
||||
if (inviter != null) {
|
||||
if (inviter.isOnline()) {
|
||||
inviter.sendMessage("commands.island.team.invite.accept.name-joined-your-island", TextVariables.NAME, user.getName());
|
||||
}
|
||||
getIslands().save(teamIsland);
|
||||
@ -224,7 +232,5 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
|
||||
user.getPlayer().setTotalExperience(0);
|
||||
}
|
||||
|
||||
// Execute commands
|
||||
Util.runCommands(user, getIWM().getOnJoinCommands(getWorld()), "join");
|
||||
}
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ public class IslandTeamInviteCommand extends CompositeCommand {
|
||||
return false;
|
||||
}
|
||||
// Check for space on team
|
||||
if (island.getMemberSet().size() > getIslands().getMaxMembers(island, RanksManager.MEMBER_RANK)) {
|
||||
if (island.getMemberSet().size() >= getIslands().getMaxMembers(island, RanksManager.MEMBER_RANK)) {
|
||||
user.sendMessage("commands.island.team.invite.errors.island-is-full");
|
||||
return false;
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
@ -103,7 +102,7 @@ public class IslandTeamKickCommand extends ConfirmableCommand {
|
||||
|
||||
getIslands().removePlayer(getWorld(), targetUUID);
|
||||
// Clean the target player
|
||||
getPlayers().cleanLeavingPlayer(getWorld(), target, true);
|
||||
getPlayers().cleanLeavingPlayer(getWorld(), target, true, oldIsland);
|
||||
|
||||
user.sendMessage("commands.island.team.kick.success", TextVariables.NAME, target.getName());
|
||||
IslandEvent.builder()
|
||||
@ -131,7 +130,7 @@ public class IslandTeamKickCommand extends ConfirmableCommand {
|
||||
List<String> options = island.getMemberSet().stream()
|
||||
.filter(uuid -> island.getRank(uuid) >= RanksManager.MEMBER_RANK)
|
||||
.map(Bukkit::getOfflinePlayer)
|
||||
.map(OfflinePlayer::getName).collect(Collectors.toList());
|
||||
.map(OfflinePlayer::getName).toList();
|
||||
|
||||
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
|
||||
return Optional.of(Util.tabLimit(options, lastArg));
|
||||
|
@ -82,7 +82,7 @@ public class IslandTeamLeaveCommand extends ConfirmableCommand {
|
||||
}
|
||||
getIslands().setLeaveTeam(getWorld(), user.getUniqueId());
|
||||
// Clean the player
|
||||
getPlayers().cleanLeavingPlayer(getWorld(), user, false);
|
||||
getPlayers().cleanLeavingPlayer(getWorld(), user, false, island);
|
||||
|
||||
// Add cooldown for this player and target
|
||||
if (getSettings().getInviteCooldown() > 0 && getParent() != null) {
|
||||
|
@ -3,7 +3,6 @@ package world.bentobox.bentobox.api.commands.island.team;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
@ -125,7 +124,7 @@ public class IslandTeamPromoteCommand extends CompositeCommand {
|
||||
if (island != null) {
|
||||
List<String> options = island.getMemberSet().stream()
|
||||
.map(Bukkit::getOfflinePlayer)
|
||||
.map(OfflinePlayer::getName).collect(Collectors.toList());
|
||||
.map(OfflinePlayer::getName).toList();
|
||||
|
||||
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
|
||||
return Optional.of(Util.tabLimit(options, lastArg));
|
||||
|
@ -4,7 +4,6 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
@ -114,7 +113,7 @@ public class IslandTeamUncoopCommand extends CompositeCommand {
|
||||
List<String> options = island.getMembers().entrySet().stream()
|
||||
.filter(e -> e.getValue() == RanksManager.COOP_RANK)
|
||||
.map(e -> Bukkit.getOfflinePlayer(e.getKey()))
|
||||
.map(OfflinePlayer::getName).collect(Collectors.toList());
|
||||
.map(OfflinePlayer::getName).toList();
|
||||
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
|
||||
return Optional.of(Util.tabLimit(options, lastArg));
|
||||
} else {
|
||||
|
@ -4,7 +4,6 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
@ -114,7 +113,7 @@ public class IslandTeamUntrustCommand extends CompositeCommand {
|
||||
List<String> options = island.getMembers().entrySet().stream()
|
||||
.filter(e -> e.getValue() == RanksManager.TRUSTED_RANK)
|
||||
.map(e -> Bukkit.getOfflinePlayer(e.getKey()))
|
||||
.map(OfflinePlayer::getName).collect(Collectors.toList());
|
||||
.map(OfflinePlayer::getName).toList();
|
||||
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
|
||||
return Optional.of(Util.tabLimit(options, lastArg));
|
||||
} else {
|
||||
|
@ -32,12 +32,12 @@ public interface WorldSettings extends ConfigObject {
|
||||
|
||||
/**
|
||||
* @return default rank settings for new islands
|
||||
* @deprecated since 1.21
|
||||
* Map of Flag, Integer does not allow to load other plugin/addon flags.
|
||||
* @deprecated 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()
|
||||
* @since 1.21.0
|
||||
*/
|
||||
@Deprecated
|
||||
@Deprecated(since="1.21.0", forRemoval=true)
|
||||
Map<Flag, Integer> getDefaultIslandFlags();
|
||||
|
||||
/**
|
||||
@ -57,12 +57,12 @@ public interface WorldSettings extends ConfigObject {
|
||||
|
||||
/**
|
||||
* @return default settings for new
|
||||
* @deprecated since 1.21
|
||||
* Map of Flag, Integer does not allow to load other plugin/addon flags.
|
||||
* @deprecated 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()
|
||||
* @since 1.21.0
|
||||
*/
|
||||
@Deprecated
|
||||
@Deprecated(since="1.21.0", forRemoval=true)
|
||||
Map<Flag, Integer> getDefaultIslandSettings();
|
||||
|
||||
/**
|
||||
@ -70,7 +70,7 @@ public interface WorldSettings extends ConfigObject {
|
||||
* 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
|
||||
* @since 1.21.0
|
||||
* @return default settings for new islands.
|
||||
*/
|
||||
default Map<String, Integer> getDefaultIslandSettingNames()
|
||||
@ -288,6 +288,8 @@ public interface WorldSettings extends ConfigObject {
|
||||
* Available placeholders for the commands are the following:
|
||||
* <ul>
|
||||
* <li>{@code [player]}: name of the player</li>
|
||||
* <li>{@code [owner]}: name of the owner of the island. When joining a team, this will be the team leader's name. When
|
||||
* creating an island, it is the name of the player</li>
|
||||
* </ul>
|
||||
* <br/>
|
||||
* Here are some examples of valid commands to execute:
|
||||
@ -345,6 +347,8 @@ public interface WorldSettings extends ConfigObject {
|
||||
* Available placeholders for the commands are the following:
|
||||
* <ul>
|
||||
* <li>{@code [player]}: name of the player</li>
|
||||
* <li>{@code [owner]}: name of the owner of the island. When joining a team, this will be the team leader's name. When
|
||||
* creating an island, it is the name of the player</li>
|
||||
* </ul>
|
||||
* <br/>
|
||||
* Here are some examples of valid commands to execute:
|
||||
@ -363,6 +367,22 @@ public interface WorldSettings extends ConfigObject {
|
||||
|
||||
/**
|
||||
* Returns a list of commands that should be executed when the player respawns after death if {@link Flags#ISLAND_RESPAWN} is true.<br/>
|
||||
* These commands are executed by the console, unless otherwise stated using the {@code [SUDO]} prefix, in which case they are executed by the player.<br/>
|
||||
* <br/>
|
||||
* Available placeholders for the commands are the following:
|
||||
* <ul>
|
||||
* <li>{@code [player]}: name of the player</li>
|
||||
* <li>{@code [owner]}: name of the owner of the island. When joining a team, this will be the team leader's name. When
|
||||
* creating an island, it is the name of the player</li>
|
||||
* </ul>
|
||||
* <br/>
|
||||
* Here are some examples of valid commands to execute:
|
||||
* <ul>
|
||||
* <li>{@code "[SUDO] bbox version"}</li>
|
||||
* <li>{@code "bsbadmin deaths set [player] 0"}</li>
|
||||
* </ul>
|
||||
* <br/>
|
||||
* Note that player-executed commands might not work, as these commands can be run with said player being offline.
|
||||
* @return a list of commands.
|
||||
* @since 1.14.0
|
||||
* @see #getOnJoinCommands()
|
||||
|
@ -53,8 +53,8 @@ public class Flag implements Comparable<Flag> {
|
||||
*/
|
||||
WORLD_SETTING(Material.GRASS_BLOCK);
|
||||
|
||||
private @NonNull
|
||||
final Material icon;
|
||||
@NonNull
|
||||
private final Material icon;
|
||||
|
||||
Type(@NonNull Material icon) {
|
||||
this.icon = icon;
|
||||
|
@ -95,8 +95,8 @@ public abstract class FlagListener implements Listener {
|
||||
* @param string - translation reference
|
||||
*/
|
||||
public void noGo(@NonNull Event e, @NonNull Flag flag, boolean silent, String string) {
|
||||
if (e instanceof Cancellable) {
|
||||
((Cancellable)e).setCancelled(true);
|
||||
if (e instanceof Cancellable cancellable) {
|
||||
cancellable.setCancelled(true);
|
||||
}
|
||||
if (user != null && !silent) {
|
||||
user.notify(string, TextVariables.DESCRIPTION, user.getTranslation(flag.getHintReference()));
|
||||
@ -128,7 +128,7 @@ public abstract class FlagListener implements Listener {
|
||||
// Set user
|
||||
user = player == null ? null : User.getInstance(player);
|
||||
if (loc == null) {
|
||||
if (user != null && user.getLocation() != null && user.getLocation().getWorld() != null) {
|
||||
if (user != null && user.getLocation().getWorld() != null) {
|
||||
report(user, e, user.getLocation(), flag, Why.NULL_LOCATION);
|
||||
}
|
||||
return true;
|
||||
@ -144,13 +144,7 @@ public abstract class FlagListener implements Listener {
|
||||
Optional<Island> island = getIslands().getProtectedIslandAt(loc);
|
||||
// Handle Settings Flag
|
||||
if (flag.getType().equals(Flag.Type.SETTING)) {
|
||||
// If the island exists, return the setting, otherwise return the default setting for this flag
|
||||
if (island.isPresent()) {
|
||||
report(user, e, loc, flag, island.map(x -> x.isAllowed(flag)).orElse(false) ? Why.SETTING_ALLOWED_ON_ISLAND : Why.SETTING_NOT_ALLOWED_ON_ISLAND);
|
||||
} else {
|
||||
report(user, e, loc, flag, flag.isSetForWorld(loc.getWorld()) ? Why.SETTING_ALLOWED_IN_WORLD : Why.SETTING_NOT_ALLOWED_IN_WORLD);
|
||||
}
|
||||
return island.map(x -> x.isAllowed(flag)).orElseGet(() -> flag.isSetForWorld(loc.getWorld()));
|
||||
return processSetting(flag, island, e, loc);
|
||||
}
|
||||
|
||||
// Protection flag
|
||||
@ -169,31 +163,14 @@ public abstract class FlagListener implements Listener {
|
||||
|
||||
// Handle World Settings
|
||||
if (flag.getType().equals(Flag.Type.WORLD_SETTING)) {
|
||||
if (flag.isSetForWorld(loc.getWorld())) {
|
||||
report(user, e, loc, flag, Why.ALLOWED_IN_WORLD);
|
||||
return true;
|
||||
}
|
||||
report(user, e, loc, flag, Why.NOT_ALLOWED_IN_WORLD);
|
||||
noGo(e, flag, silent, "protection.world-protected");
|
||||
return false;
|
||||
return processWorldSetting(flag, loc, e, silent);
|
||||
}
|
||||
|
||||
// Check if the plugin is set in User (required for testing)
|
||||
User.setPlugin(plugin);
|
||||
|
||||
if (island.isPresent()) {
|
||||
// If it is not allowed on the island, "bypass island" moderators can do anything
|
||||
if (island.get().isAllowed(user, flag)) {
|
||||
report(user, e, loc, flag, Why.RANK_ALLOWED);
|
||||
return true;
|
||||
} else if (!user.getMetaData(AdminSwitchCommand.META_TAG).map(MetaDataValue::asBoolean).orElse(false)
|
||||
&& (user.hasPermission(getIWM().getPermissionPrefix(loc.getWorld()) + "mod.bypass." + flag.getID() + ".island"))) {
|
||||
report(user, e, loc, flag, Why.BYPASS_ISLAND);
|
||||
return true;
|
||||
}
|
||||
report(user, e, loc, flag, Why.NOT_ALLOWED_ON_ISLAND);
|
||||
noGo(e, flag, silent, island.get().isSpawn() ? "protection.spawn-protected" : "protection.protected");
|
||||
return false;
|
||||
return processBypass(flag, island.get(), e, loc, silent);
|
||||
}
|
||||
// The player is in the world, but not on an island, so general world settings apply
|
||||
if (flag.isSetForWorld(loc.getWorld())) {
|
||||
@ -206,6 +183,41 @@ public abstract class FlagListener implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean processBypass(@NonNull Flag flag, Island island, @NonNull Event e, @NonNull Location loc, boolean silent) {
|
||||
// If it is not allowed on the island, "bypass island" moderators can do anything
|
||||
if (island.isAllowed(user, flag)) {
|
||||
report(user, e, loc, flag, Why.RANK_ALLOWED);
|
||||
return true;
|
||||
} else if (!user.getMetaData(AdminSwitchCommand.META_TAG).map(MetaDataValue::asBoolean).orElse(false)
|
||||
&& (user.hasPermission(getIWM().getPermissionPrefix(loc.getWorld()) + "mod.bypass." + flag.getID() + ".island"))) {
|
||||
report(user, e, loc, flag, Why.BYPASS_ISLAND);
|
||||
return true;
|
||||
}
|
||||
report(user, e, loc, flag, Why.NOT_ALLOWED_ON_ISLAND);
|
||||
noGo(e, flag, silent, island.isSpawn() ? "protection.spawn-protected" : "protection.protected");
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean processWorldSetting(@NonNull Flag flag, @NonNull Location loc, @NonNull Event e, boolean silent) {
|
||||
if (flag.isSetForWorld(loc.getWorld())) {
|
||||
report(user, e, loc, flag, Why.ALLOWED_IN_WORLD);
|
||||
return true;
|
||||
}
|
||||
report(user, e, loc, flag, Why.NOT_ALLOWED_IN_WORLD);
|
||||
noGo(e, flag, silent, "protection.world-protected");
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean processSetting(@NonNull Flag flag, Optional<Island> island, @NonNull Event e, @NonNull Location loc) {
|
||||
// If the island exists, return the setting, otherwise return the default setting for this flag
|
||||
if (island.isPresent()) {
|
||||
report(user, e, loc, flag, island.map(x -> x.isAllowed(flag)).orElse(false) ? Why.SETTING_ALLOWED_ON_ISLAND : Why.SETTING_NOT_ALLOWED_ON_ISLAND);
|
||||
} else {
|
||||
report(user, e, loc, flag, flag.isSetForWorld(loc.getWorld()) ? Why.SETTING_ALLOWED_IN_WORLD : Why.SETTING_NOT_ALLOWED_IN_WORLD);
|
||||
}
|
||||
return island.map(x -> x.isAllowed(flag)).orElseGet(() -> flag.isSetForWorld(loc.getWorld()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Report why something did or did not happen for the admin why command
|
||||
* @param user user involved
|
||||
@ -229,7 +241,7 @@ public abstract class FlagListener implements Listener {
|
||||
.filter(p -> getPlugin().equals(p.getOwningPlugin())).findFirst().map(MetadataValue::asString).orElse("");
|
||||
if (!issuerUUID.isEmpty()) {
|
||||
User issuer = User.getInstance(UUID.fromString(issuerUUID));
|
||||
if (issuer != null && issuer.isPlayer()) {
|
||||
if (issuer.isPlayer()) {
|
||||
user.sendRawMessage(whyEvent);
|
||||
user.sendRawMessage(whyBypass);
|
||||
}
|
||||
|
@ -1,14 +1,15 @@
|
||||
package world.bentobox.bentobox.api.flags.clicklisteners;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.api.addons.GameModeAddon;
|
||||
import world.bentobox.bentobox.api.events.flags.FlagProtectionChangeEvent;
|
||||
import world.bentobox.bentobox.api.flags.Flag;
|
||||
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||
import world.bentobox.bentobox.api.panels.Panel;
|
||||
import world.bentobox.bentobox.api.panels.PanelItem;
|
||||
@ -57,13 +58,13 @@ public class CycleClick implements PanelItem.ClickHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onClick(Panel panel, User user, ClickType click, int slot) {
|
||||
public boolean onClick(Panel panel, User user2, ClickType click, int slot) {
|
||||
// This click listener is used with TabbedPanel and SettingsTabs only
|
||||
TabbedPanel tp = (TabbedPanel)panel;
|
||||
SettingsTab st = (SettingsTab)tp.getActiveTab();
|
||||
// Get the island for this tab
|
||||
island = st.getIsland();
|
||||
this.user = user;
|
||||
this.user = user2;
|
||||
changeOccurred = false;
|
||||
// Permission prefix
|
||||
String prefix = plugin.getIWM().getPermissionPrefix(Util.getWorld(user.getWorld()));
|
||||
@ -85,6 +86,35 @@ public class CycleClick implements PanelItem.ClickHandler {
|
||||
// Rank
|
||||
int currentRank = island.getFlag(flag);
|
||||
if (click.equals(ClickType.LEFT)) {
|
||||
leftClick(flag, rm, currentRank);
|
||||
|
||||
} else if (click.equals(ClickType.RIGHT)) {
|
||||
rightClick(flag, rm, currentRank);
|
||||
|
||||
} else if (click.equals(ClickType.SHIFT_LEFT) && user2.isOp()) {
|
||||
leftShiftClick(flag);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
reportError();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void reportError() {
|
||||
if (island == null) {
|
||||
// Island is not targeted.
|
||||
user.sendMessage("general.errors.not-on-island");
|
||||
} else {
|
||||
// Player is not the allowed to change settings.
|
||||
user.sendMessage("general.errors.insufficient-rank",
|
||||
TextVariables.RANK,
|
||||
user.getTranslation(plugin.getRanksManager().getRank(Objects.requireNonNull(island).getRank(user))));
|
||||
}
|
||||
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_METAL_HIT, 1F, 1F);
|
||||
}
|
||||
|
||||
private void leftClick(Flag flag, RanksManager rm, int currentRank) {
|
||||
if (currentRank >= maxRank) {
|
||||
island.setFlag(flag, minRank);
|
||||
} else {
|
||||
@ -99,7 +129,10 @@ public class CycleClick implements PanelItem.ClickHandler {
|
||||
// Fire events for all subflags as well
|
||||
flag.getSubflags().forEach(subflag -> Bukkit.getPluginManager().callEvent(new FlagProtectionChangeEvent(island, user.getUniqueId(), subflag, island.getFlag(subflag))));
|
||||
}
|
||||
} else if (click.equals(ClickType.RIGHT)) {
|
||||
|
||||
}
|
||||
|
||||
private void rightClick(Flag flag, RanksManager rm, int currentRank) {
|
||||
if (currentRank <= minRank) {
|
||||
island.setFlag(flag, maxRank);
|
||||
} else {
|
||||
@ -114,7 +147,10 @@ public class CycleClick implements PanelItem.ClickHandler {
|
||||
// Fire events for all subflags as well
|
||||
flag.getSubflags().forEach(subflag -> Bukkit.getPluginManager().callEvent(new FlagProtectionChangeEvent(island, user.getUniqueId(), subflag, island.getFlag(subflag))));
|
||||
}
|
||||
} else if (click.equals(ClickType.SHIFT_LEFT) && user.isOp()) {
|
||||
|
||||
}
|
||||
|
||||
private void leftShiftClick(Flag flag) {
|
||||
if (!plugin.getIWM().getHiddenFlags(user.getWorld()).contains(flag.getID())) {
|
||||
plugin.getIWM().getHiddenFlags(user.getWorld()).add(flag.getID());
|
||||
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_GLASS_BREAK, 1F, 1F);
|
||||
@ -124,22 +160,7 @@ public class CycleClick implements PanelItem.ClickHandler {
|
||||
}
|
||||
// Save changes
|
||||
plugin.getIWM().getAddon(user.getWorld()).ifPresent(GameModeAddon::saveWorldSettings);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (island == null) {
|
||||
// Island is not targeted.
|
||||
user.sendMessage("general.errors.not-on-island");
|
||||
} else {
|
||||
// Player is not the allowed to change settings.
|
||||
user.sendMessage("general.errors.insufficient-rank",
|
||||
TextVariables.RANK,
|
||||
user.getTranslation(plugin.getRanksManager().getRank(Objects.requireNonNull(island).getRank(user))));
|
||||
}
|
||||
|
||||
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_METAL_HIT, 1F, 1F);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,14 +1,15 @@
|
||||
package world.bentobox.bentobox.api.flags.clicklisteners;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.api.addons.GameModeAddon;
|
||||
import world.bentobox.bentobox.api.events.flags.FlagSettingChangeEvent;
|
||||
import world.bentobox.bentobox.api.flags.Flag;
|
||||
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||
import world.bentobox.bentobox.api.panels.Panel;
|
||||
import world.bentobox.bentobox.api.panels.PanelItem.ClickHandler;
|
||||
@ -60,18 +61,7 @@ public class IslandToggleClick implements ClickHandler {
|
||||
{
|
||||
if (click.equals(ClickType.SHIFT_LEFT) && user.isOp())
|
||||
{
|
||||
if (!plugin.getIWM().getHiddenFlags(user.getWorld()).contains(flag.getID()))
|
||||
{
|
||||
plugin.getIWM().getHiddenFlags(user.getWorld()).add(flag.getID());
|
||||
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_GLASS_BREAK, 1F, 1F);
|
||||
}
|
||||
else
|
||||
{
|
||||
plugin.getIWM().getHiddenFlags(user.getWorld()).remove(flag.getID());
|
||||
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_NOTE_BLOCK_CHIME, 1F, 1F);
|
||||
}
|
||||
// Save changes
|
||||
plugin.getIWM().getAddon(user.getWorld()).ifPresent(GameModeAddon::saveWorldSettings);
|
||||
shiftLeftClick(user, flag);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -82,6 +72,16 @@ public class IslandToggleClick implements ClickHandler {
|
||||
user.notify("protection.panel.flag-item.setting-cooldown");
|
||||
return;
|
||||
}
|
||||
toggleFlag(user, flag, island);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
reportError(user, island);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void toggleFlag(User user, Flag flag, Island island) {
|
||||
// Toggle flag
|
||||
island.toggleFlag(flag);
|
||||
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1F, 1F);
|
||||
@ -102,9 +102,10 @@ public class IslandToggleClick implements ClickHandler {
|
||||
subflag,
|
||||
island.isAllowed(subflag))));
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
||||
private void reportError(User user, Island island) {
|
||||
if (island == null) {
|
||||
user.sendMessage("general.errors.not-on-island");
|
||||
} else {
|
||||
@ -115,7 +116,22 @@ public class IslandToggleClick implements ClickHandler {
|
||||
}
|
||||
|
||||
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_METAL_HIT, 1F, 1F);
|
||||
|
||||
}
|
||||
return true;
|
||||
|
||||
private void shiftLeftClick(User user, Flag flag) {
|
||||
if (!plugin.getIWM().getHiddenFlags(user.getWorld()).contains(flag.getID()))
|
||||
{
|
||||
plugin.getIWM().getHiddenFlags(user.getWorld()).add(flag.getID());
|
||||
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_GLASS_BREAK, 1F, 1F);
|
||||
}
|
||||
else
|
||||
{
|
||||
plugin.getIWM().getHiddenFlags(user.getWorld()).remove(flag.getID());
|
||||
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_NOTE_BLOCK_CHIME, 1F, 1F);
|
||||
}
|
||||
// Save changes
|
||||
plugin.getIWM().getAddon(user.getWorld()).ifPresent(GameModeAddon::saveWorldSettings);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -36,22 +36,22 @@ public class MetaDataValue {
|
||||
* @param value the value assigned to this metadata value
|
||||
*/
|
||||
public MetaDataValue(@NonNull Object value) {
|
||||
if (value instanceof Integer) {
|
||||
intValue = (int)value;
|
||||
} else if (value instanceof Float) {
|
||||
floatValue = (float)value;
|
||||
} else if (value instanceof Double) {
|
||||
doubleValue = (double)value;
|
||||
} else if (value instanceof Long) {
|
||||
longValue = (long)value;
|
||||
} else if (value instanceof Short) {
|
||||
shortValue = (short)value;
|
||||
} else if (value instanceof Byte) {
|
||||
byteValue = (byte)value;
|
||||
} else if (value instanceof Boolean) {
|
||||
booleanValue = (boolean)value;
|
||||
} else if (value instanceof String) {
|
||||
stringValue = (String)value;
|
||||
if (value instanceof Integer i) {
|
||||
intValue = i;
|
||||
} else if (value instanceof Float f) {
|
||||
floatValue = f;
|
||||
} else if (value instanceof Double d) {
|
||||
doubleValue = d;
|
||||
} else if (value instanceof Long l) {
|
||||
longValue = l;
|
||||
} else if (value instanceof Short s) {
|
||||
shortValue = s;
|
||||
} else if (value instanceof Byte b) {
|
||||
byteValue = b;
|
||||
} else if (value instanceof Boolean bo) {
|
||||
booleanValue = bo;
|
||||
} else if (value instanceof String st) {
|
||||
stringValue = st;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ public interface Tab {
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Return the panel items for this tab
|
||||
* Return an immutable list of the panel items for this tab
|
||||
* @return a list of items in slot order
|
||||
*/
|
||||
List<@Nullable PanelItem> getPanelItems();
|
||||
|
@ -31,8 +31,8 @@ public class TabbedPanel extends Panel implements PanelListener {
|
||||
private static final String PROTECTION_PANEL = "protection.panel.";
|
||||
private static final long ITEMS_PER_PAGE = 36;
|
||||
private final TabbedPanelBuilder tpb;
|
||||
private @NonNull
|
||||
final BentoBox plugin = BentoBox.getInstance();
|
||||
@NonNull
|
||||
private final BentoBox plugin = BentoBox.getInstance();
|
||||
private int activeTab;
|
||||
private int activePage;
|
||||
private boolean closed;
|
||||
|
@ -96,11 +96,11 @@ public class TemplatedPanel extends Panel
|
||||
{
|
||||
for (int k = 0; k < this.panelTemplate.content()[i].length; k++)
|
||||
{
|
||||
ItemTemplateRecord record = this.panelTemplate.content()[i][k];
|
||||
ItemTemplateRecord rec = this.panelTemplate.content()[i][k];
|
||||
|
||||
if (record != null && record.dataMap().containsKey("type"))
|
||||
if (rec != null && rec.dataMap().containsKey("type"))
|
||||
{
|
||||
String type = String.valueOf(record.dataMap().get("type"));
|
||||
String type = String.valueOf(rec.dataMap().get("type"));
|
||||
|
||||
int counter = this.typeSlotMap.computeIfAbsent(type, key -> 0);
|
||||
this.typeSlotMap.put(type, counter + 1);
|
||||
@ -226,11 +226,11 @@ public class TemplatedPanel extends Panel
|
||||
// Analyze the template
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
ItemTemplateRecord record = this.panelTemplate.content()[0][i];
|
||||
ItemTemplateRecord rec = this.panelTemplate.content()[0][i];
|
||||
|
||||
if (record != null && record.dataMap().containsKey("type"))
|
||||
if (rec != null && rec.dataMap().containsKey("type"))
|
||||
{
|
||||
String type = String.valueOf(record.dataMap().get("type"));
|
||||
String type = String.valueOf(rec.dataMap().get("type"));
|
||||
|
||||
int counter = this.typeSlotMap.computeIfAbsent(type, key -> 0);
|
||||
this.typeSlotMap.put(type, counter + 1);
|
||||
@ -289,11 +289,11 @@ public class TemplatedPanel extends Panel
|
||||
{
|
||||
for (int k = 0; k < 3; k++)
|
||||
{
|
||||
ItemTemplateRecord record = this.panelTemplate.content()[i][k];
|
||||
ItemTemplateRecord rec = this.panelTemplate.content()[i][k];
|
||||
|
||||
if (record != null && record.dataMap().containsKey("type"))
|
||||
if (rec != null && rec.dataMap().containsKey("type"))
|
||||
{
|
||||
String type = String.valueOf(record.dataMap().get("type"));
|
||||
String type = String.valueOf(rec.dataMap().get("type"));
|
||||
|
||||
int counter = this.typeSlotMap.computeIfAbsent(type, key -> 0);
|
||||
this.typeSlotMap.put(type, counter + 1);
|
||||
@ -354,41 +354,41 @@ public class TemplatedPanel extends Panel
|
||||
|
||||
/**
|
||||
* This method passes button creation from given record template.
|
||||
* @param record Template of the button that must be created.
|
||||
* @param rec Template of the button that must be created.
|
||||
* @return PanelItem of the template, otherwise null.
|
||||
*/
|
||||
@Nullable
|
||||
private PanelItem makeButton(@Nullable ItemTemplateRecord record)
|
||||
private PanelItem makeButton(@Nullable ItemTemplateRecord rec)
|
||||
{
|
||||
if (record == null)
|
||||
if (rec == null)
|
||||
{
|
||||
// Immediate exit if record is null.
|
||||
return null;
|
||||
}
|
||||
|
||||
if (record.dataMap().containsKey("type"))
|
||||
if (rec.dataMap().containsKey("type"))
|
||||
{
|
||||
// If dataMap is not null, and it is not empty, then pass button to the object creator function.
|
||||
|
||||
return this.makeAddonButton(record);
|
||||
return this.makeAddonButton(rec);
|
||||
}
|
||||
else
|
||||
{
|
||||
PanelItemBuilder itemBuilder = new PanelItemBuilder();
|
||||
|
||||
if (record.icon() != null)
|
||||
if (rec.icon() != null)
|
||||
{
|
||||
itemBuilder.icon(record.icon().clone());
|
||||
itemBuilder.icon(rec.icon().clone());
|
||||
}
|
||||
|
||||
if (record.title() != null)
|
||||
if (rec.title() != null)
|
||||
{
|
||||
itemBuilder.name(this.user.getTranslation(record.title()));
|
||||
itemBuilder.name(this.user.getTranslation(rec.title()));
|
||||
}
|
||||
|
||||
if (record.description() != null)
|
||||
if (rec.description() != null)
|
||||
{
|
||||
itemBuilder.description(this.user.getTranslation(record.description()));
|
||||
itemBuilder.description(this.user.getTranslation(rec.description()));
|
||||
}
|
||||
|
||||
// If there are generic click handlers that could be added, then this is a place
|
||||
@ -402,19 +402,19 @@ public class TemplatedPanel extends Panel
|
||||
|
||||
/**
|
||||
* This method passes button to the type creator, if that exists.
|
||||
* @param record Template of the button that must be created.
|
||||
* @param rec Template of the button that must be created.
|
||||
* @return PanelItem of the button created by typeCreator, otherwise null.
|
||||
*/
|
||||
@Nullable
|
||||
private PanelItem makeAddonButton(@NonNull ItemTemplateRecord record)
|
||||
private PanelItem makeAddonButton(@NonNull ItemTemplateRecord rec)
|
||||
{
|
||||
// Get object type.
|
||||
String type = String.valueOf(record.dataMap().getOrDefault("type", ""));
|
||||
String type = String.valueOf(rec.dataMap().getOrDefault("type", ""));
|
||||
|
||||
if (!this.typeCreators.containsKey(type))
|
||||
{
|
||||
// There are no object with a given type.
|
||||
return this.makeFallBack(record.fallback());
|
||||
return this.makeFallBack(rec.fallback());
|
||||
}
|
||||
|
||||
BiFunction<ItemTemplateRecord, ItemSlot, PanelItem> buttonBuilder = this.typeCreators.get(type);
|
||||
@ -426,48 +426,48 @@ public class TemplatedPanel extends Panel
|
||||
this.typeIndex.put(type, itemSlot.nextItemSlot());
|
||||
|
||||
// Try to get next object.
|
||||
PanelItem item = buttonBuilder.apply(record, itemSlot);
|
||||
return item == null ? this.makeFallBack(record.fallback()) : item;
|
||||
PanelItem item = buttonBuilder.apply(rec, itemSlot);
|
||||
return item == null ? this.makeFallBack(rec.fallback()) : item;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method creates a fall back button for given record.
|
||||
* @param record Record which fallback must be created.
|
||||
* @param rec Record which fallback must be created.
|
||||
* @return PanelItem if fallback was creates successfully, otherwise null.
|
||||
*/
|
||||
@Nullable
|
||||
private PanelItem makeFallBack(@Nullable ItemTemplateRecord record)
|
||||
private PanelItem makeFallBack(@Nullable ItemTemplateRecord rec)
|
||||
{
|
||||
return record == null ? null : this.makeButton(record.fallback());
|
||||
return rec == null ? null : this.makeButton(rec.fallback());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method translates template record into a panel item.
|
||||
* @param record Record that must be translated.
|
||||
* @param rec Record that must be translated.
|
||||
* @return PanelItem that contains all information from the record.
|
||||
*/
|
||||
private PanelItem makeTemplate(PanelTemplateRecord.TemplateItem record)
|
||||
private PanelItem makeTemplate(PanelTemplateRecord.TemplateItem rec)
|
||||
{
|
||||
PanelItemBuilder itemBuilder = new PanelItemBuilder();
|
||||
|
||||
// Read icon only if it is not null.
|
||||
if (record.icon() != null)
|
||||
if (rec.icon() != null)
|
||||
{
|
||||
itemBuilder.icon(record.icon().clone());
|
||||
itemBuilder.icon(rec.icon().clone());
|
||||
}
|
||||
|
||||
// Read title only if it is not null.
|
||||
if (record.title() != null)
|
||||
if (rec.title() != null)
|
||||
{
|
||||
itemBuilder.name(this.user.getTranslation(record.title()));
|
||||
itemBuilder.name(this.user.getTranslation(rec.title()));
|
||||
}
|
||||
|
||||
// Read description only if it is not null.
|
||||
if (record.description() != null)
|
||||
if (rec.description() != null)
|
||||
{
|
||||
itemBuilder.description(this.user.getTranslation(record.description()));
|
||||
itemBuilder.description(this.user.getTranslation(rec.description()));
|
||||
}
|
||||
|
||||
// Click Handlers are managed by custom addon buttons.
|
||||
|
@ -8,7 +8,11 @@ package world.bentobox.bentobox.api.panels.builders;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import org.bukkit.World;
|
||||
|
@ -19,7 +19,6 @@ import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import world.bentobox.bentobox.api.panels.reader.ItemTemplateRecord.ActionRecords;
|
||||
|
||||
|
||||
/**
|
||||
* This Record contains all necessary information about Item Template that can be used to craft panel item.
|
||||
*
|
||||
|
@ -17,7 +17,6 @@ import org.eclipse.jdt.annotation.Nullable;
|
||||
import world.bentobox.bentobox.api.panels.Panel;
|
||||
import world.bentobox.bentobox.api.panels.reader.PanelTemplateRecord.TemplateItem;
|
||||
|
||||
|
||||
/**
|
||||
* This is template object for the panel reader. It contains data that can exist in the panel.
|
||||
* PanelBuilder will use this to build panel.
|
||||
@ -98,10 +97,9 @@ public record PanelTemplateRecord(Panel.Type type,
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof PanelTemplateRecord)) {
|
||||
if (!(obj instanceof PanelTemplateRecord other)) {
|
||||
return false;
|
||||
}
|
||||
PanelTemplateRecord other = (PanelTemplateRecord) obj;
|
||||
return Objects.equals(background, other.background) && Objects.equals(border, other.border)
|
||||
&& Arrays.deepEquals(content, other.content) && Arrays.equals(forcedRows, other.forcedRows)
|
||||
&& Objects.equals(title, other.title) && type == other.type;
|
||||
|
@ -6,6 +6,12 @@
|
||||
|
||||
package world.bentobox.bentobox.api.panels.reader;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.InvalidConfigurationException;
|
||||
@ -14,12 +20,6 @@ import org.bukkit.event.inventory.ClickType;
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.base.Enums;
|
||||
|
||||
import world.bentobox.bentobox.api.panels.Panel;
|
||||
@ -49,6 +49,14 @@ public class TemplateReader
|
||||
private static final String TYPE = "type";
|
||||
|
||||
|
||||
/**
|
||||
* Utility classes, which are collections of static members, are not meant to be instantiated.
|
||||
* Even abstract utility classes, which can be extended, should not have public constructors.
|
||||
* Java adds an implicit public constructor to every class which does not define at least one explicitly.
|
||||
* Hence, at least one non-public constructor should be defined.
|
||||
*/
|
||||
private TemplateReader() {}
|
||||
|
||||
/**
|
||||
* Read template panel panel template record.
|
||||
*
|
||||
@ -95,7 +103,7 @@ public class TemplateReader
|
||||
return TemplateReader.loadedPanels.get(panelKey);
|
||||
}
|
||||
|
||||
PanelTemplateRecord record;
|
||||
PanelTemplateRecord rec;
|
||||
|
||||
try
|
||||
{
|
||||
@ -103,16 +111,16 @@ public class TemplateReader
|
||||
YamlConfiguration config = new YamlConfiguration();
|
||||
config.load(file);
|
||||
// Read panel
|
||||
record = readPanelTemplate(config.getConfigurationSection(panelName));
|
||||
rec = readPanelTemplate(config.getConfigurationSection(panelName));
|
||||
// Put panel into memory
|
||||
TemplateReader.loadedPanels.put(panelKey, record);
|
||||
TemplateReader.loadedPanels.put(panelKey, rec);
|
||||
}
|
||||
catch (IOException | InvalidConfigurationException e)
|
||||
{
|
||||
record = null;
|
||||
rec = null;
|
||||
}
|
||||
|
||||
return record;
|
||||
return rec;
|
||||
}
|
||||
|
||||
|
||||
|
@ -6,6 +6,7 @@ import java.util.Map;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||
import world.bentobox.bentobox.api.placeholders.PlaceholderReplacer;
|
||||
@ -42,9 +43,9 @@ abstract class BasicPlaceholderExpansion extends PlaceholderExpansion {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onPlaceholderRequest(Player p, @NonNull String placeholder) {
|
||||
if (placeholders.containsKey(placeholder) && p != null) {
|
||||
return placeholders.get(placeholder).onReplace(User.getInstance(p));
|
||||
public String onPlaceholderRequest(@Nullable Player p, @NonNull String placeholder) {
|
||||
if (placeholders.containsKey(placeholder)) {
|
||||
return placeholders.get(placeholder).onReplace(p != null ? User.getInstance(p) : null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package world.bentobox.bentobox.api.user;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@ -8,7 +10,6 @@ import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.lang.math.NumberUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
@ -17,6 +18,7 @@ import org.bukkit.GameMode;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.Particle;
|
||||
import org.bukkit.Particle.DustTransition;
|
||||
import org.bukkit.Vibration;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
@ -54,6 +56,26 @@ public class User implements MetaDataAble {
|
||||
|
||||
private static final Map<UUID, User> users = new HashMap<>();
|
||||
|
||||
// Used for particle validation
|
||||
private static final Map<Particle, Class<?>> VALIDATION_CHECK;
|
||||
static {
|
||||
Map<Particle, Class<?>> v = new EnumMap<>(Particle.class);
|
||||
v.put(Particle.REDSTONE, Particle.DustOptions.class);
|
||||
v.put(Particle.ITEM_CRACK, ItemStack.class);
|
||||
v.put(Particle.BLOCK_CRACK, BlockData.class);
|
||||
v.put(Particle.BLOCK_DUST, BlockData.class);
|
||||
v.put(Particle.FALLING_DUST, BlockData.class);
|
||||
v.put(Particle.BLOCK_MARKER, BlockData.class);
|
||||
v.put(Particle.DUST_COLOR_TRANSITION, DustTransition.class);
|
||||
v.put(Particle.VIBRATION, Vibration.class);
|
||||
v.put(Particle.SCULK_CHARGE, Float.class);
|
||||
v.put(Particle.SHRIEK, Integer.class);
|
||||
v.put(Particle.LEGACY_BLOCK_CRACK, BlockData.class);
|
||||
v.put(Particle.LEGACY_BLOCK_DUST, BlockData.class);
|
||||
v.put(Particle.LEGACY_FALLING_DUST, BlockData.class);
|
||||
VALIDATION_CHECK = Collections.unmodifiableMap(v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all users from the user list
|
||||
*/
|
||||
@ -89,7 +111,8 @@ public class User implements MetaDataAble {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instance of User from a UUID.
|
||||
* Gets an instance of User from a UUID. This will always return a user object.
|
||||
* If the player is offline then the getPlayer value will be null.
|
||||
* @param uuid - UUID
|
||||
* @return user - user
|
||||
*/
|
||||
@ -98,7 +121,7 @@ public class User implements MetaDataAble {
|
||||
if (users.containsKey(uuid)) {
|
||||
return users.get(uuid);
|
||||
}
|
||||
// Return player, or null if they are not online
|
||||
// Return a user instance
|
||||
return new User(uuid);
|
||||
}
|
||||
|
||||
@ -317,8 +340,6 @@ public class User implements MetaDataAble {
|
||||
// If requester is console, then return the default value
|
||||
if (!isPlayer()) return defaultValue;
|
||||
|
||||
int value = 0;
|
||||
|
||||
// If there is a dot at the end of the permissionPrefix, remove it
|
||||
if (permissionPrefix.endsWith(".")) {
|
||||
permissionPrefix = permissionPrefix.substring(0, permissionPrefix.length()-1);
|
||||
@ -330,10 +351,16 @@ public class User implements MetaDataAble {
|
||||
.filter(PermissionAttachmentInfo::getValue) // Must be a positive permission, not a negative one
|
||||
.map(PermissionAttachmentInfo::getPermission)
|
||||
.filter(permission -> permission.startsWith(permPrefix))
|
||||
.collect(Collectors.toList());
|
||||
.toList();
|
||||
|
||||
if (permissions.isEmpty()) return defaultValue;
|
||||
|
||||
return iteratePerms(permissions, permPrefix, defaultValue);
|
||||
|
||||
}
|
||||
|
||||
private int iteratePerms(List<String> permissions, String permPrefix, int defaultValue) {
|
||||
int value = 0;
|
||||
for (String permission : permissions) {
|
||||
if (permission.contains(permPrefix + "*")) {
|
||||
// 'Star' permission
|
||||
@ -403,21 +430,27 @@ public class User implements MetaDataAble {
|
||||
}
|
||||
|
||||
private String translate(String addonPrefix, String reference, String[] variables) {
|
||||
// Try to get the translation for this specific addon
|
||||
String translation = plugin.getLocalesManager().get(this, addonPrefix + reference);
|
||||
|
||||
if (translation == null) {
|
||||
// No luck, try to get the generic translation
|
||||
translation = plugin.getLocalesManager().get(this, reference);
|
||||
if (translation == null) {
|
||||
// If no translation has been found, return the reference for debug purposes.
|
||||
return reference;
|
||||
// Nothing found. Replace vars (probably will do nothing) and return
|
||||
return replaceVars(reference, variables);
|
||||
}
|
||||
}
|
||||
|
||||
// If this is a prefix, just gather and return the translation
|
||||
if (reference.startsWith("prefixes.")) {
|
||||
return translation;
|
||||
} else {
|
||||
if (!reference.startsWith("prefixes.")) {
|
||||
// Replace the prefixes
|
||||
return replacePrefixes(translation, variables);
|
||||
}
|
||||
return translation;
|
||||
}
|
||||
|
||||
private String replacePrefixes(String translation, String[] variables) {
|
||||
for (String prefix : plugin.getLocalesManager().getAvailablePrefixes(this)) {
|
||||
String prefixTranslation = getTranslation("prefixes." + prefix);
|
||||
// Replace the [gamemode] text variable
|
||||
@ -440,9 +473,25 @@ public class User implements MetaDataAble {
|
||||
if (player != null) {
|
||||
translation = plugin.getPlaceholdersManager().replacePlaceholders(player, translation);
|
||||
}
|
||||
|
||||
return translation;
|
||||
}
|
||||
|
||||
private String replaceVars(String reference, String[] variables) {
|
||||
|
||||
// Then replace variables
|
||||
if (variables.length > 1) {
|
||||
for (int i = 0; i < variables.length; i += 2) {
|
||||
reference = reference.replace(variables[i], variables[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
// Then replace Placeholders, this will only work if this is a player
|
||||
if (player != null) {
|
||||
reference = plugin.getPlaceholdersManager().replacePlaceholders(player, reference);
|
||||
}
|
||||
|
||||
// If no translation has been found, return the reference for debug purposes.
|
||||
return reference;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -600,72 +649,18 @@ public class User implements MetaDataAble {
|
||||
* @param y Y coordinate of the particle to display.
|
||||
* @param z Z coordinate of the particle to display.
|
||||
*/
|
||||
public void spawnParticle(Particle particle, Object dustOptions, double x, double y, double z)
|
||||
public void spawnParticle(Particle particle, @Nullable Object dustOptions, double x, double y, double z)
|
||||
{
|
||||
// Improve particle validation.
|
||||
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.");
|
||||
}
|
||||
}
|
||||
Class<?> expectedClass = VALIDATION_CHECK.get(particle);
|
||||
if (expectedClass == null) throw new IllegalArgumentException("Unexpected value: " + particle);
|
||||
|
||||
if (!(expectedClass.isInstance(dustOptions))) {
|
||||
throw new IllegalArgumentException("A non-null " + expectedClass.getSimpleName() + " must be provided when using Particle." + particle + " as particle.");
|
||||
}
|
||||
|
||||
// Check if this particle is beyond the viewing distance of the server
|
||||
if (this.player != null &&
|
||||
this.player.getLocation().toVector().distanceSquared(new Vector(x, y, z)) <
|
||||
if (this.player != null
|
||||
&& this.player.getLocation().toVector().distanceSquared(new Vector(x, y, z)) <
|
||||
(Bukkit.getServer().getViewDistance() * 256 * Bukkit.getServer().getViewDistance()))
|
||||
{
|
||||
if (particle.equals(Particle.REDSTONE))
|
||||
@ -678,6 +673,7 @@ public class User implements MetaDataAble {
|
||||
}
|
||||
else
|
||||
{
|
||||
// This will never be called unless the value in VALIDATION_CHECK is null in the future
|
||||
player.spawnParticle(particle, x, y, z, 1);
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package world.bentobox.bentobox.blueprints;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import org.bukkit.Material;
|
||||
|
@ -8,7 +8,6 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
@ -138,7 +137,7 @@ public class BlueprintClipboard {
|
||||
.filter(e -> new Vector(Math.rint(e.getLocation().getX()),
|
||||
Math.rint(e.getLocation().getY()),
|
||||
Math.rint(e.getLocation().getZ())).equals(v))
|
||||
.collect(Collectors.toList());
|
||||
.toList();
|
||||
if (copyBlock(v.toLocation(world), copyAir, copyBiome, ents)) {
|
||||
count++;
|
||||
}
|
||||
@ -288,19 +287,17 @@ public class BlueprintClipboard {
|
||||
if (entity instanceof Villager villager) {
|
||||
setVillager(villager, bpe);
|
||||
}
|
||||
if (entity instanceof Colorable c) {
|
||||
if (c.getColor() != null) {
|
||||
if (entity instanceof Colorable c && c.getColor() != null) {
|
||||
bpe.setColor(c.getColor());
|
||||
}
|
||||
if (entity instanceof Tameable tameable) {
|
||||
bpe.setTamed(tameable.isTamed());
|
||||
}
|
||||
if (entity instanceof Tameable) {
|
||||
bpe.setTamed(((Tameable)entity).isTamed());
|
||||
}
|
||||
if (entity instanceof ChestedHorse) {
|
||||
bpe.setChest(((ChestedHorse)entity).isCarryingChest());
|
||||
if (entity instanceof ChestedHorse chestedHorse) {
|
||||
bpe.setChest(chestedHorse.isCarryingChest());
|
||||
}
|
||||
// Only set if child. Most animals are adults
|
||||
if (entity instanceof Ageable && !((Ageable)entity).isAdult()) {
|
||||
if (entity instanceof Ageable ageable && !ageable.isAdult()) {
|
||||
bpe.setAdult(false);
|
||||
}
|
||||
if (entity instanceof AbstractHorse horse) {
|
||||
|
@ -1,5 +1,17 @@
|
||||
package world.bentobox.bentobox.blueprints;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
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.Location;
|
||||
import org.bukkit.World;
|
||||
@ -7,6 +19,7 @@ import org.bukkit.scheduler.BukkitTask;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
@ -16,12 +29,6 @@ import world.bentobox.bentobox.database.objects.Island;
|
||||
import world.bentobox.bentobox.nms.PasteHandler;
|
||||
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
|
||||
* @author tastybento
|
||||
@ -77,10 +84,10 @@ public class BlueprintPaster {
|
||||
private final Island island;
|
||||
|
||||
/**
|
||||
* Paste a clipboard to a location and run task
|
||||
* Paste a clipboard to a location. Run {@link #paste()} to paste
|
||||
* @param plugin - BentoBox
|
||||
* @param clipboard - clipboard to paste
|
||||
* @param location - location to paste to
|
||||
* @param location - location to which to paste
|
||||
*/
|
||||
public BlueprintPaster(@NonNull BentoBox plugin, @NonNull BlueprintClipboard clipboard, @NonNull Location location) {
|
||||
this.plugin = plugin;
|
||||
@ -90,9 +97,6 @@ public class BlueprintPaster {
|
||||
this.location = location;
|
||||
this.world = location.getWorld();
|
||||
this.island = null;
|
||||
|
||||
// Paste
|
||||
paste();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -151,20 +155,71 @@ public class BlueprintPaster {
|
||||
|
||||
final int pasteSpeed = plugin.getSettings().getPasteSpeed();
|
||||
|
||||
long timer = System.currentTimeMillis();
|
||||
int count = 0;
|
||||
if (pasteState.equals(PasteState.CHUNK_LOAD)) {
|
||||
pasteState = PasteState.CHUNK_LOADING;
|
||||
// Load chunk
|
||||
currentTask = Util.getChunkAtAsync(location).thenRun(() -> {
|
||||
pasteState = PasteState.BLOCKS;
|
||||
long duration = System.currentTimeMillis() - timer;
|
||||
if (duration > chunkLoadTime) {
|
||||
chunkLoadTime = duration;
|
||||
}
|
||||
});
|
||||
loadChunk();
|
||||
}
|
||||
else if (pasteState.equals(PasteState.BLOCKS) || pasteState.equals(PasteState.ATTACHMENTS)) {
|
||||
pasteBlocks(bits, count, owner, pasteSpeed);
|
||||
}
|
||||
else if (pasteState.equals(PasteState.ENTITIES)) {
|
||||
pasteEntities(bits, count, owner, pasteSpeed);
|
||||
}
|
||||
else if (pasteState.equals(PasteState.DONE)) {
|
||||
// All done. Cancel task
|
||||
cancelTask(result);
|
||||
} else if (pasteState.equals(PasteState.CANCEL)) {
|
||||
// This state makes sure the follow-on task only ever runs once
|
||||
pastingTask.cancel();
|
||||
result.complete(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void cancelTask(CompletableFuture<Boolean> result) {
|
||||
// Set pos1 and 2 if this was a clipboard paste
|
||||
if (island == null && clipboard != null) {
|
||||
clipboard.setPos1(pos1);
|
||||
clipboard.setPos2(pos2);
|
||||
}
|
||||
pasteState = PasteState.CANCEL;
|
||||
result.complete(true);
|
||||
}
|
||||
|
||||
private void pasteEntities(Bits bits, int count, Optional<User> owner, int pasteSpeed) {
|
||||
if (bits.it3().hasNext()) {
|
||||
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 dimensionType = 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]", dimensionType));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void pasteBlocks(Bits bits, int count, Optional<User> owner, int pasteSpeed) {
|
||||
Iterator<Entry<Vector, BlueprintBlock>> it = pasteState.equals(PasteState.BLOCKS) ? bits.it : bits.it2;
|
||||
if (it.hasNext()) {
|
||||
Map<Location, BlueprintBlock> blockMap = new HashMap<>();
|
||||
@ -198,53 +253,21 @@ public class BlueprintPaster {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (pasteState.equals(PasteState.ENTITIES)) {
|
||||
if (bits.it3().hasNext()) {
|
||||
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)) {
|
||||
// All done. Cancel task
|
||||
// Set pos1 and 2 if this was a clipboard paste
|
||||
if (island == null && clipboard != null) {
|
||||
clipboard.setPos1(pos1);
|
||||
clipboard.setPos2(pos2);
|
||||
}
|
||||
pasteState = PasteState.CANCEL;
|
||||
result.complete(true);
|
||||
} else if (pasteState.equals(PasteState.CANCEL)) {
|
||||
// This state makes sure the follow-on task only ever runs once
|
||||
pastingTask.cancel();
|
||||
result.complete(true);
|
||||
private void loadChunk() {
|
||||
long timer = System.currentTimeMillis();
|
||||
pasteState = PasteState.CHUNK_LOADING;
|
||||
// Load chunk
|
||||
currentTask = Util.getChunkAtAsync(location).thenRun(() -> {
|
||||
pasteState = PasteState.BLOCKS;
|
||||
long duration = System.currentTimeMillis() - timer;
|
||||
if (duration > chunkLoadTime) {
|
||||
chunkLoadTime = duration;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void tellOwner(User user, int blocksSize, int attachedSize, int entitiesSize, int pasteSpeed) {
|
||||
|
@ -8,7 +8,6 @@ import org.bukkit.entity.Player;
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.api.addons.GameModeAddon;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.bentobox.blueprints.Blueprint;
|
||||
|
@ -3,7 +3,6 @@ package world.bentobox.bentobox.blueprints.dataobjects;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import org.bukkit.Material;
|
||||
|
@ -99,12 +99,11 @@ public class Database<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Save object. Saving may be done async or sync, depending on the underlying database.
|
||||
* Save object. Saving is done async. Same as {@link #saveObjectAsync(Object)}, which is recommended.
|
||||
* @param instance to save
|
||||
* @return true - always.
|
||||
* @deprecated As of 1.13.0. Use {@link #saveObjectAsync(Object)}.
|
||||
* @since 1.13.0
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean saveObject(T instance) {
|
||||
saveObjectAsync(instance).thenAccept(r -> {
|
||||
if (Boolean.FALSE.equals(r)) logger.severe(() -> "Could not save object to database!");
|
||||
|
@ -1,5 +1,13 @@
|
||||
package world.bentobox.bentobox.database;
|
||||
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* The type Database connection settings.
|
||||
*/
|
||||
public class DatabaseConnectionSettingsImpl {
|
||||
private String host;
|
||||
private int port;
|
||||
@ -14,6 +22,18 @@ public class DatabaseConnectionSettingsImpl {
|
||||
*/
|
||||
private boolean useSSL;
|
||||
|
||||
/**
|
||||
* Number of max connections in pool.
|
||||
* @since 1.21.0
|
||||
*/
|
||||
private int maxConnections;
|
||||
|
||||
/**
|
||||
* Map of extra properties.
|
||||
* @since 1.21.0
|
||||
*/
|
||||
private Map<String, String> extraProperties;
|
||||
|
||||
/**
|
||||
* Hosts database settings
|
||||
* @param host - database host
|
||||
@ -21,16 +41,78 @@ public class DatabaseConnectionSettingsImpl {
|
||||
* @param databaseName - database name
|
||||
* @param username - username
|
||||
* @param password - password
|
||||
* @param useSSL - whether to use SSL or not
|
||||
* @param maxConnections - max number of connections
|
||||
* @param extraProperties Map with extra properties.
|
||||
*/
|
||||
public DatabaseConnectionSettingsImpl(String host, int port, String databaseName, String username, String password, boolean useSSL) {
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.databaseName = databaseName;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.useSSL = useSSL;
|
||||
public record DatabaseSettings(String host,
|
||||
int port,
|
||||
String databaseName,
|
||||
String username,
|
||||
String password,
|
||||
boolean useSSL,
|
||||
int maxConnections,
|
||||
Map<String, String> extraProperties) {}
|
||||
|
||||
/**
|
||||
* Hosts database settings
|
||||
* @param settings - database settings see {@link DatabaseSettings}
|
||||
*/
|
||||
public DatabaseConnectionSettingsImpl(DatabaseSettings settings)
|
||||
{
|
||||
this.host = settings.host;
|
||||
this.port = settings.port;
|
||||
this.databaseName = settings.databaseName;
|
||||
this.username = settings.username;
|
||||
this.password = settings.password;
|
||||
this.useSSL = settings.useSSL;
|
||||
this.maxConnections = settings.maxConnections;
|
||||
this.extraProperties = settings.extraProperties;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Hosts database settings
|
||||
* @param host - database host
|
||||
* @param port - port
|
||||
* @param databaseName - database name
|
||||
* @param username - username
|
||||
* @param password - password
|
||||
* @param useSSL - ssl usage.
|
||||
* @param maxConnections - number of maximal connections in pool.
|
||||
*/
|
||||
public DatabaseConnectionSettingsImpl(String host,
|
||||
int port,
|
||||
String databaseName,
|
||||
String username,
|
||||
String password,
|
||||
boolean useSSL,
|
||||
int maxConnections)
|
||||
{
|
||||
this(new DatabaseSettings(host, port, databaseName, username, password, useSSL, maxConnections, Collections.emptyMap()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Hosts database settings
|
||||
* @param host - database host
|
||||
* @param port - port
|
||||
* @param databaseName - database name
|
||||
* @param username - username
|
||||
* @param password - password
|
||||
* @param useSSL - ssl usage.
|
||||
*/
|
||||
public DatabaseConnectionSettingsImpl(String host,
|
||||
int port,
|
||||
String databaseName,
|
||||
String username,
|
||||
String password,
|
||||
boolean useSSL)
|
||||
{
|
||||
this(new DatabaseSettings(host, port, databaseName, username, password, useSSL, 0, Collections.emptyMap()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the host
|
||||
*/
|
||||
@ -117,4 +199,48 @@ public class DatabaseConnectionSettingsImpl {
|
||||
public void setUseSSL(boolean useSSL) {
|
||||
this.useSSL = useSSL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets max connections.
|
||||
*
|
||||
* @return the max connections
|
||||
*/
|
||||
public int getMaxConnections()
|
||||
{
|
||||
return this.maxConnections;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets max connections.
|
||||
*
|
||||
* @param maxConnections the max connections
|
||||
*/
|
||||
public void setMaxConnections(int maxConnections)
|
||||
{
|
||||
this.maxConnections = maxConnections;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets extra properties.
|
||||
*
|
||||
* @return the extra properties
|
||||
*/
|
||||
public Map<String, String> getExtraProperties()
|
||||
{
|
||||
return extraProperties;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets extra properties.
|
||||
*
|
||||
* @param extraProperties the extra properties
|
||||
*/
|
||||
public void setExtraProperties(Map<String, String> extraProperties)
|
||||
{
|
||||
this.extraProperties = extraProperties;
|
||||
}
|
||||
}
|
||||
|
@ -45,10 +45,5 @@ public interface DatabaseConnector {
|
||||
* @return true if it exists
|
||||
*/
|
||||
boolean uniqueIdExists(String tableName, String key);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,6 @@ import world.bentobox.bentobox.database.json.adapters.LocationTypeAdapter;
|
||||
import world.bentobox.bentobox.database.json.adapters.PotionEffectTypeAdapter;
|
||||
import world.bentobox.bentobox.database.json.adapters.VectorTypeAdapter;
|
||||
import world.bentobox.bentobox.database.json.adapters.WorldTypeAdapter;
|
||||
import world.bentobox.bentobox.versions.ServerCompatibility;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -21,7 +21,7 @@ public final class EnumTypeAdapter<T extends Enum<T>> extends TypeAdapter<T> {
|
||||
|
||||
|
||||
/**
|
||||
* Bimap to store name <-> enum references
|
||||
* Bimap to store name,enum pair references
|
||||
*/
|
||||
private final BiMap<String, T> enumMap = HashBiMap.create();
|
||||
|
||||
|
@ -24,8 +24,9 @@ public class LocationTypeAdapter extends TypeAdapter<Location> {
|
||||
out.value(location.getX());
|
||||
out.value(location.getY());
|
||||
out.value(location.getZ());
|
||||
out.value(location.getYaw());
|
||||
out.value(location.getPitch());
|
||||
// This is required for 1.19-1.19.2 compatibility.
|
||||
out.value((double) location.getYaw());
|
||||
out.value((double) location.getPitch());
|
||||
out.endArray();
|
||||
}
|
||||
|
||||
|
@ -5,9 +5,6 @@ import com.google.gson.annotations.Expose;
|
||||
/**
|
||||
* Record for bonus ranges
|
||||
* @author tastybento
|
||||
* @param id an id to identify this bonus
|
||||
* @param range the additional bonus range
|
||||
* @param message the reference key to a locale message related to this bonus. May be blank.
|
||||
*/
|
||||
public class BonusRangeRecord {
|
||||
@Expose
|
||||
@ -17,9 +14,9 @@ public class BonusRangeRecord {
|
||||
@Expose
|
||||
private String message;
|
||||
/**
|
||||
* @param uniqueId
|
||||
* @param range
|
||||
* @param message
|
||||
* @param uniqueId an id to identify this bonus
|
||||
* @param range the additional bonus range
|
||||
* @param message the reference key to a locale message related to this bonus. May be blank.
|
||||
*/
|
||||
public BonusRangeRecord(String uniqueId, int range, String message) {
|
||||
this.uniqueId = uniqueId;
|
||||
|
@ -16,9 +16,9 @@ import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.World.Environment;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.util.BoundingBox;
|
||||
|
@ -1,11 +1,12 @@
|
||||
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;
|
||||
|
||||
import org.bukkit.configuration.MemorySection;
|
||||
|
||||
|
||||
/**
|
||||
* This Serializer migrates Map of String, Boolean to Map of String, Integer in serialization process.
|
||||
@ -35,7 +36,7 @@ public class FlagBooleanSerializer implements AdapterInterface<Map<String, Integ
|
||||
{
|
||||
for (Entry<String, Boolean> en : ((Map<String, Boolean>) object).entrySet())
|
||||
{
|
||||
result.put(en.getKey(), en.getValue() ? 0 : -1);
|
||||
result.put(en.getKey(), Boolean.TRUE.equals(en.getValue()) ? 0 : -1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,7 @@ public class FlagSerializer2 implements AdapterInterface<Map<Flag, Integer>, Map
|
||||
}
|
||||
} else {
|
||||
for (Entry<String, Boolean> en : ((Map<String, Boolean>)object).entrySet()) {
|
||||
BentoBox.getInstance().getFlagsManager().getFlag(en.getKey()).ifPresent(flag -> result.put(flag, en.getValue() ? 0 : -1));
|
||||
BentoBox.getInstance().getFlagsManager().getFlag(en.getKey()).ifPresent(flag -> result.put(flag, Boolean.TRUE.equals(en.getValue()) ? 0 : -1));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
@ -9,34 +9,44 @@ import world.bentobox.bentobox.database.objects.Table;
|
||||
* @author tastybento
|
||||
*
|
||||
*/
|
||||
public class SQLConfiguration {
|
||||
public class SQLConfiguration
|
||||
{
|
||||
private String loadObjectSQL;
|
||||
|
||||
private String saveObjectSQL;
|
||||
|
||||
private String deleteObjectSQL;
|
||||
|
||||
private String objectExistsSQL;
|
||||
|
||||
private String schemaSQL;
|
||||
|
||||
private String loadObjectsSQL;
|
||||
|
||||
private String renameTableSQL;
|
||||
|
||||
private final String tableName;
|
||||
|
||||
private final boolean renameRequired;
|
||||
|
||||
private final String oldTableName;
|
||||
|
||||
public <T> SQLConfiguration(BentoBox plugin, Class<T> type) {
|
||||
|
||||
public <T> SQLConfiguration(BentoBox plugin, Class<T> type)
|
||||
{
|
||||
// Set the table name
|
||||
oldTableName = plugin.getSettings().getDatabasePrefix() + type.getCanonicalName();
|
||||
this.oldTableName = plugin.getSettings().getDatabasePrefix() + type.getCanonicalName();
|
||||
this.tableName = plugin.getSettings().getDatabasePrefix() +
|
||||
(type.getAnnotation(Table.class) == null ?
|
||||
type.getCanonicalName()
|
||||
: type.getAnnotation(Table.class).name());
|
||||
(type.getAnnotation(Table.class) == null ? type.getCanonicalName() : type.getAnnotation(Table.class).name());
|
||||
// Only rename if there is a specific Table annotation
|
||||
renameRequired = !tableName.equals(oldTableName);
|
||||
schema("CREATE TABLE IF NOT EXISTS `[tableName]` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) )");
|
||||
loadObjects("SELECT `json` FROM `[tableName]`");
|
||||
loadObject("SELECT `json` FROM `[tableName]` WHERE uniqueId = ? LIMIT 1");
|
||||
saveObject("INSERT INTO `[tableName]` (json) VALUES (?) ON DUPLICATE KEY UPDATE json = ?");
|
||||
deleteObject("DELETE FROM `[tableName]` WHERE uniqueId = ?");
|
||||
objectExists("SELECT IF ( EXISTS( SELECT * FROM `[tableName]` WHERE `uniqueId` = ?), 1, 0)");
|
||||
renameTable("SELECT Count(*) INTO @exists " +
|
||||
this.renameRequired = !this.tableName.equals(this.oldTableName);
|
||||
this.schema("CREATE TABLE IF NOT EXISTS `[tableName]` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) )");
|
||||
this.loadObjects("SELECT `json` FROM `[tableName]`");
|
||||
this.loadObject("SELECT `json` FROM `[tableName]` WHERE uniqueId = ? LIMIT 1");
|
||||
this.saveObject("INSERT INTO `[tableName]` (json) VALUES (?) ON DUPLICATE KEY UPDATE json = ?");
|
||||
this.deleteObject("DELETE FROM `[tableName]` WHERE uniqueId = ?");
|
||||
this.objectExists("SELECT IF ( EXISTS( SELECT * FROM `[tableName]` WHERE `uniqueId` = ?), 1, 0)");
|
||||
this.renameTable("SELECT Count(*) INTO @exists " +
|
||||
"FROM information_schema.tables " +
|
||||
"WHERE table_schema = '" + plugin.getSettings().getDatabaseName() + "' " +
|
||||
"AND table_type = 'BASE TABLE' " +
|
||||
@ -46,48 +56,66 @@ public class SQLConfiguration {
|
||||
"EXECUTE stmt;");
|
||||
}
|
||||
|
||||
private final String TABLE_NAME = "\\[tableName]";
|
||||
|
||||
private static final String TABLE_NAME = "\\[tableName]";
|
||||
|
||||
/**
|
||||
* By default, use quotes around the unique ID in the SQL statement
|
||||
*/
|
||||
private boolean useQuotes = true;
|
||||
|
||||
public SQLConfiguration loadObject(String string) {
|
||||
|
||||
public SQLConfiguration loadObject(String string)
|
||||
{
|
||||
this.loadObjectSQL = string.replaceFirst(TABLE_NAME, tableName);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SQLConfiguration saveObject(String string) {
|
||||
|
||||
public SQLConfiguration saveObject(String string)
|
||||
{
|
||||
this.saveObjectSQL = string.replaceFirst(TABLE_NAME, tableName);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SQLConfiguration deleteObject(String string) {
|
||||
|
||||
public SQLConfiguration deleteObject(String string)
|
||||
{
|
||||
this.deleteObjectSQL = string.replaceFirst(TABLE_NAME, tableName);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SQLConfiguration objectExists(String string) {
|
||||
|
||||
public SQLConfiguration objectExists(String string)
|
||||
{
|
||||
this.objectExistsSQL = string.replaceFirst(TABLE_NAME, tableName);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SQLConfiguration schema(String string) {
|
||||
|
||||
public SQLConfiguration schema(String string)
|
||||
{
|
||||
this.schemaSQL = string.replaceFirst(TABLE_NAME, tableName);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SQLConfiguration loadObjects(String string) {
|
||||
|
||||
public SQLConfiguration loadObjects(String string)
|
||||
{
|
||||
this.loadObjectsSQL = string.replaceFirst(TABLE_NAME, tableName);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SQLConfiguration renameTable(String string) {
|
||||
|
||||
public SQLConfiguration renameTable(String string)
|
||||
{
|
||||
this.renameTableSQL = string.replace(TABLE_NAME, tableName).replace("\\[oldTableName\\]", oldTableName);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SQLConfiguration setUseQuotes(boolean b) {
|
||||
|
||||
public SQLConfiguration setUseQuotes(boolean b)
|
||||
{
|
||||
this.useQuotes = b;
|
||||
return this;
|
||||
}
|
||||
@ -96,71 +124,95 @@ public class SQLConfiguration {
|
||||
/**
|
||||
* @return the loadObjectSQL
|
||||
*/
|
||||
public String getLoadObjectSQL() {
|
||||
public String getLoadObjectSQL()
|
||||
{
|
||||
return loadObjectSQL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the saveObjectSQL
|
||||
*/
|
||||
public String getSaveObjectSQL() {
|
||||
public String getSaveObjectSQL()
|
||||
{
|
||||
return saveObjectSQL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the deleteObjectSQL
|
||||
*/
|
||||
public String getDeleteObjectSQL() {
|
||||
public String getDeleteObjectSQL()
|
||||
{
|
||||
return deleteObjectSQL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the objectExistsSQL
|
||||
*/
|
||||
public String getObjectExistsSQL() {
|
||||
public String getObjectExistsSQL()
|
||||
{
|
||||
return objectExistsSQL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the schemaSQL
|
||||
*/
|
||||
public String getSchemaSQL() {
|
||||
public String getSchemaSQL()
|
||||
{
|
||||
return schemaSQL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the loadItSQL
|
||||
*/
|
||||
public String getLoadObjectsSQL() {
|
||||
public String getLoadObjectsSQL()
|
||||
{
|
||||
return loadObjectsSQL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the renameTableSQL
|
||||
*/
|
||||
public String getRenameTableSQL() {
|
||||
public String getRenameTableSQL()
|
||||
{
|
||||
return renameTableSQL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the tableName
|
||||
*/
|
||||
public String getTableName() {
|
||||
public String getTableName()
|
||||
{
|
||||
return tableName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the oldName
|
||||
*/
|
||||
public String getOldTableName() {
|
||||
public String getOldTableName()
|
||||
{
|
||||
return oldTableName;
|
||||
}
|
||||
|
||||
public boolean renameRequired() {
|
||||
|
||||
public boolean renameRequired()
|
||||
{
|
||||
return renameRequired;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the useQuotes
|
||||
*/
|
||||
public boolean isUseQuotes() {
|
||||
public boolean isUseQuotes()
|
||||
{
|
||||
return useQuotes;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package world.bentobox.bentobox.database.sql;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
@ -9,64 +8,136 @@ import java.util.Set;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
|
||||
import world.bentobox.bentobox.database.DatabaseConnectionSettingsImpl;
|
||||
import world.bentobox.bentobox.database.DatabaseConnector;
|
||||
|
||||
public abstract class SQLDatabaseConnector implements DatabaseConnector {
|
||||
|
||||
/**
|
||||
* Generic SQL database connector.
|
||||
*/
|
||||
public abstract class SQLDatabaseConnector implements DatabaseConnector
|
||||
{
|
||||
/**
|
||||
* The connection url string for the sql database.
|
||||
*/
|
||||
protected String connectionUrl;
|
||||
private final DatabaseConnectionSettingsImpl dbSettings;
|
||||
protected static Connection connection = null;
|
||||
|
||||
/**
|
||||
* The database connection settings.
|
||||
*/
|
||||
protected final DatabaseConnectionSettingsImpl dbSettings;
|
||||
|
||||
/**
|
||||
* Hikari Data Source that creates all connections.
|
||||
*/
|
||||
protected static HikariDataSource dataSource;
|
||||
|
||||
/**
|
||||
* Type of objects stored in database.
|
||||
*/
|
||||
protected static Set<Class<?>> types = new HashSet<>();
|
||||
|
||||
protected SQLDatabaseConnector(DatabaseConnectionSettingsImpl dbSettings, String connectionUrl) {
|
||||
|
||||
/**
|
||||
* Default connector constructor.
|
||||
* @param dbSettings Settings of the database.
|
||||
* @param connectionUrl Connection url for the database.
|
||||
*/
|
||||
protected SQLDatabaseConnector(DatabaseConnectionSettingsImpl dbSettings, String connectionUrl)
|
||||
{
|
||||
this.dbSettings = dbSettings;
|
||||
this.connectionUrl = connectionUrl;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns connection url of database.
|
||||
* @return Database connection url.
|
||||
*/
|
||||
@Override
|
||||
public String getConnectionUrl() {
|
||||
public String getConnectionUrl()
|
||||
{
|
||||
return connectionUrl;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
@NonNull
|
||||
public String getUniqueId(String tableName) {
|
||||
public String getUniqueId(String tableName)
|
||||
{
|
||||
// Not used
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean uniqueIdExists(String tableName, String key) {
|
||||
public boolean uniqueIdExists(String tableName, String key)
|
||||
{
|
||||
// Not used
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void closeConnection(Class<?> type) {
|
||||
public void closeConnection(Class<?> type)
|
||||
{
|
||||
types.remove(type);
|
||||
if (types.isEmpty() && connection != null) {
|
||||
try {
|
||||
connection.close();
|
||||
|
||||
if (types.isEmpty())
|
||||
{
|
||||
dataSource.close();
|
||||
Bukkit.getLogger().info("Closed database connection");
|
||||
} catch (SQLException e) {
|
||||
Bukkit.getLogger().severe("Could not close database connection");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method creates config that is used to create HikariDataSource.
|
||||
* @return HikariConfig object.
|
||||
*/
|
||||
public abstract HikariConfig createConfig();
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Object createConnection(Class<?> type) {
|
||||
public Object createConnection(Class<?> type)
|
||||
{
|
||||
types.add(type);
|
||||
|
||||
// Only make one connection to the database
|
||||
if (connection == null) {
|
||||
try {
|
||||
connection = DriverManager.getConnection(connectionUrl, dbSettings.getUsername(), dbSettings.getPassword());
|
||||
} catch (SQLException e) {
|
||||
if (dataSource == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
dataSource = new HikariDataSource(this.createConfig());
|
||||
|
||||
// Test connection
|
||||
try (Connection connection = dataSource.getConnection())
|
||||
{
|
||||
connection.isValid(5 * 1000);
|
||||
}
|
||||
}
|
||||
catch (SQLException e)
|
||||
{
|
||||
Bukkit.getLogger().severe("Could not connect to the database! " + e.getMessage());
|
||||
dataSource = null;
|
||||
}
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
|
||||
return dataSource;
|
||||
}
|
||||
}
|
@ -11,6 +11,8 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
|
||||
@ -31,246 +33,413 @@ import world.bentobox.bentobox.database.objects.DataObject;
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public class SQLDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
||||
|
||||
public class SQLDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T>
|
||||
{
|
||||
protected static final String COULD_NOT_LOAD_OBJECTS = "Could not load objects ";
|
||||
protected static final String COULD_NOT_LOAD_OBJECT = "Could not load object ";
|
||||
|
||||
/**
|
||||
* Connection to the database
|
||||
* DataSource of database
|
||||
*/
|
||||
private Connection connection;
|
||||
protected DataSource dataSource;
|
||||
|
||||
/**
|
||||
* SQL configuration
|
||||
*/
|
||||
private SQLConfiguration sqlConfig;
|
||||
|
||||
|
||||
/**
|
||||
* Handles the connection to the database and creation of the initial database schema (tables) for
|
||||
* the class that will be stored.
|
||||
* @param plugin - plugin object
|
||||
* @param type - the type of class to be stored in the database. Must inherit DataObject
|
||||
* @param dbConnecter - authentication details for the database
|
||||
* @param databaseConnector - authentication details for the database
|
||||
* @param sqlConfiguration - SQL configuration
|
||||
*/
|
||||
protected SQLDatabaseHandler(BentoBox plugin, Class<T> type, DatabaseConnector dbConnecter, SQLConfiguration sqlConfiguration) {
|
||||
super(plugin, type, dbConnecter);
|
||||
protected SQLDatabaseHandler(BentoBox plugin, Class<T> type, DatabaseConnector databaseConnector, SQLConfiguration sqlConfiguration)
|
||||
{
|
||||
super(plugin, type, databaseConnector);
|
||||
this.sqlConfig = sqlConfiguration;
|
||||
if (setConnection((Connection)databaseConnector.createConnection(type))) {
|
||||
|
||||
if (this.setDataSource((DataSource) this.databaseConnector.createConnection(type)))
|
||||
{
|
||||
// Check if the table exists in the database and if not, create it
|
||||
createSchema();
|
||||
this.createSchema();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the sqlConfig
|
||||
*/
|
||||
public SQLConfiguration getSqlConfig() {
|
||||
public SQLConfiguration getSqlConfig()
|
||||
{
|
||||
return sqlConfig;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param sqlConfig the sqlConfig to set
|
||||
*/
|
||||
public void setSqlConfig(SQLConfiguration sqlConfig) {
|
||||
public void setSqlConfig(SQLConfiguration sqlConfig)
|
||||
{
|
||||
this.sqlConfig = sqlConfig;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates the table in the database if it doesn't exist already
|
||||
*/
|
||||
protected void createSchema() {
|
||||
if (sqlConfig.renameRequired()) {
|
||||
protected void createSchema()
|
||||
{
|
||||
if (this.sqlConfig.renameRequired())
|
||||
{
|
||||
// Transition from the old table name
|
||||
String sql = sqlConfig.getRenameTableSQL().replace("[oldTableName]", sqlConfig.getOldTableName()).replace("[tableName]", sqlConfig.getTableName());
|
||||
try (PreparedStatement pstmt = connection.prepareStatement(sql)) {
|
||||
pstmt.execute();
|
||||
} catch (SQLException e) {
|
||||
plugin.logError("Could not rename " + sqlConfig.getOldTableName() + " for data object " + dataObject.getCanonicalName() + " " + e.getMessage());
|
||||
String sql = this.sqlConfig.getRenameTableSQL().
|
||||
replace("[oldTableName]", this.sqlConfig.getOldTableName()).
|
||||
replace("[tableName]", this.sqlConfig.getTableName());
|
||||
|
||||
try (Connection connection = this.dataSource.getConnection();
|
||||
PreparedStatement preparedStatement = connection.prepareStatement(sql))
|
||||
{
|
||||
preparedStatement.execute();
|
||||
}
|
||||
}
|
||||
// Prepare and execute the database statements
|
||||
try (PreparedStatement pstmt = connection.prepareStatement(sqlConfig.getSchemaSQL())) {
|
||||
pstmt.execute();
|
||||
} catch (SQLException e) {
|
||||
plugin.logError("Problem trying to create schema for data object " + dataObject.getCanonicalName() + " " + e.getMessage());
|
||||
catch (SQLException e)
|
||||
{
|
||||
this.plugin.logError("Could not rename " + this.sqlConfig.getOldTableName() + " for data object " +
|
||||
this.dataObject.getCanonicalName() + " " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> loadObjects() {
|
||||
try (Statement preparedStatement = connection.createStatement()) {
|
||||
return loadIt(preparedStatement);
|
||||
} catch (SQLException e) {
|
||||
plugin.logError(COULD_NOT_LOAD_OBJECTS + e.getMessage());
|
||||
// Prepare and execute the database statements
|
||||
try (Connection connection = this.dataSource.getConnection();
|
||||
PreparedStatement preparedStatement = connection.prepareStatement(this.sqlConfig.getSchemaSQL()))
|
||||
{
|
||||
preparedStatement.execute();
|
||||
}
|
||||
catch (SQLException e)
|
||||
{
|
||||
this.plugin.logError("Problem trying to create schema for data object " +
|
||||
this.dataObject.getCanonicalName() + " " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public List<T> loadObjects()
|
||||
{
|
||||
try (Connection connection = this.dataSource.getConnection();
|
||||
Statement preparedStatement = connection.createStatement())
|
||||
{
|
||||
return this.loadIt(preparedStatement);
|
||||
}
|
||||
catch (SQLException e)
|
||||
{
|
||||
this.plugin.logError(COULD_NOT_LOAD_OBJECTS + e.getMessage());
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private List<T> loadIt(Statement preparedStatement) {
|
||||
|
||||
/**
|
||||
* This method loads objects based on results provided by prepared statement.
|
||||
* @param preparedStatement Statement from database.
|
||||
* @return List of object <T> from database.
|
||||
*/
|
||||
private List<T> loadIt(Statement preparedStatement)
|
||||
{
|
||||
List<T> list = new ArrayList<>();
|
||||
try (ResultSet resultSet = preparedStatement.executeQuery(sqlConfig.getLoadObjectsSQL())) {
|
||||
|
||||
try (ResultSet resultSet = preparedStatement.executeQuery(this.sqlConfig.getLoadObjectsSQL()))
|
||||
{
|
||||
// Load all the results
|
||||
Gson gson = getGson();
|
||||
while (resultSet.next()) {
|
||||
Gson gson = this.getGson();
|
||||
|
||||
while (resultSet.next())
|
||||
{
|
||||
String json = resultSet.getString("json");
|
||||
if (json != null) {
|
||||
try {
|
||||
T gsonResult = gson.fromJson(json, dataObject);
|
||||
if (gsonResult != null) {
|
||||
list.add(gsonResult);
|
||||
}
|
||||
} catch (JsonSyntaxException ex) {
|
||||
plugin.logError(COULD_NOT_LOAD_OBJECT + ex.getMessage());
|
||||
plugin.logError(json);
|
||||
|
||||
if (json != null)
|
||||
{
|
||||
getGsonResultSet(gson, json, list);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
plugin.logError(COULD_NOT_LOAD_OBJECTS + e.getMessage());
|
||||
catch (Exception e)
|
||||
{
|
||||
this.plugin.logError(COULD_NOT_LOAD_OBJECTS + e.getMessage());
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
private void getGsonResultSet(Gson gson, String json, List<T> list) {
|
||||
try
|
||||
{
|
||||
T gsonResult = gson.fromJson(json, this.dataObject);
|
||||
|
||||
if (gsonResult != null)
|
||||
{
|
||||
list.add(gsonResult);
|
||||
}
|
||||
}
|
||||
catch (JsonSyntaxException ex)
|
||||
{
|
||||
this.plugin.logError(COULD_NOT_LOAD_OBJECT + ex.getMessage());
|
||||
this.plugin.logError(json);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public T loadObject(@NonNull String uniqueId) {
|
||||
try (PreparedStatement preparedStatement = connection.prepareStatement(sqlConfig.getLoadObjectSQL())) {
|
||||
public T loadObject(@NonNull String uniqueId)
|
||||
{
|
||||
T result = null;
|
||||
try (Connection connection = this.dataSource.getConnection();
|
||||
PreparedStatement preparedStatement = connection.prepareStatement(this.sqlConfig.getLoadObjectSQL()))
|
||||
{
|
||||
// UniqueId needs to be placed in quotes?
|
||||
preparedStatement.setString(1, this.sqlConfig.isUseQuotes() ? "\"" + uniqueId + "\"" : uniqueId);
|
||||
try (ResultSet resultSet = preparedStatement.executeQuery()) {
|
||||
if (resultSet.next()) {
|
||||
result = getObject(uniqueId, preparedStatement);
|
||||
}
|
||||
catch (SQLException e)
|
||||
{
|
||||
this.plugin.logError(COULD_NOT_LOAD_OBJECT + uniqueId + " " + e.getMessage());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the object decoded from JSON or null if there is an error
|
||||
* @param uniqueId - unique Id of object used in error reporting
|
||||
* @param preparedStatement - database statement to execute
|
||||
* @return
|
||||
*/
|
||||
private T getObject(@NonNull String uniqueId, PreparedStatement preparedStatement) {
|
||||
try (ResultSet resultSet = preparedStatement.executeQuery())
|
||||
{
|
||||
if (resultSet.next())
|
||||
{
|
||||
// If there is a result, we only want/need the first one
|
||||
Gson gson = getGson();
|
||||
return gson.fromJson(resultSet.getString("json"), dataObject);
|
||||
Gson gson = this.getGson();
|
||||
return gson.fromJson(resultSet.getString("json"), this.dataObject);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
plugin.logError(COULD_NOT_LOAD_OBJECT + uniqueId + " " + e.getMessage());
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
plugin.logError(COULD_NOT_LOAD_OBJECT + uniqueId + " " + e.getMessage());
|
||||
catch (Exception e)
|
||||
{
|
||||
this.plugin.logError(COULD_NOT_LOAD_OBJECT + uniqueId + " " + e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public CompletableFuture<Boolean> saveObject(T instance) {
|
||||
public CompletableFuture<Boolean> saveObject(T instance)
|
||||
{
|
||||
CompletableFuture<Boolean> completableFuture = new CompletableFuture<>();
|
||||
|
||||
// Null check
|
||||
if (instance == null) {
|
||||
plugin.logError("SQL database request to store a null. ");
|
||||
if (instance == null)
|
||||
{
|
||||
this.plugin.logError("SQL database request to store a null. ");
|
||||
completableFuture.complete(false);
|
||||
return completableFuture;
|
||||
}
|
||||
if (!(instance instanceof DataObject)) {
|
||||
plugin.logError("This class is not a DataObject: " + instance.getClass().getName());
|
||||
completableFuture.complete(false);
|
||||
return completableFuture;
|
||||
}
|
||||
// This has to be on the main thread to avoid concurrent modification errors
|
||||
String toStore = getGson().toJson(instance);
|
||||
if (plugin.isEnabled()) {
|
||||
// Async
|
||||
processQueue.add(() -> store(completableFuture, instance.getClass().getName(), toStore, sqlConfig.getSaveObjectSQL(), true));
|
||||
} else {
|
||||
// Sync
|
||||
store(completableFuture, instance.getClass().getName(), toStore, sqlConfig.getSaveObjectSQL(), false);
|
||||
}
|
||||
return completableFuture;
|
||||
}
|
||||
|
||||
private void store(CompletableFuture<Boolean> completableFuture, String name, String toStore, String sb, boolean async) {
|
||||
if (!(instance instanceof DataObject))
|
||||
{
|
||||
this.plugin.logError("This class is not a DataObject: " + instance.getClass().getName());
|
||||
completableFuture.complete(false);
|
||||
return completableFuture;
|
||||
}
|
||||
|
||||
// This has to be on the main thread to avoid concurrent modification errors
|
||||
String toStore = this.getGson().toJson(instance);
|
||||
|
||||
if (this.plugin.isEnabled())
|
||||
{
|
||||
// Async
|
||||
this.processQueue.add(() -> store(completableFuture,
|
||||
instance.getClass().getName(),
|
||||
toStore,
|
||||
this.sqlConfig.getSaveObjectSQL(),
|
||||
true));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Sync
|
||||
this.store(completableFuture, instance.getClass().getName(), toStore, this.sqlConfig.getSaveObjectSQL(), false);
|
||||
}
|
||||
|
||||
return completableFuture;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method is called to save data into database based on given parameters.
|
||||
* @param completableFuture Failsafe on saving data.
|
||||
* @param name Name of the class that is saved.
|
||||
* @param toStore data that is stored.
|
||||
* @param storeSQL SQL command for saving.
|
||||
* @param async boolean that indicates if saving is async or not.
|
||||
*/
|
||||
private void store(CompletableFuture<Boolean> completableFuture, String name, String toStore, String storeSQL, boolean async)
|
||||
{
|
||||
// Do not save anything if plug is disabled and this was an async request
|
||||
if (async && !plugin.isEnabled()) return;
|
||||
try (PreparedStatement preparedStatement = connection.prepareStatement(sb)) {
|
||||
if (async && !this.plugin.isEnabled())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try (Connection connection = this.dataSource.getConnection();
|
||||
PreparedStatement preparedStatement = connection.prepareStatement(storeSQL))
|
||||
{
|
||||
preparedStatement.setString(1, toStore);
|
||||
preparedStatement.setString(2, toStore);
|
||||
preparedStatement.execute();
|
||||
completableFuture.complete(true);
|
||||
} catch (SQLException e) {
|
||||
plugin.logError("Could not save object " + name + " " + e.getMessage());
|
||||
}
|
||||
catch (SQLException e)
|
||||
{
|
||||
this.plugin.logError("Could not save object " + name + " " + e.getMessage());
|
||||
completableFuture.complete(false);
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see world.bentobox.bentobox.database.AbstractDatabaseHandler#deleteID(java.lang.String)
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void deleteID(String uniqueId) {
|
||||
processQueue.add(() -> delete(uniqueId));
|
||||
public void deleteID(String uniqueId)
|
||||
{
|
||||
this.processQueue.add(() -> this.delete(uniqueId));
|
||||
}
|
||||
|
||||
private void delete(String uniqueId) {
|
||||
try (PreparedStatement preparedStatement = connection.prepareStatement(sqlConfig.getDeleteObjectSQL())) {
|
||||
|
||||
/**
|
||||
* This method triggers object deletion from the database.
|
||||
* @param uniqueId Object unique id.
|
||||
*/
|
||||
private void delete(String uniqueId)
|
||||
{
|
||||
try (Connection connection = this.dataSource.getConnection();
|
||||
PreparedStatement preparedStatement = connection.prepareStatement(this.sqlConfig.getDeleteObjectSQL()))
|
||||
{
|
||||
// UniqueId needs to be placed in quotes?
|
||||
preparedStatement.setString(1, this.sqlConfig.isUseQuotes() ? "\"" + uniqueId + "\"" : uniqueId);
|
||||
preparedStatement.execute();
|
||||
} catch (Exception e) {
|
||||
plugin.logError("Could not delete object " + plugin.getSettings().getDatabasePrefix() + dataObject.getCanonicalName() + " " + uniqueId + " " + e.getMessage());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.plugin.logError("Could not delete object " + this.plugin.getSettings().getDatabasePrefix() +
|
||||
this.dataObject.getCanonicalName() + " " + uniqueId + " " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void deleteObject(T instance) {
|
||||
public void deleteObject(T instance)
|
||||
{
|
||||
// Null check
|
||||
if (instance == null) {
|
||||
plugin.logError("SQL database request to delete a null.");
|
||||
if (instance == null)
|
||||
{
|
||||
this.plugin.logError("SQL database request to delete a null.");
|
||||
return;
|
||||
}
|
||||
if (!(instance instanceof DataObject)) {
|
||||
plugin.logError("This class is not a DataObject: " + instance.getClass().getName());
|
||||
|
||||
if (!(instance instanceof DataObject))
|
||||
{
|
||||
this.plugin.logError("This class is not a DataObject: " + instance.getClass().getName());
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Method getUniqueId = dataObject.getMethod("getUniqueId");
|
||||
deleteID((String) getUniqueId.invoke(instance));
|
||||
} catch (Exception e) {
|
||||
plugin.logError("Could not delete object " + instance.getClass().getName() + " " + e.getMessage());
|
||||
|
||||
try
|
||||
{
|
||||
Method getUniqueId = this.dataObject.getMethod("getUniqueId");
|
||||
this.deleteID((String) getUniqueId.invoke(instance));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.plugin.logError("Could not delete object " + instance.getClass().getName() + " " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean objectExists(String uniqueId) {
|
||||
public boolean objectExists(String uniqueId)
|
||||
{
|
||||
// Query to see if this key exists
|
||||
try (PreparedStatement preparedStatement = connection.prepareStatement(sqlConfig.getObjectExistsSQL())) {
|
||||
try (Connection connection = this.dataSource.getConnection();
|
||||
PreparedStatement preparedStatement = connection.prepareStatement(this.sqlConfig.getObjectExistsSQL()))
|
||||
{
|
||||
// UniqueId needs to be placed in quotes?
|
||||
preparedStatement.setString(1, this.sqlConfig.isUseQuotes() ? "\"" + uniqueId + "\"" : uniqueId);
|
||||
try (ResultSet resultSet = preparedStatement.executeQuery()) {
|
||||
if (resultSet.next()) {
|
||||
|
||||
try (ResultSet resultSet = preparedStatement.executeQuery())
|
||||
{
|
||||
if (resultSet.next())
|
||||
{
|
||||
return resultSet.getBoolean(1);
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
plugin.logError("Could not check if key exists in database! " + uniqueId + " " + e.getMessage());
|
||||
}
|
||||
catch (SQLException e)
|
||||
{
|
||||
this.plugin.logError("Could not check if key exists in database! " + uniqueId + " " + e.getMessage());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
shutdown = true;
|
||||
public void close()
|
||||
{
|
||||
this.shutdown = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the connection
|
||||
*/
|
||||
public Connection getConnection() {
|
||||
return connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param connection the connection to set
|
||||
* @return true if connection is not null
|
||||
* Sets data source of database.
|
||||
*
|
||||
* @param dataSource the data source
|
||||
* @return {@code true} if data source is set, {@code false} otherwise.
|
||||
*/
|
||||
public boolean setConnection(Connection connection) {
|
||||
if (connection == null) {
|
||||
plugin.logError("Could not connect to the database. Are the credentials in the config.yml file correct?");
|
||||
plugin.logWarning("Disabling the plugin...");
|
||||
Bukkit.getPluginManager().disablePlugin(plugin);
|
||||
public boolean setDataSource(DataSource dataSource)
|
||||
{
|
||||
if (dataSource == null)
|
||||
{
|
||||
this.plugin.logError("Could not connect to the database. Are the credentials in the config.yml file correct?");
|
||||
this.plugin.logWarning("Disabling the plugin...");
|
||||
Bukkit.getPluginManager().disablePlugin(this.plugin);
|
||||
return false;
|
||||
}
|
||||
this.connection = connection;
|
||||
this.dataSource = dataSource;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -9,26 +9,34 @@ import world.bentobox.bentobox.database.DatabaseSetup;
|
||||
* @author barpec12
|
||||
* @since 1.1
|
||||
*/
|
||||
public class MariaDBDatabase implements DatabaseSetup {
|
||||
public class MariaDBDatabase implements DatabaseSetup
|
||||
{
|
||||
/**
|
||||
* MariaDB Database Connector.
|
||||
*/
|
||||
private MariaDBDatabaseConnector connector;
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see world.bentobox.bentobox.database.DatabaseSetup#getHandler(java.lang.Class)
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public <T> AbstractDatabaseHandler<T> getHandler(Class<T> type) {
|
||||
public <T> AbstractDatabaseHandler<T> getHandler(Class<T> type)
|
||||
{
|
||||
BentoBox plugin = BentoBox.getInstance();
|
||||
if (connector == null) {
|
||||
connector = new MariaDBDatabaseConnector(new DatabaseConnectionSettingsImpl(
|
||||
|
||||
if (this.connector == null)
|
||||
{
|
||||
this.connector = new MariaDBDatabaseConnector(new DatabaseConnectionSettingsImpl(
|
||||
plugin.getSettings().getDatabaseHost(),
|
||||
plugin.getSettings().getDatabasePort(),
|
||||
plugin.getSettings().getDatabaseName(),
|
||||
plugin.getSettings().getDatabaseUsername(),
|
||||
plugin.getSettings().getDatabasePassword(),
|
||||
plugin.getSettings().isUseSSL()
|
||||
));
|
||||
}
|
||||
return new MariaDBDatabaseHandler<>(plugin, type, connector);
|
||||
plugin.getSettings().isUseSSL(),
|
||||
plugin.getSettings().getMaximumPoolSize()));
|
||||
}
|
||||
|
||||
return new MariaDBDatabaseHandler<>(plugin, type, this.connector);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,9 @@
|
||||
package world.bentobox.bentobox.database.sql.mariadb;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
|
||||
import world.bentobox.bentobox.database.DatabaseConnectionSettingsImpl;
|
||||
import world.bentobox.bentobox.database.sql.SQLDatabaseConnector;
|
||||
|
||||
@ -7,15 +11,45 @@ import world.bentobox.bentobox.database.sql.SQLDatabaseConnector;
|
||||
* @author barpec12
|
||||
* @since 1.1
|
||||
*/
|
||||
public class MariaDBDatabaseConnector extends SQLDatabaseConnector {
|
||||
|
||||
public class MariaDBDatabaseConnector extends SQLDatabaseConnector
|
||||
{
|
||||
/**
|
||||
* Class for MariaDB database connections using the settings provided
|
||||
* @param dbSettings - database settings
|
||||
*/
|
||||
MariaDBDatabaseConnector(DatabaseConnectionSettingsImpl dbSettings) {
|
||||
super(dbSettings, "jdbc:mysql://" + dbSettings.getHost() + ":" + dbSettings.getPort() + "/" + dbSettings.getDatabaseName()
|
||||
+ "?autoReconnect=true&useSSL=" + dbSettings.isUseSSL() + "&allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8");
|
||||
MariaDBDatabaseConnector(@NonNull DatabaseConnectionSettingsImpl dbSettings)
|
||||
{
|
||||
// MariaDB does not use connectionUrl.
|
||||
super(dbSettings, String.format("jdbc:mariadb://%s:%s/%s",
|
||||
dbSettings.getHost(),
|
||||
dbSettings.getPort(),
|
||||
dbSettings.getDatabaseName()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public HikariConfig createConfig()
|
||||
{
|
||||
HikariConfig config = new HikariConfig();
|
||||
|
||||
config.setPoolName("BentoBox MariaDB Pool");
|
||||
config.setDriverClassName("org.mariadb.jdbc.Driver");
|
||||
|
||||
config.setJdbcUrl(this.connectionUrl);
|
||||
config.addDataSourceProperty("user", this.dbSettings.getUsername());
|
||||
config.addDataSourceProperty("password", this.dbSettings.getPassword());
|
||||
|
||||
config.addDataSourceProperty("useSsl", this.dbSettings.isUseSSL());
|
||||
config.addDataSourceProperty("allowMultiQueries", "true");
|
||||
|
||||
// Add extra properties.
|
||||
this.dbSettings.getExtraProperties().forEach(config::addDataSourceProperty);
|
||||
|
||||
config.setMaximumPoolSize(this.dbSettings.getMaxConnections());
|
||||
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
@ -13,8 +13,8 @@ import world.bentobox.bentobox.database.sql.SQLDatabaseHandler;
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public class MariaDBDatabaseHandler<T> extends SQLDatabaseHandler<T> {
|
||||
|
||||
public class MariaDBDatabaseHandler<T> extends SQLDatabaseHandler<T>
|
||||
{
|
||||
/**
|
||||
* Handles the connection to the database and creation of the initial database schema (tables) for
|
||||
* the class that will be stored.
|
||||
@ -22,9 +22,11 @@ public class MariaDBDatabaseHandler<T> extends SQLDatabaseHandler<T> {
|
||||
* @param type - the type of class to be stored in the database. Must inherit DataObject
|
||||
* @param databaseConnector - authentication details for the database
|
||||
*/
|
||||
MariaDBDatabaseHandler(BentoBox plugin, Class<T> type, DatabaseConnector databaseConnector) {
|
||||
super(plugin, type, databaseConnector,
|
||||
new SQLConfiguration(plugin, type)
|
||||
.schema("CREATE TABLE IF NOT EXISTS `[tableName]` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (JSON_EXTRACT(json, \"$.uniqueId\")), UNIQUE INDEX i (uniqueId))"));
|
||||
MariaDBDatabaseHandler(BentoBox plugin, Class<T> type, DatabaseConnector databaseConnector)
|
||||
{
|
||||
super(plugin,
|
||||
type,
|
||||
databaseConnector,
|
||||
new SQLConfiguration(plugin, type).schema("CREATE TABLE IF NOT EXISTS `[tableName]` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (JSON_EXTRACT(json, \"$.uniqueId\")), UNIQUE INDEX i (uniqueId))"));
|
||||
}
|
||||
}
|
||||
|
@ -5,27 +5,34 @@ import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
||||
import world.bentobox.bentobox.database.DatabaseConnectionSettingsImpl;
|
||||
import world.bentobox.bentobox.database.DatabaseSetup;
|
||||
|
||||
public class MySQLDatabase implements DatabaseSetup {
|
||||
|
||||
public class MySQLDatabase implements DatabaseSetup
|
||||
{
|
||||
/**
|
||||
* MySQL Database Connector
|
||||
*/
|
||||
private MySQLDatabaseConnector connector;
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see world.bentobox.bentobox.database.DatabaseSetup#getHandler(java.lang.Class)
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public <T> AbstractDatabaseHandler<T> getHandler(Class<T> type) {
|
||||
public <T> AbstractDatabaseHandler<T> getHandler(Class<T> type)
|
||||
{
|
||||
BentoBox plugin = BentoBox.getInstance();
|
||||
if (connector == null) {
|
||||
connector = new MySQLDatabaseConnector(new DatabaseConnectionSettingsImpl(
|
||||
|
||||
if (this.connector == null)
|
||||
{
|
||||
this.connector = new MySQLDatabaseConnector(new DatabaseConnectionSettingsImpl(
|
||||
plugin.getSettings().getDatabaseHost(),
|
||||
plugin.getSettings().getDatabasePort(),
|
||||
plugin.getSettings().getDatabaseName(),
|
||||
plugin.getSettings().getDatabaseUsername(),
|
||||
plugin.getSettings().getDatabasePassword(),
|
||||
plugin.getSettings().isUseSSL()
|
||||
));
|
||||
}
|
||||
return new MySQLDatabaseHandler<>(plugin, type, connector);
|
||||
plugin.getSettings().isUseSSL(),
|
||||
plugin.getSettings().getMaximumPoolSize()));
|
||||
}
|
||||
|
||||
return new MySQLDatabaseHandler<>(plugin, type, this.connector);
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,56 @@
|
||||
package world.bentobox.bentobox.database.sql.mysql;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
|
||||
import world.bentobox.bentobox.database.DatabaseConnectionSettingsImpl;
|
||||
import world.bentobox.bentobox.database.sql.SQLDatabaseConnector;
|
||||
|
||||
public class MySQLDatabaseConnector extends SQLDatabaseConnector {
|
||||
|
||||
public class MySQLDatabaseConnector extends SQLDatabaseConnector
|
||||
{
|
||||
/**
|
||||
* Class for MySQL database connections using the settings provided
|
||||
*
|
||||
* @param dbSettings - database settings
|
||||
*/
|
||||
MySQLDatabaseConnector(DatabaseConnectionSettingsImpl dbSettings) {
|
||||
super(dbSettings, "jdbc:mysql://" + dbSettings.getHost() + ":" + dbSettings.getPort() + "/" + dbSettings.getDatabaseName()
|
||||
+ "?autoReconnect=true&useSSL=" + dbSettings.isUseSSL() + "&allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8");
|
||||
MySQLDatabaseConnector(@NonNull DatabaseConnectionSettingsImpl dbSettings)
|
||||
{
|
||||
super(dbSettings, String.format("jdbc:mysql://%s:%s/%s",
|
||||
dbSettings.getHost(),
|
||||
dbSettings.getPort(),
|
||||
dbSettings.getDatabaseName()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public HikariConfig createConfig()
|
||||
{
|
||||
HikariConfig config = new HikariConfig();
|
||||
config.setPoolName("BentoBox MySQL Pool");
|
||||
|
||||
config.setDriverClassName("com.mysql.jdbc.Driver");
|
||||
config.setJdbcUrl(this.connectionUrl);
|
||||
config.setUsername(this.dbSettings.getUsername());
|
||||
config.setPassword(this.dbSettings.getPassword());
|
||||
|
||||
config.addDataSourceProperty("useSSL", this.dbSettings.isUseSSL());
|
||||
|
||||
config.addDataSourceProperty("characterEncoding", "utf8");
|
||||
config.addDataSourceProperty("encoding", "UTF-8");
|
||||
config.addDataSourceProperty("useUnicode", "true");
|
||||
config.addDataSourceProperty("allowMultiQueries", "true");
|
||||
|
||||
config.addDataSourceProperty("allowPublicKeyRetrieval", "true");
|
||||
|
||||
// Add extra properties.
|
||||
this.dbSettings.getExtraProperties().forEach(config::addDataSourceProperty);
|
||||
|
||||
config.setMaximumPoolSize(this.dbSettings.getMaxConnections());
|
||||
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
@ -13,17 +13,21 @@ import world.bentobox.bentobox.database.sql.SQLDatabaseHandler;
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public class MySQLDatabaseHandler<T> extends SQLDatabaseHandler<T> {
|
||||
|
||||
public class MySQLDatabaseHandler<T> extends SQLDatabaseHandler<T>
|
||||
{
|
||||
/**
|
||||
* Handles the connection to the database and creation of the initial database schema (tables) for
|
||||
* the class that will be stored.
|
||||
* Handles the connection to the database and creation of the initial database schema (tables) for the class that
|
||||
* will be stored.
|
||||
*
|
||||
* @param plugin - plugin object
|
||||
* @param type - the type of class to be stored in the database. Must inherit DataObject
|
||||
* @param dbConnecter - authentication details for the database
|
||||
*/
|
||||
MySQLDatabaseHandler(BentoBox plugin, Class<T> type, DatabaseConnector dbConnecter) {
|
||||
super(plugin, type, dbConnecter, new SQLConfiguration(plugin, type)
|
||||
.schema("CREATE TABLE IF NOT EXISTS `[tableName]` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) ) ENGINE = INNODB"));
|
||||
MySQLDatabaseHandler(BentoBox plugin, Class<T> type, DatabaseConnector dbConnecter)
|
||||
{
|
||||
super(plugin,
|
||||
type,
|
||||
dbConnecter,
|
||||
new SQLConfiguration(plugin, type).schema("CREATE TABLE IF NOT EXISTS `[tableName]` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) ) ENGINE = INNODB"));
|
||||
}
|
||||
}
|
||||
|
@ -9,23 +9,34 @@ import world.bentobox.bentobox.database.DatabaseSetup;
|
||||
* @since 1.6.0
|
||||
* @author Poslovitch
|
||||
*/
|
||||
public class PostgreSQLDatabase implements DatabaseSetup {
|
||||
|
||||
public class PostgreSQLDatabase implements DatabaseSetup
|
||||
{
|
||||
/**
|
||||
* PostgreSQL Database Connector.
|
||||
*/
|
||||
PostgreSQLDatabaseConnector connector;
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public <T> AbstractDatabaseHandler<T> getHandler(Class<T> dataObjectClass) {
|
||||
public <T> AbstractDatabaseHandler<T> getHandler(Class<T> dataObjectClass)
|
||||
{
|
||||
BentoBox plugin = BentoBox.getInstance();
|
||||
if (connector == null) {
|
||||
connector = new PostgreSQLDatabaseConnector(new DatabaseConnectionSettingsImpl(
|
||||
|
||||
if (this.connector == null)
|
||||
{
|
||||
this.connector = new PostgreSQLDatabaseConnector(new DatabaseConnectionSettingsImpl(
|
||||
plugin.getSettings().getDatabaseHost(),
|
||||
plugin.getSettings().getDatabasePort(),
|
||||
plugin.getSettings().getDatabaseName(),
|
||||
plugin.getSettings().getDatabaseUsername(),
|
||||
plugin.getSettings().getDatabasePassword(),
|
||||
plugin.getSettings().isUseSSL()
|
||||
));
|
||||
plugin.getSettings().isUseSSL(),
|
||||
plugin.getSettings().getMaximumPoolSize()));
|
||||
}
|
||||
return new PostgreSQLDatabaseHandler<>(plugin, dataObjectClass, connector);
|
||||
|
||||
return new PostgreSQLDatabaseHandler<>(plugin, dataObjectClass, this.connector);
|
||||
}
|
||||
}
|
||||
|
@ -1,34 +1,54 @@
|
||||
package world.bentobox.bentobox.database.sql.postgresql;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.postgresql.Driver;
|
||||
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
|
||||
import world.bentobox.bentobox.database.DatabaseConnectionSettingsImpl;
|
||||
import world.bentobox.bentobox.database.sql.SQLDatabaseConnector;
|
||||
|
||||
|
||||
/**
|
||||
* @since 1.6.0
|
||||
* @author Poslovitch
|
||||
*/
|
||||
public class PostgreSQLDatabaseConnector extends SQLDatabaseConnector {
|
||||
|
||||
/*
|
||||
* Ensure the driver is loaded as JDBC Driver might be invisible to Java's ServiceLoader.
|
||||
* Usually, this is not required as {@link DriverManager} detects JDBC drivers
|
||||
* via {@code META-INF/services/java.sql.Driver} entries. However there might be cases when the driver
|
||||
* is located at the application level classloader, thus it might be required to perform manual
|
||||
* registration of the driver.
|
||||
*/
|
||||
static {
|
||||
new Driver();
|
||||
}
|
||||
|
||||
public class PostgreSQLDatabaseConnector extends SQLDatabaseConnector
|
||||
{
|
||||
/**
|
||||
* Class for PostgreSQL database connections using the settings provided
|
||||
*
|
||||
* @param dbSettings - database settings
|
||||
*/
|
||||
PostgreSQLDatabaseConnector(@NonNull DatabaseConnectionSettingsImpl dbSettings) {
|
||||
super(dbSettings, "jdbc:postgresql://" + dbSettings.getHost() + ":" + dbSettings.getPort() + "/" + dbSettings.getDatabaseName()
|
||||
+ "?autoReconnect=true&useSSL=" + dbSettings.isUseSSL() + "&allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8");
|
||||
PostgreSQLDatabaseConnector(@NonNull DatabaseConnectionSettingsImpl dbSettings)
|
||||
{
|
||||
// connectionUrl is not used in PostgreSQL connection.
|
||||
super(dbSettings, "");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public HikariConfig createConfig()
|
||||
{
|
||||
HikariConfig config = new HikariConfig();
|
||||
config.setPoolName("BentoBox PostgreSQL Pool");
|
||||
|
||||
config.setDataSourceClassName("org.postgresql.ds.PGSimpleDataSource");
|
||||
config.addDataSourceProperty("user", this.dbSettings.getUsername());
|
||||
config.addDataSourceProperty("password", this.dbSettings.getPassword());
|
||||
config.addDataSourceProperty("databaseName", this.dbSettings.getDatabaseName());
|
||||
config.addDataSourceProperty("serverName", this.dbSettings.getHost());
|
||||
config.addDataSourceProperty("portNumber", this.dbSettings.getPort());
|
||||
|
||||
config.addDataSourceProperty("ssl", this.dbSettings.isUseSSL());
|
||||
|
||||
// Add extra properties.
|
||||
this.dbSettings.getExtraProperties().forEach(config::addDataSourceProperty);
|
||||
|
||||
config.setMaximumPoolSize(this.dbSettings.getMaxConnections());
|
||||
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package world.bentobox.bentobox.database.sql.postgresql;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
@ -19,70 +20,86 @@ import world.bentobox.bentobox.database.sql.SQLDatabaseHandler;
|
||||
* @since 1.11.0
|
||||
* @author tastybento
|
||||
*/
|
||||
public class PostgreSQLDatabaseHandler<T> extends SQLDatabaseHandler<T> {
|
||||
|
||||
public class PostgreSQLDatabaseHandler<T> extends SQLDatabaseHandler<T>
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param plugin BentoBox plugin
|
||||
* @param type The type of the objects that should be created and filled with
|
||||
* values from the database or inserted into the database
|
||||
* @param type The type of the objects that should be created and filled with values from the database or inserted
|
||||
* into the database
|
||||
* @param databaseConnector Contains the settings to create a connection to the database
|
||||
*/
|
||||
PostgreSQLDatabaseHandler(BentoBox plugin, Class<T> type, DatabaseConnector databaseConnector) {
|
||||
super(plugin, type, databaseConnector, new SQLConfiguration(plugin, type)
|
||||
PostgreSQLDatabaseHandler(BentoBox plugin, Class<T> type, DatabaseConnector databaseConnector)
|
||||
{
|
||||
super(plugin,
|
||||
type,
|
||||
databaseConnector,
|
||||
new SQLConfiguration(plugin, type).
|
||||
// Set uniqueid as the primary key (index). Postgresql convention is to use lower case field names
|
||||
// Postgresql also uses double quotes (") instead of (`) around tables names with dots.
|
||||
.schema("CREATE TABLE IF NOT EXISTS \"[tableName]\" (uniqueid VARCHAR PRIMARY KEY, json jsonb NOT NULL)")
|
||||
.loadObject("SELECT * FROM \"[tableName]\" WHERE uniqueid = ? LIMIT 1")
|
||||
.deleteObject("DELETE FROM \"[tableName]\" WHERE uniqueid = ?")
|
||||
schema("CREATE TABLE IF NOT EXISTS \"[tableName]\" (uniqueid VARCHAR PRIMARY KEY, json jsonb NOT NULL)").
|
||||
loadObject("SELECT * FROM \"[tableName]\" WHERE uniqueid = ? LIMIT 1").
|
||||
deleteObject("DELETE FROM \"[tableName]\" WHERE uniqueid = ?").
|
||||
// uniqueId has to be added into the row explicitly so we need to override the saveObject method
|
||||
// The json value is a string but has to be cast to json when done in Java
|
||||
.saveObject("INSERT INTO \"[tableName]\" (uniqueid, json) VALUES (?, cast(? as json)) "
|
||||
saveObject("INSERT INTO \"[tableName]\" (uniqueid, json) VALUES (?, cast(? as json)) "
|
||||
// This is the Postgresql version of UPSERT.
|
||||
+ "ON CONFLICT (uniqueid) "
|
||||
+ "DO UPDATE SET json = cast(? as json)")
|
||||
.loadObjects("SELECT json FROM \"[tableName]\"")
|
||||
+ "ON CONFLICT (uniqueid) DO UPDATE SET json = cast(? as json)").
|
||||
loadObjects("SELECT json FROM \"[tableName]\"").
|
||||
// Postgres exists function returns true or false natively
|
||||
.objectExists("SELECT EXISTS(SELECT * FROM \"[tableName]\" WHERE uniqueid = ?)")
|
||||
.renameTable("ALTER TABLE IF EXISTS \"[oldTableName]\" RENAME TO \"[tableName]\"")
|
||||
.setUseQuotes(false)
|
||||
objectExists("SELECT EXISTS(SELECT * FROM \"[tableName]\" WHERE uniqueid = ?)").
|
||||
renameTable("ALTER TABLE IF EXISTS \"[oldTableName]\" RENAME TO \"[tableName]\"").
|
||||
setUseQuotes(false)
|
||||
);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see world.bentobox.bentobox.database.sql.SQLDatabaseHandler#saveObject(java.lang.Object)
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public CompletableFuture<Boolean> saveObject(T instance) {
|
||||
public CompletableFuture<Boolean> saveObject(T instance)
|
||||
{
|
||||
CompletableFuture<Boolean> completableFuture = new CompletableFuture<>();
|
||||
|
||||
// Null check
|
||||
if (instance == null) {
|
||||
plugin.logError("PostgreSQL database request to store a null. ");
|
||||
if (instance == null)
|
||||
{
|
||||
this.plugin.logError("PostgreSQL database request to store a null. ");
|
||||
completableFuture.complete(false);
|
||||
return completableFuture;
|
||||
}
|
||||
if (!(instance instanceof DataObject)) {
|
||||
plugin.logError("This class is not a DataObject: " + instance.getClass().getName());
|
||||
|
||||
if (!(instance instanceof DataObject))
|
||||
{
|
||||
this.plugin.logError("This class is not a DataObject: " + instance.getClass().getName());
|
||||
completableFuture.complete(false);
|
||||
return completableFuture;
|
||||
}
|
||||
Gson gson = getGson();
|
||||
|
||||
Gson gson = this.getGson();
|
||||
String toStore = gson.toJson(instance);
|
||||
String uniqueId = ((DataObject)instance).getUniqueId();
|
||||
processQueue.add(() -> {
|
||||
try (PreparedStatement preparedStatement = getConnection().prepareStatement(getSqlConfig().getSaveObjectSQL())) {
|
||||
String uniqueId = ((DataObject) instance).getUniqueId();
|
||||
|
||||
this.processQueue.add(() ->
|
||||
{
|
||||
try (Connection connection = this.dataSource.getConnection();
|
||||
PreparedStatement preparedStatement = connection.prepareStatement(this.getSqlConfig().getSaveObjectSQL()))
|
||||
{
|
||||
preparedStatement.setString(1, uniqueId); // INSERT
|
||||
preparedStatement.setString(2, toStore); // INSERT
|
||||
preparedStatement.setString(3, toStore); // ON CONFLICT
|
||||
preparedStatement.execute();
|
||||
completableFuture.complete(true);
|
||||
} catch (SQLException e) {
|
||||
plugin.logError("Could not save object " + instance.getClass().getName() + " " + e.getMessage());
|
||||
}
|
||||
catch (SQLException e)
|
||||
{
|
||||
this.plugin.logError("Could not save object " + instance.getClass().getName() + " " + e.getMessage());
|
||||
completableFuture.complete(false);
|
||||
}
|
||||
});
|
||||
|
||||
return completableFuture;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,19 +1,51 @@
|
||||
package world.bentobox.bentobox.database.sql.sqlite;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
||||
import world.bentobox.bentobox.database.DatabaseSetup;
|
||||
|
||||
|
||||
/**
|
||||
* @since 1.6.0
|
||||
* @author Poslovitch
|
||||
*/
|
||||
public class SQLiteDatabase implements DatabaseSetup {
|
||||
public class SQLiteDatabase implements DatabaseSetup
|
||||
{
|
||||
/**
|
||||
* Database file name.
|
||||
*/
|
||||
private static final String DATABASE_FOLDER_NAME = "database";
|
||||
|
||||
private final SQLiteDatabaseConnector connector = new SQLiteDatabaseConnector(BentoBox.getInstance());
|
||||
/**
|
||||
* SQLite Database Connector.
|
||||
*/
|
||||
private SQLiteDatabaseConnector connector;
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public <T> AbstractDatabaseHandler<T> getHandler(Class<T> dataObjectClass) {
|
||||
return new SQLiteDatabaseHandler<>(BentoBox.getInstance(), dataObjectClass, connector);
|
||||
public <T> AbstractDatabaseHandler<T> getHandler(Class<T> dataObjectClass)
|
||||
{
|
||||
if (this.connector == null)
|
||||
{
|
||||
BentoBox plugin = BentoBox.getInstance();
|
||||
File dataFolder = new File(plugin.getDataFolder(), DATABASE_FOLDER_NAME);
|
||||
|
||||
if (!dataFolder.exists() && !dataFolder.mkdirs())
|
||||
{
|
||||
plugin.logError("Could not create database folder!");
|
||||
// Trigger plugin shutdown.
|
||||
plugin.onDisable();
|
||||
return null;
|
||||
}
|
||||
|
||||
this.connector = new SQLiteDatabaseConnector("jdbc:sqlite:" + dataFolder.getAbsolutePath() + File.separator + "database.db");
|
||||
}
|
||||
|
||||
return new SQLiteDatabaseHandler<>(BentoBox.getInstance(), dataObjectClass, this.connector);
|
||||
}
|
||||
}
|
||||
|
@ -1,48 +1,37 @@
|
||||
package world.bentobox.bentobox.database.sql.sqlite;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.database.sql.SQLDatabaseConnector;
|
||||
|
||||
/**
|
||||
* @since 1.6.0
|
||||
* @author Poslovitch
|
||||
*/
|
||||
public class SQLiteDatabaseConnector extends SQLDatabaseConnector {
|
||||
|
||||
private static final String DATABASE_FOLDER_NAME = "database";
|
||||
|
||||
SQLiteDatabaseConnector(@NonNull BentoBox plugin) {
|
||||
super(null, ""); // Not used by SQLite
|
||||
File dataFolder = new File(plugin.getDataFolder(), DATABASE_FOLDER_NAME);
|
||||
if (!dataFolder.exists() && !dataFolder.mkdirs()) {
|
||||
BentoBox.getInstance().logError("Could not create database folder!");
|
||||
return;
|
||||
}
|
||||
connectionUrl = "jdbc:sqlite:" + dataFolder.getAbsolutePath() + File.separator + "database.db";
|
||||
public class SQLiteDatabaseConnector extends SQLDatabaseConnector
|
||||
{
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
SQLiteDatabaseConnector(String connectionUrl)
|
||||
{
|
||||
super(null, connectionUrl);
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see world.bentobox.bentobox.database.sql.SQLDatabaseConnector#createConnection(java.lang.Class)
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Object createConnection(Class<?> type) {
|
||||
types.add(type);
|
||||
// Only make one connection at a time
|
||||
if (connection == null) {
|
||||
try {
|
||||
connection = DriverManager.getConnection(connectionUrl);
|
||||
} catch (SQLException e) {
|
||||
Bukkit.getLogger().severe("Could not connect to the database! " + e.getMessage());
|
||||
}
|
||||
}
|
||||
return connection;
|
||||
public HikariConfig createConfig()
|
||||
{
|
||||
HikariConfig config = new HikariConfig();
|
||||
config.setDataSourceClassName("org.sqlite.SQLiteDataSource");
|
||||
config.setPoolName("BentoBox SQLite Pool");
|
||||
config.addDataSourceProperty("encoding", "UTF-8");
|
||||
config.addDataSourceProperty("url", this.connectionUrl);
|
||||
config.setMaximumPoolSize(100);
|
||||
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package world.bentobox.bentobox.database.sql.sqlite;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
@ -17,23 +18,24 @@ import world.bentobox.bentobox.database.sql.SQLDatabaseHandler;
|
||||
* @since 1.6.0
|
||||
* @author Poslovitch, tastybento
|
||||
*/
|
||||
public class SQLiteDatabaseHandler<T> extends SQLDatabaseHandler<T> {
|
||||
|
||||
public class SQLiteDatabaseHandler<T> extends SQLDatabaseHandler<T>
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param plugin BentoBox plugin
|
||||
* @param type The type of the objects that should be created and filled with
|
||||
* values from the database or inserted into the database
|
||||
* @param type The type of the objects that should be created and filled with values from the database or inserted
|
||||
* into the database
|
||||
* @param databaseConnector Contains the settings to create a connection to the database
|
||||
*/
|
||||
protected SQLiteDatabaseHandler(BentoBox plugin, Class<T> type, DatabaseConnector databaseConnector) {
|
||||
super(plugin, type, databaseConnector, new SQLConfiguration(plugin, type)
|
||||
.schema("CREATE TABLE IF NOT EXISTS `[tableName]` (json JSON, uniqueId VARCHAR(255) NOT NULL PRIMARY KEY)")
|
||||
.saveObject("INSERT INTO `[tableName]` (json, uniqueId) VALUES (?, ?) ON CONFLICT(uniqueId) DO UPDATE SET json = ?")
|
||||
.objectExists("SELECT EXISTS (SELECT 1 FROM `[tableName]` WHERE `uniqueId` = ?)")
|
||||
.renameTable("ALTER TABLE `[oldTableName]` RENAME TO `[tableName]`")
|
||||
.setUseQuotes(false)
|
||||
protected SQLiteDatabaseHandler(BentoBox plugin, Class<T> type, DatabaseConnector databaseConnector)
|
||||
{
|
||||
super(plugin, type, databaseConnector, new SQLConfiguration(plugin, type).
|
||||
schema("CREATE TABLE IF NOT EXISTS `[tableName]` (json JSON, uniqueId VARCHAR(255) NOT NULL PRIMARY KEY)").
|
||||
saveObject("INSERT INTO `[tableName]` (json, uniqueId) VALUES (?, ?) ON CONFLICT(uniqueId) DO UPDATE SET json = ?").
|
||||
objectExists("SELECT EXISTS (SELECT 1 FROM `[tableName]` WHERE `uniqueId` = ?)").
|
||||
renameTable("ALTER TABLE `[oldTableName]` RENAME TO `[tableName]`").
|
||||
setUseQuotes(false)
|
||||
);
|
||||
}
|
||||
|
||||
@ -42,70 +44,115 @@ public class SQLiteDatabaseHandler<T> extends SQLDatabaseHandler<T> {
|
||||
* Creates the table in the database if it doesn't exist already
|
||||
*/
|
||||
@Override
|
||||
protected void createSchema() {
|
||||
if (getSqlConfig().renameRequired()) {
|
||||
protected void createSchema()
|
||||
{
|
||||
if (this.getSqlConfig().renameRequired())
|
||||
{
|
||||
// SQLite does not have a rename if exists command so we have to manually check if the old table exists
|
||||
String sql = "SELECT EXISTS (SELECT 1 FROM sqlite_master WHERE type='table' AND name='" + getSqlConfig().getOldTableName() + "' COLLATE NOCASE)";
|
||||
try (PreparedStatement pstmt = getConnection().prepareStatement(sql)) {
|
||||
rename(pstmt);
|
||||
} catch (SQLException e) {
|
||||
plugin.logError("Could not check if " + getSqlConfig().getOldTableName() + " exists for data object " + dataObject.getCanonicalName() + " " + e.getMessage());
|
||||
String sql = "SELECT EXISTS (SELECT 1 FROM sqlite_master WHERE type='table' AND name='" +
|
||||
this.getSqlConfig().getOldTableName() + "' COLLATE NOCASE)";
|
||||
|
||||
try (Connection connection = this.dataSource.getConnection();
|
||||
PreparedStatement preparedStatement = connection.prepareStatement(sql))
|
||||
{
|
||||
this.rename(preparedStatement);
|
||||
}
|
||||
catch (SQLException e)
|
||||
{
|
||||
this.plugin.logError("Could not check if " + this.getSqlConfig().getOldTableName() + " exists for data object " +
|
||||
this.dataObject.getCanonicalName() + " " + e.getMessage());
|
||||
}
|
||||
}
|
||||
// Prepare and execute the database statements
|
||||
try (PreparedStatement pstmt = getConnection().prepareStatement(getSqlConfig().getSchemaSQL())) {
|
||||
pstmt.execute();
|
||||
} catch (SQLException e) {
|
||||
plugin.logError("Problem trying to create schema for data object " + dataObject.getCanonicalName() + " " + e.getMessage());
|
||||
try (Connection connection = this.dataSource.getConnection();
|
||||
PreparedStatement preparedStatement = connection.prepareStatement(this.getSqlConfig().getSchemaSQL()))
|
||||
{
|
||||
preparedStatement.execute();
|
||||
}
|
||||
catch (SQLException e)
|
||||
{
|
||||
this.plugin.logError("Problem trying to create schema for data object " + dataObject.getCanonicalName() + " " +
|
||||
e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void rename(PreparedStatement pstmt) {
|
||||
try (ResultSet resultSet = pstmt.executeQuery()) {
|
||||
if (resultSet.next() && resultSet.getBoolean(1)) {
|
||||
|
||||
private void rename(PreparedStatement pstmt)
|
||||
{
|
||||
try (ResultSet resultSet = pstmt.executeQuery())
|
||||
{
|
||||
if (resultSet.next() && resultSet.getBoolean(1))
|
||||
{
|
||||
// Transition from the old table name
|
||||
String sql = getSqlConfig().getRenameTableSQL().replace("[oldTableName]", getSqlConfig().getOldTableName().replace("[tableName]", getSqlConfig().getTableName()));
|
||||
try (PreparedStatement pstmt2 = getConnection().prepareStatement(sql)) {
|
||||
pstmt2.execute();
|
||||
} catch (SQLException e) {
|
||||
plugin.logError("Could not rename " + getSqlConfig().getOldTableName() + " for data object " + dataObject.getCanonicalName() + " " + e.getMessage());
|
||||
String sql = this.getSqlConfig().getRenameTableSQL().replace("[oldTableName]",
|
||||
this.getSqlConfig().getOldTableName().replace("[tableName]", this.getSqlConfig().getTableName()));
|
||||
|
||||
executeStatement(sql);
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
plugin.logError("Could not check if " + getSqlConfig().getOldTableName() + " exists for data object " + dataObject.getCanonicalName() + " " + ex.getMessage());
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.plugin.logError("Could not check if " + getSqlConfig().getOldTableName() + " exists for data object " +
|
||||
this.dataObject.getCanonicalName() + " " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void executeStatement(String sql) {
|
||||
try (Connection connection = this.dataSource.getConnection();
|
||||
PreparedStatement preparedStatement = connection.prepareStatement(sql))
|
||||
{
|
||||
preparedStatement.execute();
|
||||
}
|
||||
catch (SQLException e)
|
||||
{
|
||||
this.plugin.logError("Could not rename " + getSqlConfig().getOldTableName() + " for data object " +
|
||||
this.dataObject.getCanonicalName() + " " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Boolean> saveObject(T instance) {
|
||||
public CompletableFuture<Boolean> saveObject(T instance)
|
||||
{
|
||||
CompletableFuture<Boolean> completableFuture = new CompletableFuture<>();
|
||||
|
||||
// Null check
|
||||
if (instance == null) {
|
||||
plugin.logError("SQLite database request to store a null. ");
|
||||
if (instance == null)
|
||||
{
|
||||
this.plugin.logError("SQLite database request to store a null. ");
|
||||
completableFuture.complete(false);
|
||||
return completableFuture;
|
||||
}
|
||||
if (!(instance instanceof DataObject)) {
|
||||
plugin.logError("This class is not a DataObject: " + instance.getClass().getName());
|
||||
|
||||
if (!(instance instanceof DataObject))
|
||||
{
|
||||
this.plugin.logError("This class is not a DataObject: " + instance.getClass().getName());
|
||||
completableFuture.complete(false);
|
||||
return completableFuture;
|
||||
}
|
||||
Gson gson = getGson();
|
||||
|
||||
Gson gson = this.getGson();
|
||||
String toStore = gson.toJson(instance);
|
||||
processQueue.add(() -> {
|
||||
try (PreparedStatement preparedStatement = getConnection().prepareStatement(getSqlConfig().getSaveObjectSQL())) {
|
||||
|
||||
this.processQueue.add(() ->
|
||||
{
|
||||
try (Connection connection = this.dataSource.getConnection();
|
||||
PreparedStatement preparedStatement = connection.prepareStatement(this.getSqlConfig().getSaveObjectSQL()))
|
||||
{
|
||||
preparedStatement.setString(1, toStore);
|
||||
preparedStatement.setString(2, ((DataObject)instance).getUniqueId());
|
||||
preparedStatement.setString(2, ((DataObject) instance).getUniqueId());
|
||||
preparedStatement.setString(3, toStore);
|
||||
preparedStatement.execute();
|
||||
completableFuture.complete(true);
|
||||
} catch (SQLException e) {
|
||||
plugin.logError("Could not save object " + instance.getClass().getName() + " " + e.getMessage());
|
||||
}
|
||||
catch (SQLException e)
|
||||
{
|
||||
this.plugin.logError("Could not save object " + instance.getClass().getName() + " " + ((DataObject) instance).getUniqueId() + " " + e.getMessage());
|
||||
completableFuture.complete(false);
|
||||
}
|
||||
});
|
||||
|
||||
return completableFuture;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -25,8 +25,6 @@ import org.bukkit.configuration.InvalidConfigurationException;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.database.DatabaseConnector;
|
||||
|
||||
@ -100,11 +98,14 @@ public class YamlDatabaseConnector implements DatabaseConnector {
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(yamlFile), StandardCharsets.UTF_8))){
|
||||
File temp = File.createTempFile("file", ".tmp", yamlFile.getParentFile());
|
||||
writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(temp), StandardCharsets.UTF_8));
|
||||
for (String line; (line = reader.readLine()) != null;) {
|
||||
String line = reader.readLine();
|
||||
while (line != null) {
|
||||
line = line.replace("!!java.util.UUID", "");
|
||||
writer.println(line);
|
||||
line = reader.readLine();
|
||||
}
|
||||
if (yamlFile.delete() && !temp.renameTo(yamlFile)) {
|
||||
Files.delete(yamlFile.toPath());
|
||||
if (!temp.renameTo(yamlFile)) {
|
||||
plugin.logError("Could not rename fixed Island object. Are the writing permissions correctly setup?");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@ -123,7 +124,7 @@ public class YamlDatabaseConnector implements DatabaseConnector {
|
||||
if (!tableFolder.exists()) {
|
||||
tableFolder.mkdirs();
|
||||
}
|
||||
try (Writer writer = new OutputStreamWriter(new FileOutputStream(file), Charsets.UTF_8)) {
|
||||
try (Writer writer = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)) {
|
||||
writer.write(data);
|
||||
} catch (IOException e) {
|
||||
plugin.logError("Could not save yml file: " + tableName + " " + fileName + " " + e.getMessage());
|
||||
@ -151,9 +152,8 @@ public class YamlDatabaseConnector implements DatabaseConnector {
|
||||
for (Entry<String, String> e : commentMap.entrySet()) {
|
||||
if (nextLine.contains(e.getKey())) {
|
||||
// We want the comment to start at the same level as the entry
|
||||
String commentLine = " ".repeat(Math.max(0, nextLine.indexOf(e.getKey()))) +
|
||||
nextLine = " ".repeat(Math.max(0, nextLine.indexOf(e.getKey()))) +
|
||||
e.getValue();
|
||||
nextLine = commentLine;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -316,9 +316,9 @@ public class YamlDatabaseHandler<T> extends AbstractDatabaseHandler<T> {
|
||||
// There could be more than one argument, so step through them
|
||||
for (Type genericParameterType : genericParameterTypes) {
|
||||
// If the argument is a parameter, then do something - this should always be true if the parameter is a collection
|
||||
if(genericParameterType instanceof ParameterizedType ) {
|
||||
if(genericParameterType instanceof ParameterizedType pt) {
|
||||
// Get the actual type arguments of the parameter
|
||||
Type[] parameters = ((ParameterizedType)genericParameterType).getActualTypeArguments();
|
||||
Type[] parameters = pt.getActualTypeArguments();
|
||||
result.addAll(Arrays.asList(parameters));
|
||||
}
|
||||
}
|
||||
@ -338,13 +338,11 @@ public class YamlDatabaseHandler<T> extends AbstractDatabaseHandler<T> {
|
||||
// Null check
|
||||
if (instance == null) {
|
||||
plugin.logError("YAML database request to store a null.");
|
||||
completableFuture.complete(false);
|
||||
return completableFuture;
|
||||
return CompletableFuture.completedFuture(false);
|
||||
}
|
||||
if (!(instance instanceof DataObject)) {
|
||||
plugin.logError("This class is not a DataObject: " + instance.getClass().getName());
|
||||
completableFuture.complete(false);
|
||||
return completableFuture;
|
||||
return CompletableFuture.completedFuture(false);
|
||||
}
|
||||
// This is the Yaml Configuration that will be used and saved at the end
|
||||
YamlConfiguration config = new YamlConfiguration();
|
||||
@ -396,9 +394,7 @@ public class YamlDatabaseHandler<T> extends AbstractDatabaseHandler<T> {
|
||||
handleConfigEntryComments(configEntry, config, yamlComments, parent);
|
||||
}
|
||||
|
||||
if (checkAdapter(field, config, storageLocation, value)) {
|
||||
continue;
|
||||
}
|
||||
if (!checkAdapter(field, config, storageLocation, value)) {
|
||||
// Set the filename if it has not be set already
|
||||
if (filename.isEmpty() && method.getName().equals("getUniqueId")) {
|
||||
// Save the name for when the file is saved
|
||||
@ -414,6 +410,7 @@ public class YamlDatabaseHandler<T> extends AbstractDatabaseHandler<T> {
|
||||
config.set(storageLocation, serialize(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
// If the filename has not been set by now then we have a problem
|
||||
if (filename.isEmpty()) {
|
||||
throw new IllegalArgumentException("No uniqueId in class");
|
||||
@ -469,6 +466,16 @@ public class YamlDatabaseHandler<T> extends AbstractDatabaseHandler<T> {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an adapter is to be used. If so, it is used and true returned, if not, fase is returned
|
||||
* @param field Field
|
||||
* @param config Yaml Config
|
||||
* @param storageLocation Storage location
|
||||
* @param value Value
|
||||
* @return true if adapater used
|
||||
* @throws IllegalAccessException exception
|
||||
* @throws InvocationTargetException exception
|
||||
*/
|
||||
private boolean checkAdapter(Field field, YamlConfiguration config, String storageLocation, Object value) throws IllegalAccessException, InvocationTargetException {
|
||||
Adapter adapterNotation = field.getAnnotation(Adapter.class);
|
||||
if (adapterNotation != null && AdapterInterface.class.isAssignableFrom(adapterNotation.value())) {
|
||||
|
@ -14,7 +14,7 @@ import world.bentobox.bentobox.api.hooks.Hook;
|
||||
*
|
||||
* @author Poslovitch
|
||||
*/
|
||||
public class MultiverseCoreHook extends Hook {
|
||||
public class MultiverseCoreHook extends Hook implements WorldManagementHook {
|
||||
|
||||
private static final String MULTIVERSE_SET_GENERATOR = "mv modify set generator ";
|
||||
private static final String MULTIVERSE_IMPORT = "mv import ";
|
||||
@ -28,6 +28,7 @@ public class MultiverseCoreHook extends Hook {
|
||||
* @param world - world to register
|
||||
* @param islandWorld - if true, then this is an island world
|
||||
*/
|
||||
@Override
|
||||
public void registerWorld(World world, boolean islandWorld) {
|
||||
if (islandWorld) {
|
||||
// Only register generator if one is defined in the addon (is not null)
|
||||
|
@ -0,0 +1,73 @@
|
||||
package world.bentobox.bentobox.hooks;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
|
||||
import com.bergerkiller.bukkit.mw.WorldConfigStore;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.api.hooks.Hook;
|
||||
|
||||
/**
|
||||
* Provides implementation and interfacing to interact with MyWorlds.
|
||||
*
|
||||
* @author bergerkiller (Irmo van den Berge)
|
||||
*/
|
||||
public class MyWorldsHook extends Hook implements WorldManagementHook {
|
||||
|
||||
public MyWorldsHook() {
|
||||
super("My_Worlds", Material.FILLED_MAP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the world with MyWorlds
|
||||
*
|
||||
* @param world - world to register
|
||||
* @param islandWorld - if true, then this is an island world
|
||||
*/
|
||||
@Override
|
||||
public void registerWorld(World world, boolean islandWorld) {
|
||||
if (islandWorld) {
|
||||
// Only register generator if one is defined in the addon (is not null)
|
||||
boolean hasGenerator = BentoBox.getInstance().getIWM().getAddon(world).map(gm -> gm.getDefaultWorldGenerator(world.getName(), "") != null).orElse(false);
|
||||
setUseBentoboxGenerator(world, hasGenerator);
|
||||
} else {
|
||||
// Set the generator to null - this will remove any previous registration
|
||||
setUseBentoboxGenerator(world, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void setUseBentoboxGenerator(World world, boolean hasGenerator) {
|
||||
String name = hasGenerator ? BentoBox.getInstance().getName() : null;
|
||||
|
||||
try {
|
||||
WorldConfigStore.get(world).setChunkGeneratorName(name);
|
||||
|
||||
// Alternative Reflection way to do it, if a MyWorlds dependency isn't available at
|
||||
// compile time.
|
||||
/*
|
||||
// WorldConfigStore -> public static WorldConfig get(World world);
|
||||
Object worldConfig = Class.forName("com.bergerkiller.bukkit.mw.WorldConfigStore")
|
||||
.getMethod("get", World.class)
|
||||
.invoke(null, world);
|
||||
|
||||
// WorldConfig -> public void setChunkGeneratorName(String name);
|
||||
Class.forName("com.bergerkiller.bukkit.mw.WorldConfig")
|
||||
.getMethod("setChunkGeneratorName", String.class)
|
||||
.invoke(worldConfig, name);
|
||||
*/
|
||||
} catch (Exception t) {
|
||||
BentoBox.getInstance().logError("Failed to register world " + world.getName() + " with MyWorlds " + t.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hook() {
|
||||
return true; // The hook process shouldn't fail
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFailureCause() {
|
||||
return null; // The hook process shouldn't fail
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package world.bentobox.bentobox.hooks;
|
||||
|
||||
import org.bukkit.World;
|
||||
|
||||
/**
|
||||
* Hook for a type of Multi-World management plugin that must be made
|
||||
* aware of the correct configuration of a BentoBox World.
|
||||
*
|
||||
* @author bergerkiller (Irmo van den Berge)
|
||||
*/
|
||||
public interface WorldManagementHook {
|
||||
|
||||
/**
|
||||
* Register the world with the World Management hook
|
||||
*
|
||||
* @param world - world to register
|
||||
* @param islandWorld - if true, then this is an island world
|
||||
*/
|
||||
void registerWorld(World world, boolean islandWorld);
|
||||
}
|
@ -9,6 +9,7 @@ import java.util.regex.Pattern;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import me.clip.placeholderapi.PlaceholderAPI;
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
@ -69,12 +70,12 @@ public class PlaceholderAPIHook extends PlaceholderHook {
|
||||
@Override
|
||||
public void registerPlaceholder(@NonNull Addon addon, @NonNull String placeholder, @NonNull PlaceholderReplacer replacer) {
|
||||
// Check if the addon expansion does not exist
|
||||
if (!addonsExpansions.containsKey(addon)) {
|
||||
addonsExpansions.computeIfAbsent(addon, k -> {
|
||||
AddonPlaceholderExpansion addonPlaceholderExpansion = new AddonPlaceholderExpansion(addon);
|
||||
addonPlaceholderExpansion.register();
|
||||
addonsExpansions.put(addon, addonPlaceholderExpansion);
|
||||
this.addonPlaceholders.computeIfAbsent(addon, k -> new HashSet<>()).add(placeholder);
|
||||
}
|
||||
this.addonPlaceholders.computeIfAbsent(addon, kk -> new HashSet<>()).add(placeholder);
|
||||
return addonPlaceholderExpansion;
|
||||
});
|
||||
addonsExpansions.get(addon).registerPlaceholder(placeholder, replacer);
|
||||
}
|
||||
|
||||
@ -109,7 +110,10 @@ public class PlaceholderAPIHook extends PlaceholderHook {
|
||||
*/
|
||||
@Override
|
||||
@NonNull
|
||||
public String replacePlaceholders(@NonNull Player player, @NonNull String string) {
|
||||
public String replacePlaceholders(@Nullable Player player, @NonNull String string) {
|
||||
if (player == null) {
|
||||
return PlaceholderAPI.setPlaceholders(player, removeGMPlaceholder(string));
|
||||
}
|
||||
// Transform [gamemode] in string to the game mode description name, or remove it for the default replacement
|
||||
String newString = BentoBox.getInstance().getIWM().getAddon(player.getWorld()).map(gm ->
|
||||
string.replace(TextVariables.GAMEMODE, gm.getDescription().getName().toLowerCase())
|
||||
|
@ -16,6 +16,7 @@ import org.bukkit.event.player.PlayerChangedWorldEvent;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.api.addons.GameModeAddon;
|
||||
@ -49,7 +50,8 @@ public class JoinLeaveListener implements Listener {
|
||||
User.removePlayer(event.getPlayer());
|
||||
|
||||
User user = User.getInstance(event.getPlayer());
|
||||
if (user == null || user.getUniqueId() == null) {
|
||||
if (!user.isPlayer() || user.getUniqueId() == null) {
|
||||
// This should never be the case, but it might be caused by some fake player plugins
|
||||
return;
|
||||
}
|
||||
UUID playerUUID = event.getPlayer().getUniqueId();
|
||||
@ -142,7 +144,7 @@ public class JoinLeaveListener implements Listener {
|
||||
}
|
||||
|
||||
/**
|
||||
* This event will clean players inventor
|
||||
* This event will clean players inventory
|
||||
* @param event SwitchWorld event.
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||
@ -161,8 +163,8 @@ public class JoinLeaveListener implements Listener {
|
||||
* @param world World where cleaning must occur.
|
||||
* @param user Targeted user.
|
||||
*/
|
||||
private void clearPlayersInventory(World world, @NonNull User user) {
|
||||
if (user.getUniqueId() == null) return;
|
||||
private void clearPlayersInventory(@Nullable World world, @NonNull User user) {
|
||||
if (user.getUniqueId() == null || world == null) return;
|
||||
// Clear inventory if required
|
||||
Players playerData = players.getPlayer(user.getUniqueId());
|
||||
|
||||
|
@ -18,7 +18,9 @@ import world.bentobox.bentobox.database.objects.Island;
|
||||
* Abstracts PlayerPortalEvent and EntityPortalEvent
|
||||
* @author tastybento
|
||||
* @deprecated replaced not used in new listeners.
|
||||
* @since 1.12.1
|
||||
*/
|
||||
@Deprecated(since="1.21.0", forRemoval=true)
|
||||
public class PlayerEntityPortalEvent {
|
||||
|
||||
private final EntityPortalEvent epe;
|
||||
|
@ -43,8 +43,9 @@ import world.bentobox.bentobox.util.teleport.SafeSpotTeleport;
|
||||
* @deprecated replaced by better listeners.
|
||||
* @see world.bentobox.bentobox.listeners.teleports.PlayerTeleportListener
|
||||
* @see world.bentobox.bentobox.listeners.teleports.EntityTeleportListener
|
||||
* @since 1.12.1
|
||||
*/
|
||||
@Deprecated
|
||||
@Deprecated(since="1.21.0", forRemoval=true)
|
||||
public class PortalTeleportationListener implements Listener {
|
||||
|
||||
private final BentoBox plugin;
|
||||
@ -148,7 +149,7 @@ public class PortalTeleportationListener implements Listener {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -268,9 +269,8 @@ public class PortalTeleportationListener implements Listener {
|
||||
return null;
|
||||
}
|
||||
|
||||
Location toLocation = e.getIsland().map(island -> island.getSpawnPoint(env)).
|
||||
orElse(e.getFrom().toVector().toLocation(toWorld));
|
||||
|
||||
Location toLocation = Objects.requireNonNullElse(e.getIsland().map(island -> island.getSpawnPoint(env)).
|
||||
orElse(e.getFrom().toVector().toLocation(toWorld)), e.getFrom().toVector().toLocation(toWorld));
|
||||
// Limit Y to the min/max world height.
|
||||
toLocation.setY(Math.max(Math.min(toLocation.getY(), toWorld.getMaxHeight()), toWorld.getMinHeight()));
|
||||
|
||||
@ -438,9 +438,9 @@ public class PortalTeleportationListener implements Listener {
|
||||
}
|
||||
}
|
||||
// From standard nether or end
|
||||
else if (e.getEntity() instanceof Player){
|
||||
else if (e.getEntity() instanceof Player player){
|
||||
e.setCancelled(true);
|
||||
plugin.getIslands().homeTeleportAsync(overWorld, (Player)e.getEntity());
|
||||
plugin.getIslands().homeTeleportAsync(overWorld, player);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
@ -39,7 +38,7 @@ public class GeoMobLimitTab implements Tab, ClickHandler {
|
||||
.filter(EntityType::isAlive)
|
||||
.filter(t -> !(t.equals(EntityType.PLAYER) || t.equals(EntityType.GIANT) || t.equals(EntityType.ARMOR_STAND)))
|
||||
.sorted(Comparator.comparing(EntityType::name))
|
||||
.collect(Collectors.toList()));
|
||||
.toList());
|
||||
|
||||
public enum EntityLimitTabType {
|
||||
GEO_LIMIT,
|
||||
@ -110,7 +109,7 @@ public class GeoMobLimitTab implements Tab, ClickHandler {
|
||||
@Override
|
||||
public List<@Nullable PanelItem> getPanelItems() {
|
||||
// Make panel items
|
||||
return LIVING_ENTITY_TYPES.stream().map(c -> getPanelItem(c, user)).collect(Collectors.toList());
|
||||
return LIVING_ENTITY_TYPES.stream().map(c -> getPanelItem(c, user)).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,6 +1,5 @@
|
||||
package world.bentobox.bentobox.listeners.flags.protection;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@ -36,18 +35,19 @@ public class BlockInteractionListener extends FlagListener
|
||||
* These cover materials in another server version. This avoids run time errors due to unknown enum values, at the
|
||||
* expense of a string comparison
|
||||
*/
|
||||
private final static Map<String, String> stringFlags;
|
||||
private static final Map<String, String> stringFlags;
|
||||
private static final String CHEST = "CHEST";
|
||||
|
||||
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");
|
||||
"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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -6,7 +6,6 @@ import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.entity.AbstractArrow;
|
||||
import org.bukkit.entity.ArmorStand;
|
||||
import org.bukkit.entity.EnderCrystal;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.ItemFrame;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.Projectile;
|
||||
@ -123,14 +122,12 @@ public class BreakBlocksListener extends FlagListener {
|
||||
if (e.getDamager() instanceof Player p) {
|
||||
// Check the break blocks flag
|
||||
notAllowed(e, p, e.getEntity().getLocation());
|
||||
} else if (e.getDamager() instanceof Projectile p) {
|
||||
// Find out who fired the arrow
|
||||
if (p.getShooter() instanceof Player && notAllowed(e, (Player)p.getShooter(), e.getEntity().getLocation())) {
|
||||
} else if (e.getDamager() instanceof Projectile p && // Find out who fired the arrow
|
||||
p.getShooter() instanceof Player player && notAllowed(e, player, e.getEntity().getLocation())) {
|
||||
e.getEntity().setFireTicks(0);
|
||||
p.setFireTicks(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean notAllowed(EntityDamageByEntityEvent e, Player player, Location location) {
|
||||
if (!checkIsland(e, player, location, Flags.BREAK_BLOCKS)) return true;
|
||||
|
@ -2,7 +2,7 @@ package world.bentobox.bentobox.listeners.flags.protection;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@ -36,7 +36,7 @@ public class BreedingListener extends FlagListener {
|
||||
*/
|
||||
private static final Map<EntityType, List<Material>> BREEDING_ITEMS;
|
||||
static {
|
||||
Map<EntityType, List<Material>> bi = new HashMap<>();
|
||||
Map<EntityType, List<Material>> bi = new EnumMap<>(EntityType.class);
|
||||
|
||||
bi.put(EntityType.HORSE, Arrays.asList(Material.GOLDEN_APPLE, Material.GOLDEN_CARROT));
|
||||
bi.put(EntityType.DONKEY, Arrays.asList(Material.GOLDEN_APPLE, Material.GOLDEN_CARROT));
|
||||
|
@ -18,12 +18,11 @@ public class ElytraListener extends FlagListener {
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||
public void onGlide(EntityToggleGlideEvent e) {
|
||||
if (e.getEntity() instanceof Player player) {
|
||||
if (!checkIsland(e, player, player.getLocation(), Flags.ELYTRA)) {
|
||||
if (e.getEntity() instanceof Player player
|
||||
&& !checkIsland(e, player, player.getLocation(), Flags.ELYTRA)) {
|
||||
player.setGliding(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||
public void onGliding(PlayerTeleportEvent e) {
|
||||
|
@ -2,8 +2,19 @@ package world.bentobox.bentobox.listeners.flags.protection;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.*;
|
||||
import org.bukkit.entity.Allay;
|
||||
import org.bukkit.entity.Animals;
|
||||
import org.bukkit.entity.ArmorStand;
|
||||
import org.bukkit.entity.Boat;
|
||||
import org.bukkit.entity.ChestBoat;
|
||||
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.HopperMinecart;
|
||||
import org.bukkit.entity.minecart.PoweredMinecart;
|
||||
import org.bukkit.entity.minecart.RideableMinecart;
|
||||
import org.bukkit.entity.minecart.StorageMinecart;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.player.PlayerInteractAtEntityEvent;
|
||||
@ -11,7 +22,6 @@ import org.bukkit.event.player.PlayerInteractEntityEvent;
|
||||
|
||||
import world.bentobox.bentobox.api.flags.FlagListener;
|
||||
import world.bentobox.bentobox.lists.Flags;
|
||||
import world.bentobox.bentobox.versions.ServerCompatibility;
|
||||
|
||||
|
||||
/**
|
||||
@ -47,11 +57,19 @@ public class EntityInteractListener extends FlagListener {
|
||||
// Minecart riding
|
||||
this.checkIsland(e, p, l, Flags.MINECART);
|
||||
}
|
||||
else if (!ServerCompatibility.getInstance().isVersion(
|
||||
ServerCompatibility.ServerVersion.V1_18,
|
||||
ServerCompatibility.ServerVersion.V1_18_1,
|
||||
ServerCompatibility.ServerVersion.V1_18_2) &&
|
||||
e.getPlayer().isSneaking() && e.getRightClicked() instanceof ChestBoat)
|
||||
else if (e.getRightClicked() instanceof StorageMinecart)
|
||||
{
|
||||
this.checkIsland(e, p, l, Flags.CHEST);
|
||||
}
|
||||
else if (e.getRightClicked() instanceof HopperMinecart)
|
||||
{
|
||||
this.checkIsland(e, p, l, Flags.HOPPER);
|
||||
}
|
||||
else if (e.getRightClicked() instanceof PoweredMinecart)
|
||||
{
|
||||
this.checkIsland(e, p, l, Flags.FURNACE);
|
||||
}
|
||||
else if (e.getPlayer().isSneaking() && e.getRightClicked() instanceof ChestBoat)
|
||||
{
|
||||
// Access to chest boat since 1.19
|
||||
this.checkIsland(e, p, l, Flags.CHEST);
|
||||
@ -73,11 +91,7 @@ public class EntityInteractListener extends FlagListener {
|
||||
this.checkIsland(e, p, l, Flags.NAME_TAG);
|
||||
}
|
||||
}
|
||||
else if (!ServerCompatibility.getInstance().isVersion(
|
||||
ServerCompatibility.ServerVersion.V1_18,
|
||||
ServerCompatibility.ServerVersion.V1_18_1,
|
||||
ServerCompatibility.ServerVersion.V1_18_2) &&
|
||||
e.getRightClicked() instanceof Allay)
|
||||
else if (e.getRightClicked() instanceof Allay)
|
||||
{
|
||||
// Allay item giving/taking
|
||||
this.checkIsland(e, p, l, Flags.ALLAY);
|
||||
|
@ -74,15 +74,13 @@ public class HurtingListener extends FlagListener {
|
||||
*/
|
||||
private void respond(EntityDamageByEntityEvent e, Entity damager, Flag flag) {
|
||||
// Get the attacker
|
||||
if (damager instanceof Player) {
|
||||
checkIsland(e, (Player)damager, damager.getLocation(), flag);
|
||||
} else if (damager instanceof Projectile p) {
|
||||
// Find out who fired the projectile
|
||||
if (p.getShooter() instanceof Player && !checkIsland(e, (Player)p.getShooter(), damager.getLocation(), flag)) {
|
||||
if (damager instanceof Player player) {
|
||||
checkIsland(e, player, player.getLocation(), flag);
|
||||
} else if (damager instanceof Projectile p && // Find out who fired the projectile
|
||||
p.getShooter() instanceof Player player && !checkIsland(e, player, player.getLocation(), flag)) {
|
||||
e.getEntity().setFireTicks(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle attacks with a fishing rod
|
||||
@ -166,9 +164,9 @@ public class HurtingListener extends FlagListener {
|
||||
public void onLingeringPotionSplash(final LingeringPotionSplashEvent e) {
|
||||
// Try to get the shooter
|
||||
Projectile projectile = e.getEntity();
|
||||
if (projectile.getShooter() instanceof Player) {
|
||||
if (projectile.getShooter() instanceof Player player) {
|
||||
// Store it and remove it when the effect is gone
|
||||
thrownPotions.put(e.getAreaEffectCloud().getEntityId(), (Player)projectile.getShooter());
|
||||
thrownPotions.put(e.getAreaEffectCloud().getEntityId(), player);
|
||||
getPlugin().getServer().getScheduler().runTaskLater(getPlugin(), () -> thrownPotions.remove(e.getAreaEffectCloud().getEntityId()), e.getAreaEffectCloud().getDuration());
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,6 @@ import org.bukkit.inventory.InventoryHolder;
|
||||
|
||||
import world.bentobox.bentobox.api.flags.FlagListener;
|
||||
import world.bentobox.bentobox.lists.Flags;
|
||||
import world.bentobox.bentobox.versions.ServerCompatibility;
|
||||
|
||||
|
||||
/**
|
||||
@ -54,10 +53,7 @@ public class InventoryListener extends FlagListener
|
||||
// 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)
|
||||
else if (inventoryHolder instanceof ChestBoat)
|
||||
{
|
||||
// Prevent opening chest inventories
|
||||
this.checkIsland(event, player, event.getInventory().getLocation(), Flags.CHEST);
|
||||
@ -132,8 +128,7 @@ public class InventoryListener extends FlagListener
|
||||
{
|
||||
this.checkIsland(e, player, e.getInventory().getLocation(), Flags.CHEST);
|
||||
}
|
||||
else if (!ServerCompatibility.getInstance().isVersion(ServerCompatibility.ServerVersion.V1_18, ServerCompatibility.ServerVersion.V1_18_1, ServerCompatibility.ServerVersion.V1_18_2) &&
|
||||
inventoryHolder instanceof ChestBoat)
|
||||
else if (inventoryHolder instanceof ChestBoat)
|
||||
{
|
||||
// TODO: 1.19 added chest boat. Remove compatibility check when 1.18 is dropped.
|
||||
this.checkIsland(e, player, e.getInventory().getLocation(), Flags.CHEST);
|
||||
|
@ -27,8 +27,8 @@ public class ItemDropPickUpListener extends FlagListener {
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||
public void onPickup(EntityPickupItemEvent e) {
|
||||
if (e.getEntity() instanceof Player) {
|
||||
checkIsland(e, (Player)e.getEntity(), e.getItem().getLocation(), Flags.ITEM_PICKUP);
|
||||
if (e.getEntity() instanceof Player player) {
|
||||
checkIsland(e, player, e.getItem().getLocation(), Flags.ITEM_PICKUP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ public class LockAndBanListener extends FlagListener {
|
||||
return;
|
||||
}
|
||||
// For each Player in the vehicle
|
||||
e.getVehicle().getPassengers().stream().filter(en -> en instanceof Player).map(en -> (Player)en).forEach(p -> {
|
||||
e.getVehicle().getPassengers().stream().filter(Player.class::isInstance).map(Player.class::cast).forEach(p -> {
|
||||
if (!checkAndNotify(p, e.getTo()).equals(CheckResult.OPEN)) {
|
||||
p.leaveVehicle();
|
||||
p.teleport(e.getFrom());
|
||||
@ -140,13 +140,11 @@ public class LockAndBanListener extends FlagListener {
|
||||
private CheckResult checkAndNotify(@NonNull Player player, Location loc)
|
||||
{
|
||||
CheckResult result = this.check(player, loc);
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case BANNED -> User.getInstance(player).notify("commands.island.ban.you-are-banned");
|
||||
case LOCKED -> User.getInstance(player).notify("protection.locked");
|
||||
if (result == CheckResult.BANNED) {
|
||||
User.getInstance(player).notify("commands.island.ban.you-are-banned");
|
||||
} else if (result == CheckResult.LOCKED) {
|
||||
User.getInstance(player).notify("protection.locked");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user