From 658937b489f1662bad6098077ed5923836dd80c4 Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Tue, 5 Sep 2023 23:14:15 +0800 Subject: [PATCH] 2.0-backup-5 --- .../api/data/DataStorageInterface.java | 7 ++- .../api/event/LavaFishingEvent.java | 3 +- .../api/manager/GameManager.java | 5 +- .../api/mechanic/game/GameCreator.java | 9 ++++ .../api/mechanic/game/GameExpansion.java | 12 +++++ .../customfishing/api/util/LogUtils.java | 1 - .../mechanic/block/BlockManagerImpl.java | 2 +- .../competition/CompetitionManagerImpl.java | 2 +- .../mechanic/effect/EffectManagerImpl.java | 2 +- .../mechanic/game/GameManagerImpl.java | 39 +++++++++++++- .../mechanic/item/ItemManagerImpl.java | 2 +- .../mechanic/loot/LootManagerImpl.java | 2 +- .../mechanic/mob/MobManagerImpl.java | 2 +- .../storage/StorageManagerImpl.java | 49 +++++++++++------ .../storage/method/AbstractStorage.java | 9 ++++ .../method/database/nosql/MongoDBImpl.java | 18 ++++++- .../method/database/nosql/RedisManager.java | 2 +- .../database/sql/AbstractSQLDatabase.java | 32 +++++++++-- .../method/database/sql/SQLiteImpl.java | 29 +++++++++- .../storage/method/file/JsonImpl.java | 2 +- .../storage/method/file/YAMLImpl.java | 2 +- .../customfishing/util/ClassUtils.java | 54 +++++++++++++++++++ plugin/src/main/resources/config.yml | 1 + 23 files changed, 247 insertions(+), 39 deletions(-) create mode 100644 api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameCreator.java create mode 100644 api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameExpansion.java create mode 100644 plugin/src/main/java/net/momirealms/customfishing/util/ClassUtils.java diff --git a/api/src/main/java/net/momirealms/customfishing/api/data/DataStorageInterface.java b/api/src/main/java/net/momirealms/customfishing/api/data/DataStorageInterface.java index 8332bfe8..b15a1598 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/data/DataStorageInterface.java +++ b/api/src/main/java/net/momirealms/customfishing/api/data/DataStorageInterface.java @@ -17,6 +17,9 @@ package net.momirealms.customfishing.api.data; +import net.momirealms.customfishing.api.data.user.OnlineUser; + +import java.util.Collection; import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -30,5 +33,7 @@ public interface DataStorageInterface { CompletableFuture> getPlayerData(UUID uuid, boolean force); - CompletableFuture setPlayData(UUID uuid, PlayerData playerData, boolean unlock); + CompletableFuture setPlayerData(UUID uuid, PlayerData playerData, boolean unlock); + + void setPlayersData(Collection users, boolean unlock); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/event/LavaFishingEvent.java b/api/src/main/java/net/momirealms/customfishing/api/event/LavaFishingEvent.java index 7730a4b4..f619b6ed 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/event/LavaFishingEvent.java +++ b/api/src/main/java/net/momirealms/customfishing/api/event/LavaFishingEvent.java @@ -68,6 +68,7 @@ public class LavaFishingEvent extends PlayerEvent implements Cancellable { public enum State { REEL_IN, - CAUGHT_FISH, BITE + CAUGHT_FISH, + BITE } } diff --git a/api/src/main/java/net/momirealms/customfishing/api/manager/GameManager.java b/api/src/main/java/net/momirealms/customfishing/api/manager/GameManager.java index 01af8176..98642b63 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/GameManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/manager/GameManager.java @@ -19,7 +19,7 @@ package net.momirealms.customfishing.api.manager; import net.momirealms.customfishing.api.mechanic.game.Game; import net.momirealms.customfishing.api.mechanic.game.GameConfig; -import org.bukkit.configuration.ConfigurationSection; +import net.momirealms.customfishing.api.mechanic.game.GameCreator; import org.jetbrains.annotations.Nullable; public interface GameManager { @@ -39,8 +39,5 @@ public interface GameManager { GameConfig getRandomGameConfig(); - public interface GameCreator { - Game setArgs(ConfigurationSection section); - } } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameCreator.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameCreator.java new file mode 100644 index 00000000..4378136c --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameCreator.java @@ -0,0 +1,9 @@ +package net.momirealms.customfishing.api.mechanic.game; + +import org.bukkit.configuration.ConfigurationSection; + +public interface GameCreator { + + Game setArgs(ConfigurationSection section); + +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameExpansion.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameExpansion.java new file mode 100644 index 00000000..4d7f0b31 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GameExpansion.java @@ -0,0 +1,12 @@ +package net.momirealms.customfishing.api.mechanic.game; + +public abstract class GameExpansion { + + public abstract String getVersion(); + + public abstract String getAuthor(); + + public abstract String getGameType(); + + public abstract GameCreator getGameCreator(); +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/util/LogUtils.java b/api/src/main/java/net/momirealms/customfishing/api/util/LogUtils.java index b4d569fa..450fa8ff 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/util/LogUtils.java +++ b/api/src/main/java/net/momirealms/customfishing/api/util/LogUtils.java @@ -18,7 +18,6 @@ package net.momirealms.customfishing.api.util; import net.momirealms.customfishing.api.CustomFishingPlugin; -import org.bukkit.Bukkit; import org.jetbrains.annotations.NotNull; import java.util.logging.Level; diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/block/BlockManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/block/BlockManagerImpl.java index af2d5908..37aecbec 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/block/BlockManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/block/BlockManagerImpl.java @@ -178,7 +178,7 @@ public class BlockManagerImpl implements BlockManager, Listener { for (File subFile : files) { if (subFile.isDirectory()) { fileDeque.push(subFile); - } else if (subFile.isFile()) { + } else if (subFile.isFile() && subFile.getName().endsWith(".yml")) { this.loadSingleFile(subFile); } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/CompetitionManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/CompetitionManagerImpl.java index e3593ec5..a4c6b4a0 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/CompetitionManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/competition/CompetitionManagerImpl.java @@ -105,7 +105,7 @@ public class CompetitionManagerImpl implements CompetitionManager { for (File subFile : files) { if (subFile.isDirectory()) { fileDeque.push(subFile); - } else if (subFile.isFile()) { + } else if (subFile.isFile() && subFile.getName().endsWith(".yml")) { this.loadSingleFileCompetition(subFile); } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/effect/EffectManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/effect/EffectManagerImpl.java index 43e94864..f02e3f01 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/effect/EffectManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/effect/EffectManagerImpl.java @@ -77,7 +77,7 @@ public class EffectManagerImpl implements EffectManager { for (File subFile : files) { if (subFile.isDirectory()) { fileDeque.push(subFile); - } else if (subFile.isFile()) { + } else if (subFile.isFile() && subFile.getName().endsWith(".yml")) { this.loadSingleFile(subFile, StringUtils.chop(type)); } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/game/GameManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/game/GameManagerImpl.java index 323a5647..0f5d9136 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/game/GameManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/game/GameManagerImpl.java @@ -22,13 +22,17 @@ import net.momirealms.customfishing.api.CustomFishingPlugin; import net.momirealms.customfishing.api.manager.GameManager; import net.momirealms.customfishing.api.mechanic.game.*; import net.momirealms.customfishing.api.util.FontUtils; +import net.momirealms.customfishing.api.util.LogUtils; import net.momirealms.customfishing.api.util.OffsetUtils; +import net.momirealms.customfishing.util.ClassUtils; import net.momirealms.customfishing.util.ConfigUtils; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.YamlConfiguration; import org.jetbrains.annotations.Nullable; import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; import java.util.*; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; @@ -39,6 +43,7 @@ public class GameManagerImpl implements GameManager { private final HashMap gameCreatorMap; private final HashMap gameMap; private final HashMap gameConfigMap; + private final String EXPANSION_FOLDER = "expansions/minigames"; public GameManagerImpl(CustomFishingPlugin plugin) { this.plugin = plugin; @@ -55,6 +60,7 @@ public class GameManagerImpl implements GameManager { } public void load() { + this.loadExpansions(); this.loadGamesFromPluginFolder(); this.loadGameConfigs(); } @@ -147,7 +153,7 @@ public class GameManagerImpl implements GameManager { for (File subFile : files) { if (subFile.isDirectory()) { fileDeque.push(subFile); - } else if (subFile.isFile()) { + } else if (subFile.isFile() && subFile.getName().endsWith(".yml")) { loadSingleFile(subFile); } } @@ -464,4 +470,35 @@ public class GameManagerImpl implements GameManager { }; })); } + + private void loadExpansions() { + File expansionFolder = new File(plugin.getDataFolder(), EXPANSION_FOLDER); + if (!expansionFolder.exists()) + expansionFolder.mkdirs(); + + List> classes = new ArrayList<>(); + File[] expansionJars = expansionFolder.listFiles(); + if (expansionJars == null) return; + for (File expansionJar : expansionJars) { + if (expansionJar.getName().endsWith(".jar")) { + try { + Class expansionClass = ClassUtils.findClass(expansionJar, GameExpansion.class); + classes.add(expansionClass); + } catch (IOException | ClassNotFoundException e) { + LogUtils.warn("Failed to load expansion: " + expansionJar.getName(), e); + } + } + } + + try { + for (Class expansionClass : classes) { + GameExpansion expansion = expansionClass.getDeclaredConstructor().newInstance(); + unregisterGameType(expansion.getGameType()); + registerGameType(expansion.getGameType(), expansion.getGameCreator()); + LogUtils.info("Loaded expansion: " + expansion.getGameType() + " made by " + expansion.getAuthor() + "[" + expansion.getVersion() + "]"); + } + } catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) { + LogUtils.warn("Error occurred when creating expansion instance.", e); + } + } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/item/ItemManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/item/ItemManagerImpl.java index 2fc30776..caccd963 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/item/ItemManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/item/ItemManagerImpl.java @@ -110,7 +110,7 @@ public class ItemManagerImpl implements ItemManager { for (File subFile : files) { if (subFile.isDirectory()) { fileDeque.push(subFile); - } else if (subFile.isFile()) { + } else if (subFile.isFile() && subFile.getName().endsWith(".yml")) { this.loadSingleFile(subFile, StringUtils.chop(type)); } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/loot/LootManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/loot/LootManagerImpl.java index 8e753263..3161ea82 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/loot/LootManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/loot/LootManagerImpl.java @@ -80,7 +80,7 @@ public class LootManagerImpl implements LootManager { for (File subFile : files) { if (subFile.isDirectory()) { fileDeque.push(subFile); - } else if (subFile.isFile()) { + } else if (subFile.isFile() && subFile.getName().endsWith(".yml")) { loadSingleFile(subFile, StringUtils.chop(type)); } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/mob/MobManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/mob/MobManagerImpl.java index dff83e96..7127bb46 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/mob/MobManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/mob/MobManagerImpl.java @@ -94,7 +94,7 @@ public class MobManagerImpl implements MobManager { for (File subFile : files) { if (subFile.isDirectory()) { fileDeque.push(subFile); - } else if (subFile.isFile()) { + } else if (subFile.isFile() && subFile.getName().endsWith(".yml")) { this.loadSingleFile(subFile); } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/storage/StorageManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/storage/StorageManagerImpl.java index 53807c33..3cb98c4c 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/storage/StorageManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/storage/StorageManagerImpl.java @@ -25,9 +25,11 @@ import net.momirealms.customfishing.api.data.DataStorageInterface; import net.momirealms.customfishing.api.data.PlayerData; import net.momirealms.customfishing.api.data.StorageType; import net.momirealms.customfishing.api.data.user.OfflineUser; +import net.momirealms.customfishing.api.data.user.OnlineUser; import net.momirealms.customfishing.api.manager.StorageManager; import net.momirealms.customfishing.api.scheduler.CancellableTask; import net.momirealms.customfishing.api.util.LogUtils; +import net.momirealms.customfishing.setting.Config; import net.momirealms.customfishing.storage.method.database.nosql.MongoDBImpl; import net.momirealms.customfishing.storage.method.database.nosql.RedisManager; import net.momirealms.customfishing.storage.method.database.sql.H2Impl; @@ -62,11 +64,12 @@ public class StorageManagerImpl implements StorageManager, Listener { private final CustomFishingPlugin plugin; private DataStorageInterface dataSource; private StorageType previousType; - private final ConcurrentHashMap onlineUserMap; + private final ConcurrentHashMap onlineUserMap; private final HashSet locked; private boolean hasRedis; private RedisManager redisManager; private String uniqueID; + private CancellableTask timerSaveTask; public StorageManagerImpl(CustomFishingPluginImpl plugin) { this.plugin = plugin; @@ -77,7 +80,7 @@ public class StorageManagerImpl implements StorageManager, Listener { public void reload() { YamlConfiguration config = plugin.getConfig("database.yml"); - uniqueID = config.getString("unique-server-id", "default"); + this.uniqueID = config.getString("unique-server-id", "default"); StorageType storageType = StorageType.valueOf(config.getString("data-storage-method", "H2")); if (storageType != previousType) { if (this.dataSource != null) this.dataSource.disable(); @@ -91,26 +94,42 @@ public class StorageManagerImpl implements StorageManager, Listener { case MariaDB -> this.dataSource = new MariaDBImpl(plugin); case MongoDB -> this.dataSource = new MongoDBImpl(plugin); } - if (dataSource != null) this.dataSource.initialize(); + if (this.dataSource != null) this.dataSource.initialize(); else LogUtils.severe("No storage type is set."); } - if (!hasRedis && config.getBoolean("Redis.enable", false)) { - hasRedis = true; + if (!this.hasRedis && config.getBoolean("Redis.enable", false)) { + this.hasRedis = true; this.redisManager = new RedisManager(plugin); this.redisManager.initialize(); } - if (hasRedis && !config.getBoolean("Redis.enable", false) && redisManager != null) { - redisManager.disable(); - redisManager = null; + if (this.hasRedis && !config.getBoolean("Redis.enable", false) && this.redisManager != null) { + this.redisManager.disable(); + this.redisManager = null; } + if (this.timerSaveTask != null && !this.timerSaveTask.isCancelled()) { + this.timerSaveTask.cancel(); + } + if (Config.dataSaveInterval != -1) + this.timerSaveTask = this.plugin.getScheduler().runTaskAsyncTimer( + () -> { + long time1 = System.currentTimeMillis(); + this.dataSource.setPlayersData(this.onlineUserMap.values(), false); + LogUtils.info("Data Saved for online players. Took " + (System.currentTimeMillis() - time1) + "ms."); + }, + Config.dataSaveInterval, + Config.dataSaveInterval, + TimeUnit.SECONDS + ); } public void disable() { HandlerList.unregisterAll(this); + this.dataSource.setPlayersData(onlineUserMap.values(), true); + this.onlineUserMap.clear(); if (this.dataSource != null) - dataSource.disable(); + this.dataSource.disable(); if (this.redisManager != null) - redisManager.disable(); + this.redisManager.disable(); } @Override @@ -119,7 +138,7 @@ public class StorageManagerImpl implements StorageManager, Listener { } @Override - public OnlineUserImpl getOnlineUser(UUID uuid) { + public OnlineUser getOnlineUser(UUID uuid) { return onlineUserMap.get(uuid); } @@ -170,19 +189,19 @@ public class StorageManagerImpl implements StorageManager, Listener { if (locked.contains(uuid)) return; - OnlineUserImpl onlineUser = onlineUserMap.remove(uuid); + OnlineUser onlineUser = onlineUserMap.remove(uuid); if (onlineUser == null) return; PlayerData data = onlineUser.getPlayerData(); if (hasRedis) { redisManager.setChangeServer(uuid).thenRun( - () -> redisManager.setPlayData(uuid, data, true).thenRun( - () -> dataSource.setPlayData(uuid, data, true).thenAccept( + () -> redisManager.setPlayerData(uuid, data, true).thenRun( + () -> dataSource.setPlayerData(uuid, data, true).thenAccept( result -> { if (result) locked.remove(uuid); }))); } else { - dataSource.setPlayData(uuid, data, true).thenAccept( + dataSource.setPlayerData(uuid, data, true).thenAccept( result -> { if (result) locked.remove(uuid); }); diff --git a/plugin/src/main/java/net/momirealms/customfishing/storage/method/AbstractStorage.java b/plugin/src/main/java/net/momirealms/customfishing/storage/method/AbstractStorage.java index f1292a6b..6dbfe84e 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/storage/method/AbstractStorage.java +++ b/plugin/src/main/java/net/momirealms/customfishing/storage/method/AbstractStorage.java @@ -19,8 +19,10 @@ package net.momirealms.customfishing.storage.method; import net.momirealms.customfishing.api.CustomFishingPlugin; import net.momirealms.customfishing.api.data.DataStorageInterface; +import net.momirealms.customfishing.api.data.user.OnlineUser; import java.time.Instant; +import java.util.Collection; public abstract class AbstractStorage implements DataStorageInterface { @@ -43,4 +45,11 @@ public abstract class AbstractStorage implements DataStorageInterface { public int getCurrentSeconds() { return (int) Instant.now().getEpochSecond(); } + + @Override + public void setPlayersData(Collection users, boolean unlock) { + for (OnlineUser user : users) { + this.setPlayerData(user.getUUID(), user.getPlayerData(), unlock); + } + } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/nosql/MongoDBImpl.java b/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/nosql/MongoDBImpl.java index 6e8c8b5c..f2faad68 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/nosql/MongoDBImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/nosql/MongoDBImpl.java @@ -27,6 +27,7 @@ import com.mongodb.client.result.InsertOneResult; import net.momirealms.customfishing.api.CustomFishingPlugin; import net.momirealms.customfishing.api.data.PlayerData; import net.momirealms.customfishing.api.data.StorageType; +import net.momirealms.customfishing.api.data.user.OnlineUser; import net.momirealms.customfishing.api.util.LogUtils; import net.momirealms.customfishing.storage.method.AbstractStorage; import org.bson.Document; @@ -37,6 +38,7 @@ import org.bukkit.Bukkit; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.YamlConfiguration; +import java.util.Collection; import java.util.Collections; import java.util.Optional; import java.util.UUID; @@ -131,7 +133,7 @@ public class MongoDBImpl extends AbstractStorage { } @Override - public CompletableFuture setPlayData(UUID uuid, PlayerData playerData, boolean unlock) { + public CompletableFuture setPlayerData(UUID uuid, PlayerData playerData, boolean unlock) { var future = new CompletableFuture(); plugin.getScheduler().runTaskAsync(() -> { MongoCollection collection = database.getCollection(getCollectionName("data")); @@ -148,4 +150,18 @@ public class MongoDBImpl extends AbstractStorage { }); return future; } + + @Override + public void setPlayersData(Collection users, boolean unlock) { + MongoCollection collection = database.getCollection(getCollectionName("data")); + try { + collection.insertMany(users.stream().map(it -> new Document() + .append("_id", new ObjectId()) + .append("uuid", it.getUUID()) + .append("lock", unlock ? 0 : getCurrentSeconds()) + .append("data", new Binary(plugin.getStorageManager().toBytes(it.getPlayerData())))).toList()); + } catch (MongoException e) { + LogUtils.warn("Failed to update data for online players", e); + } + } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/nosql/RedisManager.java b/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/nosql/RedisManager.java index b91baa80..b98d9ccc 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/nosql/RedisManager.java +++ b/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/nosql/RedisManager.java @@ -256,7 +256,7 @@ public class RedisManager extends AbstractStorage { } @Override - public CompletableFuture setPlayData(UUID uuid, PlayerData playerData, boolean ignore) { + public CompletableFuture setPlayerData(UUID uuid, PlayerData playerData, boolean ignore) { var future = new CompletableFuture(); plugin.getScheduler().runTaskAsync(() -> { try (Jedis jedis = jedisPool.getResource()) { diff --git a/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/AbstractSQLDatabase.java b/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/AbstractSQLDatabase.java index 2b3fb5fb..04bc8e68 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/AbstractSQLDatabase.java +++ b/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/AbstractSQLDatabase.java @@ -19,6 +19,7 @@ package net.momirealms.customfishing.storage.method.database.sql; import net.momirealms.customfishing.api.CustomFishingPlugin; import net.momirealms.customfishing.api.data.PlayerData; +import net.momirealms.customfishing.api.data.user.OnlineUser; import net.momirealms.customfishing.api.util.LogUtils; import net.momirealms.customfishing.setting.Config; import net.momirealms.customfishing.storage.method.AbstractStorage; @@ -29,10 +30,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.sql.*; -import java.util.Locale; -import java.util.Objects; -import java.util.Optional; -import java.util.UUID; +import java.util.*; import java.util.concurrent.CompletableFuture; public abstract class AbstractSQLDatabase extends AbstractStorage { @@ -79,6 +77,7 @@ public abstract class AbstractSQLDatabase extends AbstractStorage { return tablePrefix; } + @SuppressWarnings("DuplicatedCode") @Override public CompletableFuture> getPlayerData(UUID uuid, boolean force) { var future = new CompletableFuture>(); @@ -118,7 +117,7 @@ public abstract class AbstractSQLDatabase extends AbstractStorage { } @Override - public CompletableFuture setPlayData(UUID uuid, PlayerData playerData, boolean unlock) { + public CompletableFuture setPlayerData(UUID uuid, PlayerData playerData, boolean unlock) { var future = new CompletableFuture(); plugin.getScheduler().runTaskAsync(() -> { try ( @@ -138,6 +137,29 @@ public abstract class AbstractSQLDatabase extends AbstractStorage { return future; } + @Override + public void setPlayersData(Collection users, boolean unlock) { + String sql = String.format(SqlConstants.SQL_UPDATE_BY_UUID, getTableName("data")); + try (Connection connection = getConnection()) { + connection.setAutoCommit(false); + try (PreparedStatement statement = connection.prepareStatement(sql)) { + for (OnlineUser user : users) { + statement.setInt(1, unlock ? 0 : getCurrentSeconds()); + statement.setBlob(2, new ByteArrayInputStream(plugin.getStorageManager().toBytes(user.getPlayerData()))); + statement.setString(3, user.getUUID().toString()); + statement.addBatch(); + } + statement.executeBatch(); + connection.commit(); + } catch (SQLException e) { + connection.rollback(); + LogUtils.warn("Failed to update data for online players", e); + } + } catch (SQLException e) { + LogUtils.warn("Failed to get connection when saving online players' data", e); + } + } + public void insertPlayerData(UUID uuid, PlayerData playerData) { try ( Connection connection = getConnection(); diff --git a/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/SQLiteImpl.java b/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/SQLiteImpl.java index d721008a..38b22709 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/SQLiteImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/storage/method/database/sql/SQLiteImpl.java @@ -20,6 +20,8 @@ package net.momirealms.customfishing.storage.method.database.sql; import net.momirealms.customfishing.api.CustomFishingPlugin; import net.momirealms.customfishing.api.data.PlayerData; import net.momirealms.customfishing.api.data.StorageType; +import net.momirealms.customfishing.api.data.user.OfflineUser; +import net.momirealms.customfishing.api.data.user.OnlineUser; import net.momirealms.customfishing.api.util.LogUtils; import net.momirealms.customfishing.setting.Config; import org.bukkit.Bukkit; @@ -29,6 +31,7 @@ import org.sqlite.SQLiteConfig; import java.io.File; import java.io.IOException; import java.sql.*; +import java.util.Collection; import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -73,6 +76,7 @@ public class SQLiteImpl extends AbstractSQLDatabase { return connection; } + @SuppressWarnings("DuplicatedCode") @Override public CompletableFuture> getPlayerData(UUID uuid, boolean force) { var future = new CompletableFuture>(); @@ -110,7 +114,7 @@ public class SQLiteImpl extends AbstractSQLDatabase { } @Override - public CompletableFuture setPlayData(UUID uuid, PlayerData playerData, boolean unlock) { + public CompletableFuture setPlayerData(UUID uuid, PlayerData playerData, boolean unlock) { var future = new CompletableFuture(); plugin.getScheduler().runTaskAsync(() -> { try ( @@ -130,6 +134,29 @@ public class SQLiteImpl extends AbstractSQLDatabase { return future; } + @Override + public void setPlayersData(Collection users, boolean unlock) { + String sql = String.format(SqlConstants.SQL_UPDATE_BY_UUID, getTableName("data")); + try (Connection connection = getConnection()) { + connection.setAutoCommit(false); + try (PreparedStatement statement = connection.prepareStatement(sql)) { + for (OfflineUser user : users) { + statement.setInt(1, unlock ? 0 : getCurrentSeconds()); + statement.setBytes(2, plugin.getStorageManager().toBytes(user.getPlayerData())); + statement.setString(3, user.getUUID().toString()); + statement.addBatch(); + } + statement.executeBatch(); + connection.commit(); + } catch (SQLException e) { + connection.rollback(); + LogUtils.warn("Failed to update bag data for online players", e); + } + } catch (SQLException e) { + LogUtils.warn("Failed to get connection when saving online players' data", e); + } + } + @Override public void insertPlayerData(UUID uuid, PlayerData playerData) { try ( diff --git a/plugin/src/main/java/net/momirealms/customfishing/storage/method/file/JsonImpl.java b/plugin/src/main/java/net/momirealms/customfishing/storage/method/file/JsonImpl.java index 480119f2..00b1c883 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/storage/method/file/JsonImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/storage/method/file/JsonImpl.java @@ -61,7 +61,7 @@ public class JsonImpl extends AbstractStorage { } @Override - public CompletableFuture setPlayData(UUID uuid, PlayerData playerData, boolean ignore) { + public CompletableFuture setPlayerData(UUID uuid, PlayerData playerData, boolean ignore) { this.saveToJsonFile(playerData, getPlayerDataFile(uuid)); return CompletableFuture.completedFuture(true); } diff --git a/plugin/src/main/java/net/momirealms/customfishing/storage/method/file/YAMLImpl.java b/plugin/src/main/java/net/momirealms/customfishing/storage/method/file/YAMLImpl.java index deaa44f3..8307f6ed 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/storage/method/file/YAMLImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/storage/method/file/YAMLImpl.java @@ -72,7 +72,7 @@ public class YAMLImpl extends AbstractStorage { } @Override - public CompletableFuture setPlayData(UUID uuid, PlayerData playerData, boolean ignore) { + public CompletableFuture setPlayerData(UUID uuid, PlayerData playerData, boolean ignore) { YamlConfiguration data = new YamlConfiguration(); data.set("name", playerData.getName()); data.set("bag", playerData.getBagData().serialized); diff --git a/plugin/src/main/java/net/momirealms/customfishing/util/ClassUtils.java b/plugin/src/main/java/net/momirealms/customfishing/util/ClassUtils.java new file mode 100644 index 00000000..b3c4d3e8 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customfishing/util/ClassUtils.java @@ -0,0 +1,54 @@ +package net.momirealms.customfishing.util; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; + +public class ClassUtils { + + @Nullable + public static Class findClass( + @NotNull File file, + @NotNull Class clazz + ) throws IOException, ClassNotFoundException { + if (!file.exists()) { + return null; + } + + URL jar = file.toURI().toURL(); + URLClassLoader loader = new URLClassLoader(new URL[]{jar}, clazz.getClassLoader()); + List matches = new ArrayList<>(); + + try (JarInputStream stream = new JarInputStream(jar.openStream())) { + JarEntry entry; + while ((entry = stream.getNextJarEntry()) != null) { + final String name = entry.getName(); + if (!name.endsWith(".class")) { + continue; + } + matches.add(name.substring(0, name.lastIndexOf('.')).replace('/', '.')); + } + + for (String match : matches) { + try { + Class loaded = loader.loadClass(match); + if (clazz.isAssignableFrom(loaded)) { + loader.close(); + return loaded.asSubclass(clazz); + } + } catch (NoClassDefFoundError ignored) { + } + } + } + loader.close(); + return null; + } +} diff --git a/plugin/src/main/resources/config.yml b/plugin/src/main/resources/config.yml index 569b6827..292159d4 100644 --- a/plugin/src/main/resources/config.yml +++ b/plugin/src/main/resources/config.yml @@ -126,6 +126,7 @@ other-settings: event-priority: NORMAL # Save the data from cache to file periodically to minimize the data loss if server crashes + # set to -1 to disable data-saving-interval: 600 # Requires PlaceholderAPI to work