From 5424c389ce825a85b77938b9b5df4577b70ed4c2 Mon Sep 17 00:00:00 2001 From: CarmJos Date: Fri, 25 Feb 2022 04:38:46 +0800 Subject: [PATCH 1/2] =?UTF-8?q?[2.1.0]=20=E7=89=88=E6=9C=AC=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=20-=20[F]=20=E4=BF=AE=E5=A4=8D=E7=8E=A9=E5=AE=B6?= =?UTF-8?q?=E7=BB=99=E8=87=AA=E5=B7=B1=E5=8F=91=E9=80=81=E4=BC=A0=E9=80=81?= =?UTF-8?q?=E8=AF=B7=E6=B1=82=E7=9A=84=E9=97=AE=E9=A2=98=E3=80=82=20-=20[A?= =?UTF-8?q?]=20=E6=B7=BB=E5=8A=A0=20=E6=8F=92=E4=BB=B6=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=20=E6=8F=90=E7=A4=BA=EF=BC=8C=E5=85=81=E8=AE=B8=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E5=BC=80=E5=85=B3=E3=80=82=20-=20[A]=20?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20=E6=8F=92=E4=BB=B6=E7=BB=9F=E8=AE=A1?= =?UTF-8?q?=EF=BC=8C=E5=85=81=E8=AE=B8=E8=87=AA=E5=AE=9A=E4=B9=89=E5=BC=80?= =?UTF-8?q?=E5=85=B3=E3=80=82=20-=20[A]=20=E5=AE=9E=E7=8E=B0=20#6=20?= =?UTF-8?q?=E6=8F=90=E5=88=B0=E7=9A=84=E5=A4=9A=E6=95=B0=E6=8D=AE=E6=BA=90?= =?UTF-8?q?=E6=94=AF=E6=8C=81=EF=BC=8C=E7=9B=AE=E5=89=8D=E6=94=AF=E6=8C=81?= =?UTF-8?q?=20YAML=E3=80=81JSON=E4=B8=8EMySQL=EF=BC=8C=E5=B9=B6=E6=8F=90?= =?UTF-8?q?=E4=BE=9BCUSTOM=E6=95=B0=E6=8D=AE=E6=BA=90=E5=85=81=E8=AE=B8?= =?UTF-8?q?=E5=BC=80=E5=8F=91=E8=80=85=E9=87=8D=E5=86=99=E3=80=82=20-=20[A?= =?UTF-8?q?]=20=E5=AF=B9=E4=BA=8E=E5=AE=B6=E7=9A=84=E5=90=8D=E5=AD=97?= =?UTF-8?q?=E9=95=BF=E5=BA=A6=E5=81=9A=E5=87=BA=E9=99=90=E5=88=B6=EF=BC=8C?= =?UTF-8?q?=E4=B8=BA=2030=20=E4=B8=AA=E5=AD=97=E7=AC=A6=E3=80=82=20-=20[U]?= =?UTF-8?q?=20=E5=AF=B9=E4=BA=8E=E5=88=A4=E6=96=AD=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E6=9C=80=E5=A4=9A=E8=AE=BE=E7=BD=AE=E5=AE=B6=E6=95=B0=E9=87=8F?= =?UTF-8?q?=E7=9A=84=E6=9D=83=E9=99=90=EF=BC=8C=E4=B8=8D=E5=86=8D=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E2=80=9CMoeTeleport=E2=80=9D=E5=BC=80=E5=A4=B4?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 40 ++- .../java/cc/carm/plugin/moeteleport/Main.java | 153 ++++++----- .../command/home/SetHomeCommand.java | 6 + .../configuration/PluginConfig.java | 16 ++ .../configuration/PluginMessages.java | 1 + .../moeteleport/listener/UserListener.java | 10 +- .../moeteleport/manager/UserManager.java | 110 ++++++-- .../plugin/moeteleport/model/UserData.java | 74 ++---- .../moeteleport/storage/DataSerializer.java | 27 ++ .../moeteleport/storage/DataStorage.java | 68 +++++ .../moeteleport/storage/StorageMethod.java | 64 +++++ .../storage/database/DBConfiguration.java | 27 ++ .../storage/database/DBTables.java | 52 ++++ .../storage/database/MySQLStorage.java | 152 +++++++++++ .../storage/file/FileBasedStorage.java | 56 ++++ .../moeteleport/storage/file/JSONStorage.java | 68 +++++ .../moeteleport/storage/file/YAMLStorage.java | 61 +++++ .../storage/impl/CustomStorage.java | 50 ++++ .../moeteleport/util/DataTaskRunner.java | 8 + .../moeteleport/util/SchedulerUtils.java | 250 ++++++++++++++++++ src/main/resources/config.yml | 47 +++- src/main/resources/messages.yml | 2 + 22 files changed, 1195 insertions(+), 147 deletions(-) create mode 100644 src/main/java/cc/carm/plugin/moeteleport/storage/DataSerializer.java create mode 100644 src/main/java/cc/carm/plugin/moeteleport/storage/DataStorage.java create mode 100644 src/main/java/cc/carm/plugin/moeteleport/storage/StorageMethod.java create mode 100644 src/main/java/cc/carm/plugin/moeteleport/storage/database/DBConfiguration.java create mode 100644 src/main/java/cc/carm/plugin/moeteleport/storage/database/DBTables.java create mode 100644 src/main/java/cc/carm/plugin/moeteleport/storage/database/MySQLStorage.java create mode 100644 src/main/java/cc/carm/plugin/moeteleport/storage/file/FileBasedStorage.java create mode 100644 src/main/java/cc/carm/plugin/moeteleport/storage/file/JSONStorage.java create mode 100644 src/main/java/cc/carm/plugin/moeteleport/storage/file/YAMLStorage.java create mode 100644 src/main/java/cc/carm/plugin/moeteleport/storage/impl/CustomStorage.java create mode 100644 src/main/java/cc/carm/plugin/moeteleport/util/DataTaskRunner.java create mode 100644 src/main/java/cc/carm/plugin/moeteleport/util/SchedulerUtils.java diff --git a/pom.xml b/pom.xml index ba89006..10e2560 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ cc.carm.plugin moeteleport - 2.0.2 + 3.0.0 MoeTeleport 喵喵传送,简单的传送、设置家的插件。 @@ -108,6 +108,21 @@ provided + + cc.carm.lib + easysql-beecp + 0.3.8 + compile + true + + + + cc.carm.lib + githubreleases4j + 1.3.1 + compile + + de.themoep minedown @@ -115,6 +130,13 @@ compile + + org.bstats + bstats-bukkit + 3.0.0 + compile + + junit junit @@ -216,6 +238,22 @@ de.themoep.minedown cc.carm.plugin.moeteleport.lib.minedown + + org.bstats + cc.carm.plugin.moeteleport.lib.bstats + + + org.json + cc.carm.plugin.moeteleport.lib.json + + + cc.carm.lib.easysql + cc.carm.plugin.moeteleport.lib.easysql + + + cc.carm.lib.githubreleases4j + cc.carm.plugin.moeteleport.lib.github + diff --git a/src/main/java/cc/carm/plugin/moeteleport/Main.java b/src/main/java/cc/carm/plugin/moeteleport/Main.java index e5eaf07..869ec56 100644 --- a/src/main/java/cc/carm/plugin/moeteleport/Main.java +++ b/src/main/java/cc/carm/plugin/moeteleport/Main.java @@ -11,96 +11,63 @@ import cc.carm.plugin.moeteleport.command.home.ListHomeCommand; import cc.carm.plugin.moeteleport.command.home.SetHomeCommand; import cc.carm.plugin.moeteleport.command.tpa.TpHandleCommand; import cc.carm.plugin.moeteleport.command.tpa.TpaCommand; +import cc.carm.plugin.moeteleport.configuration.PluginConfig; import cc.carm.plugin.moeteleport.listener.UserListener; import cc.carm.plugin.moeteleport.manager.ConfigManager; import cc.carm.plugin.moeteleport.manager.RequestManager; import cc.carm.plugin.moeteleport.manager.UserManager; -import cc.carm.plugin.moeteleport.model.UserData; +import cc.carm.plugin.moeteleport.storage.DataStorage; +import cc.carm.plugin.moeteleport.storage.StorageMethod; import cc.carm.plugin.moeteleport.util.ColorParser; +import cc.carm.plugin.moeteleport.util.SchedulerUtils; +import org.bstats.bukkit.Metrics; +import org.bstats.charts.SimplePie; import org.bukkit.Bukkit; import org.bukkit.command.CommandExecutor; import org.bukkit.command.PluginCommand; import org.bukkit.command.TabCompleter; -import org.bukkit.entity.Player; import org.bukkit.event.Listener; import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class Main extends JavaPlugin { - - public static boolean debugMode = true; private static Main instance; + private static SchedulerUtils scheduler; + + private static DataStorage storage; + private UserManager userManager; private RequestManager requestManager; - /** - * 注册监听器 - * - * @param listener 监听器 - */ - public static void regListener(Listener listener) { - Bukkit.getPluginManager().registerEvents(listener, getInstance()); - } - - public static void log(String message) { - Bukkit.getConsoleSender().sendMessage(ColorParser.parse("[" + getInstance().getName() + "] " + message)); - } - - public static void error(String message) { - log("&4[ERROR] &r" + message); - } - - public static void debug(String message) { - if (debugMode) { - log("&b[DEBUG] &r" + message); - } - } - - public static Main getInstance() { - return instance; - } - - public static void registerCommand(String commandName, - @NotNull CommandExecutor executor) { - registerCommand(commandName, executor, null); - } - - public static void registerCommand(String commandName, - @NotNull CommandExecutor executor, - @Nullable TabCompleter tabCompleter) { - PluginCommand command = Bukkit.getPluginCommand(commandName); - if (command == null) return; - command.setExecutor(executor); - if (tabCompleter != null) command.setTabCompleter(tabCompleter); - } - - public static UserManager getUserManager() { - return Main.getInstance().userManager; - } - - public static RequestManager getRequestManager() { - return Main.getInstance().requestManager; - } - @Override public void onEnable() { instance = this; + scheduler = new SchedulerUtils(this); log(getName() + " " + getDescription().getVersion() + " &7开始加载..."); long startTime = System.currentTimeMillis(); log("加载配置文件..."); ConfigManager.initConfig(); - log("加载用户管理器..."); - this.userManager = new UserManager(this); + log("初始化存储方式..."); + StorageMethod storageMethod = StorageMethod.read(PluginConfig.STORAGE_METHOD.get()); + log(" 正在使用 " + storageMethod.name() + " 进行数据存储"); + storage = storageMethod.createStorage(); + if (!storage.initialize()) { + error("初始化存储失败,请检查配置文件。"); + storage.shutdown(); + setEnabled(false); + return; // 初始化失败,不再继续加载 + } + + + log("加载用户管理器..."); + this.userManager = new UserManager(); if (Bukkit.getOnlinePlayers().size() > 0) { log(" 加载现有用户数据..."); - for (Player player : Bukkit.getOnlinePlayers()) { - UserData data = Main.getUserManager().loadData(player.getUniqueId()); - Main.getUserManager().getUserDataMap().put(player.getUniqueId(), data); - } + getUserManager().loadAll(); } log("加载请求管理器..."); @@ -124,6 +91,13 @@ public class Main extends JavaPlugin { registerCommand("tpAccept", new TpHandleCommand(), new TpRequestCompleter()); registerCommand("tpDeny", new TpHandleCommand(), new TpRequestCompleter()); + if (PluginConfig.METRICS.get()) { + log("启用统计数据..."); + Metrics metrics = new Metrics(this, 14459); + metrics.addCustomChart(new SimplePie("storage_method", storageMethod::name)); + } + + log("加载完成 ,共耗时 " + (System.currentTimeMillis() - startTime) + " ms 。"); } @@ -137,8 +111,7 @@ public class Main extends JavaPlugin { getRequestManager().shutdown(); log("保存用户数据..."); - getUserManager().getUserDataMap().values().forEach(UserData::save); - getUserManager().getUserDataMap().clear(); + getUserManager().unloadAll(true); log("卸载监听器..."); Bukkit.getServicesManager().unregisterAll(this); @@ -146,4 +119,62 @@ public class Main extends JavaPlugin { log("卸载完成 ,共耗时 " + (System.currentTimeMillis() - startTime) + " ms 。"); } + /** + * 注册监听器 + * + * @param listener 监听器 + */ + public static void regListener(Listener listener) { + Bukkit.getPluginManager().registerEvents(listener, getInstance()); + } + + public static void log(String message) { + Bukkit.getConsoleSender().sendMessage(ColorParser.parse("[" + getInstance().getName() + "] " + message)); + } + + public static void error(String message) { + log("&4[ERROR] &r" + message); + } + + public static void debug(String message) { + if (PluginConfig.DEBUG.get()) { + log("&e[DEBUG] &r" + message); + } + } + + public static Main getInstance() { + return instance; + } + + public static SchedulerUtils getScheduler() { + return scheduler; + } + + public static void registerCommand(String commandName, + @NotNull CommandExecutor executor) { + registerCommand(commandName, executor, null); + } + + public static void registerCommand(String commandName, + @NotNull CommandExecutor executor, + @Nullable TabCompleter tabCompleter) { + PluginCommand command = Bukkit.getPluginCommand(commandName); + if (command == null) return; + command.setExecutor(executor); + if (tabCompleter != null) command.setTabCompleter(tabCompleter); + } + + public static DataStorage getStorage() { + return storage; + } + + public static UserManager getUserManager() { + return Main.getInstance().userManager; + } + + public static RequestManager getRequestManager() { + return Main.getInstance().requestManager; + } + + } diff --git a/src/main/java/cc/carm/plugin/moeteleport/command/home/SetHomeCommand.java b/src/main/java/cc/carm/plugin/moeteleport/command/home/SetHomeCommand.java index 0cadc59..7f597d3 100644 --- a/src/main/java/cc/carm/plugin/moeteleport/command/home/SetHomeCommand.java +++ b/src/main/java/cc/carm/plugin/moeteleport/command/home/SetHomeCommand.java @@ -19,6 +19,12 @@ public class SetHomeCommand implements CommandExecutor { UserData data = Main.getUserManager().getData(player); String homeName = args.length >= 1 ? args[0] : "home"; + if (homeName.length() > 30) { + // 限定家的名字长度 + PluginMessages.Home.NAME_TOO_LONG.sendWithPlaceholders(sender); + return true; + } + int maxHome = Main.getUserManager().getMaxHome(player); if (data.getHomeLocations().size() >= maxHome && data.getHomeLocation(homeName) == null) { PluginMessages.Home.OVER_LIMIT.sendWithPlaceholders(sender, diff --git a/src/main/java/cc/carm/plugin/moeteleport/configuration/PluginConfig.java b/src/main/java/cc/carm/plugin/moeteleport/configuration/PluginConfig.java index 49083e6..d4d16f9 100644 --- a/src/main/java/cc/carm/plugin/moeteleport/configuration/PluginConfig.java +++ b/src/main/java/cc/carm/plugin/moeteleport/configuration/PluginConfig.java @@ -6,6 +6,22 @@ import cc.carm.plugin.moeteleport.configuration.values.ConfigValueMap; public class PluginConfig { + public static final ConfigValue DEBUG = new ConfigValue<>( + "debug", Boolean.class, false + ); + + public static final ConfigValue METRICS = new ConfigValue<>( + "metrics", Boolean.class, true + ); + + public static final ConfigValue CHECK_UPDATE = new ConfigValue<>( + "check-update", Boolean.class, true + ); + + public static final ConfigValue STORAGE_METHOD = new ConfigValue<>( + "storage.method", String.class, "YAML" + ); + public static final ConfigValueMap PERMISSIONS = new ConfigValueMap<>( "permissions", Integer::parseInt, String.class ); diff --git a/src/main/java/cc/carm/plugin/moeteleport/configuration/PluginMessages.java b/src/main/java/cc/carm/plugin/moeteleport/configuration/PluginMessages.java index 23d1db8..21932cd 100644 --- a/src/main/java/cc/carm/plugin/moeteleport/configuration/PluginMessages.java +++ b/src/main/java/cc/carm/plugin/moeteleport/configuration/PluginMessages.java @@ -46,6 +46,7 @@ public class PluginMessages { public static final ConfigMessageList OVER_LIMIT = new ConfigMessageList("home-over-limit"); + public static final ConfigMessageList NAME_TOO_LONG = new ConfigMessageList("name-too-long"); public static final ConfigMessageList NOT_FOUND = new ConfigMessageList("home-not-found"); public static final ConfigMessageList SET = new ConfigMessageList("home-set"); diff --git a/src/main/java/cc/carm/plugin/moeteleport/listener/UserListener.java b/src/main/java/cc/carm/plugin/moeteleport/listener/UserListener.java index 5034916..d289d0c 100644 --- a/src/main/java/cc/carm/plugin/moeteleport/listener/UserListener.java +++ b/src/main/java/cc/carm/plugin/moeteleport/listener/UserListener.java @@ -3,7 +3,6 @@ package cc.carm.plugin.moeteleport.listener; import cc.carm.plugin.moeteleport.Main; import cc.carm.plugin.moeteleport.configuration.PluginConfig; import cc.carm.plugin.moeteleport.configuration.PluginMessages; -import cc.carm.plugin.moeteleport.model.UserData; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; @@ -11,23 +10,18 @@ import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; -import java.util.UUID; - public class UserListener implements Listener { @EventHandler public void onJoin(PlayerJoinEvent event) { - UUID uuid = event.getPlayer().getUniqueId(); - UserData data = Main.getUserManager().loadData(uuid); - Main.getUserManager().getUserDataMap().put(uuid, data); + Main.getUserManager().loadData(event.getPlayer().getUniqueId()); } @EventHandler public void onQuit(PlayerQuitEvent event) { Player player = event.getPlayer(); Main.getRequestManager().cancelAllRequests(player); - Main.getUserManager().getData(player).save(); //保存 - Main.getUserManager().getUserDataMap().remove(player.getUniqueId()); + Main.getUserManager().unloadData(player.getUniqueId()); } @EventHandler diff --git a/src/main/java/cc/carm/plugin/moeteleport/manager/UserManager.java b/src/main/java/cc/carm/plugin/moeteleport/manager/UserManager.java index 893b15f..af1951d 100644 --- a/src/main/java/cc/carm/plugin/moeteleport/manager/UserManager.java +++ b/src/main/java/cc/carm/plugin/moeteleport/manager/UserManager.java @@ -3,31 +3,98 @@ package cc.carm.plugin.moeteleport.manager; import cc.carm.plugin.moeteleport.Main; import cc.carm.plugin.moeteleport.configuration.PluginConfig; import cc.carm.plugin.moeteleport.model.UserData; +import cc.carm.plugin.moeteleport.storage.DataStorage; +import cc.carm.plugin.moeteleport.util.DataTaskRunner; +import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.File; import java.util.HashMap; import java.util.Map; import java.util.UUID; public class UserManager { - private final File dataFolder; + private final HashMap userDataMap; - private final HashMap userDataMap = new HashMap<>(); - - public UserManager(Main main) { - this.dataFolder = new File(main.getDataFolder() + "/data"); - if (!dataFolder.isDirectory() || !dataFolder.exists()) { - boolean success = dataFolder.mkdir(); - } + public UserManager() { + this.userDataMap = new HashMap<>(); } @NotNull - public UserData loadData(UUID userUUID) { - return new UserData(getDataFolder(), userUUID); + public UserData readData(UUID userUUID) { + try { + long start = System.currentTimeMillis(); + DataStorage storage = Main.getStorage(); + Main.debug("正通过 " + storage.getClass().getSimpleName() + " 读取 " + userUUID + " 的用户数据...(" + System.currentTimeMillis() + ")"); + + UserData data = storage.loadData(userUUID); + + if (data == null) { + Main.debug("当前还不存在玩家 " + userUUID + " 的数据,视作新档。"); + return new UserData(userUUID); + } + + Main.debug("通过 " + storage.getClass().getSimpleName() + "读取 " + userUUID + " 的用户数据完成," + + "耗时 " + (System.currentTimeMillis() - start) + "ms。"); + + return data; + } catch (Exception e) { + Main.error("无法正常读取玩家数据,玩家操作将不会被保存,请检查数据配置!"); + Main.error("Could not load user's data, please check the data configuration!"); + e.printStackTrace(); + return new UserData(userUUID); + } + } + + public void saveData(UserData data) { + try { + long start = System.currentTimeMillis(); + DataStorage storage = Main.getStorage(); + + Main.debug("正通过 " + storage.getClass().getSimpleName() + " 保存 " + data.getUserUUID() + " 的用户数据...(" + System.currentTimeMillis() + ")"); + storage.saveUserData(data); + + Main.debug("通过 " + storage.getClass().getSimpleName() + " 保存 " + data.getUserUUID() + " 的用户数据完成," + + "耗时 " + (System.currentTimeMillis() - start) + "ms。"); + + } catch (Exception e) { + Main.error("无法正常保存玩家数据,请检查数据配置!"); + Main.error("Could not save user's data, please check the data configuration!"); + e.printStackTrace(); + } + } + + public void loadData(UUID userUUID) { + getUserDataMap().put(userUUID, readData(userUUID)); + } + + public void unloadData(UUID userUUID) { + unloadData(userUUID, true); + } + + public void unloadData(UUID userUUID, boolean save) { + UserData data = getData(userUUID); + if (data == null) return; + if (save) saveData(data); + getUserDataMap().remove(userUUID); + } + + public void loadAll() { + for (Player player : Bukkit.getOnlinePlayers()) { + if (getUserDataMap().containsKey(player.getUniqueId())) continue; + loadData(player.getUniqueId()); + } + } + + public void saveAll() { + getUserDataMap().values().forEach(this::saveData); + } + + public void unloadAll(boolean save) { + if (save) saveAll(); + getUserDataMap().clear(); } @Nullable @@ -45,20 +112,29 @@ public class UserManager { int current = PluginConfig.DEFAULT_HOME.get(); for (Map.Entry entry : permissions.entrySet()) { - if (entry.getKey() > current && player.hasPermission( - Main.getInstance().getName() + "." + entry.getValue() - )) { + if (entry.getKey() > current && player.hasPermission(entry.getValue())) { current = entry.getKey(); } } return current; } + public void editData(@NotNull DataTaskRunner task) { + try { + task.run(Main.getStorage()); + } catch (Exception exception) { + Main.error("无法正常更改玩家数据,请检查数据配置!"); + Main.error("Could not edit user's data, please check the data configuration!"); + exception.printStackTrace(); + } + } + + public void editDataAsync(@NotNull DataTaskRunner task) { + Main.getScheduler().runAsync(() -> editData(task)); + } + public HashMap getUserDataMap() { return userDataMap; } - public File getDataFolder() { - return dataFolder; - } } diff --git a/src/main/java/cc/carm/plugin/moeteleport/model/UserData.java b/src/main/java/cc/carm/plugin/moeteleport/model/UserData.java index dc85ad5..6901bbb 100644 --- a/src/main/java/cc/carm/plugin/moeteleport/model/UserData.java +++ b/src/main/java/cc/carm/plugin/moeteleport/model/UserData.java @@ -3,52 +3,37 @@ package cc.carm.plugin.moeteleport.model; import cc.carm.plugin.moeteleport.Main; import cc.carm.plugin.moeteleport.configuration.location.DataLocation; import org.bukkit.Location; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.configuration.file.YamlConfiguration; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.File; -import java.io.IOException; import java.util.*; import java.util.concurrent.ConcurrentHashMap; public class UserData { - private final @NotNull File dataFile; - private final @NotNull FileConfiguration dataConfig; - private final HashSet sentRequests = new HashSet<>(); // 记录发出的请求 - private final ConcurrentHashMap receivedRequests = new ConcurrentHashMap<>(); // 记录收到的传送请求 + protected final @NotNull UUID userUUID; + public boolean enableAutoSelect = false; private @Nullable Location lastLocation; - private LinkedHashMap homeLocations; + private final LinkedHashMap homeLocations; - public UserData(@NotNull File dataFolder, @NotNull UUID uuid) { - this(new File(dataFolder, uuid + ".yml")); + private final HashSet sentRequests = new HashSet<>(); // 记录发出的请求 + private final ConcurrentHashMap receivedRequests = new ConcurrentHashMap<>(); // 记录收到的传送请求 + + public UserData(@NotNull UUID userUUID) { + this(userUUID, null, new LinkedHashMap<>()); } - public UserData(@NotNull File file) { - if (!file.exists()) { - try { - boolean success = file.createNewFile(); - } catch (IOException e) { - Main.error("在加载用户 " + file.getName() + " 的数据时出现异常。"); - Main.error(e.getLocalizedMessage()); - } - } - this.dataFile = file; - this.dataConfig = YamlConfiguration.loadConfiguration(dataFile); - loadHomeData(); + public UserData(@NotNull UUID userUUID, + @Nullable DataLocation lastLocation, + @NotNull LinkedHashMap homeLocations) { + this.userUUID = userUUID; + this.lastLocation = Optional.ofNullable(lastLocation).map(DataLocation::getBukkitLocation).orElse(null); + this.homeLocations = homeLocations; } - public void loadHomeData() { - LinkedHashMap data = new LinkedHashMap<>(); - Optional.ofNullable(getDataConfig().getConfigurationSection("homes")) - .ifPresent(section -> section.getKeys(false).forEach(homeName -> { - DataLocation location = DataLocation.deserializeText(section.getString(homeName)); - if (location != null) data.put(homeName, location); - })); - this.homeLocations = data; + public @NotNull UUID getUserUUID() { + return userUUID; } public LinkedHashMap getHomeLocations() { @@ -58,11 +43,13 @@ public class UserData { public void setHomeLocation(String homeName, Location location) { delHomeLocation(homeName); getHomeLocations().put(homeName, new DataLocation(location)); + Main.getUserManager().editData((storage) -> storage.setHome(userUUID, homeName, new DataLocation(location))); } public void delHomeLocation(String homeName) { Map.Entry lastLocation = getHomeLocation(homeName); if (lastLocation != null) getHomeLocations().remove(lastLocation.getKey()); + Main.getUserManager().editData((storage) -> storage.delHome(userUUID, homeName)); } public Map.Entry getHomeLocation(@Nullable String homeName) { @@ -104,29 +91,4 @@ public class UserData { this.enableAutoSelect = enableAutoSelect; } - public @NotNull File getDataFile() { - return dataFile; - } - - public @NotNull FileConfiguration getDataConfig() { - return dataConfig; - } - - public LinkedHashMap saveToMap() { - LinkedHashMap homeLocations = getHomeLocations(); - LinkedHashMap data = new LinkedHashMap<>(); - if (homeLocations.isEmpty()) return data; - homeLocations.forEach((name, loc) -> data.put(name, loc.serializeToText())); - return data; - } - - public void save() { - try { - getDataConfig().createSection("homes", saveToMap()); - getDataConfig().save(getDataFile()); - } catch (Exception ex) { - Main.error("在保存 " + getDataFile().getName() + " 时出现异常。"); - Main.error(ex.getLocalizedMessage()); - } - } } diff --git a/src/main/java/cc/carm/plugin/moeteleport/storage/DataSerializer.java b/src/main/java/cc/carm/plugin/moeteleport/storage/DataSerializer.java new file mode 100644 index 0000000..010f7f2 --- /dev/null +++ b/src/main/java/cc/carm/plugin/moeteleport/storage/DataSerializer.java @@ -0,0 +1,27 @@ +package cc.carm.plugin.moeteleport.storage; + +import cc.carm.plugin.moeteleport.configuration.location.DataLocation; +import org.bukkit.Location; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class DataSerializer { + + public static Map serializeLocationsMap(LinkedHashMap data) { + LinkedHashMap after = new LinkedHashMap<>(); + if (data == null || data.isEmpty()) return after; + data.forEach((name, loc) -> after.put(name, loc.serializeToText())); + return after; + } + + public static String serializeLocation(DataLocation loc) { + return loc.serializeToText(); + } + + public static String serializeLocation(Location loc) { + return serializeLocation(new DataLocation(loc)); + } + + +} diff --git a/src/main/java/cc/carm/plugin/moeteleport/storage/DataStorage.java b/src/main/java/cc/carm/plugin/moeteleport/storage/DataStorage.java new file mode 100644 index 0000000..37e1a52 --- /dev/null +++ b/src/main/java/cc/carm/plugin/moeteleport/storage/DataStorage.java @@ -0,0 +1,68 @@ +package cc.carm.plugin.moeteleport.storage; + +import cc.carm.plugin.moeteleport.configuration.location.DataLocation; +import cc.carm.plugin.moeteleport.manager.UserManager; +import cc.carm.plugin.moeteleport.model.UserData; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.UUID; + +public interface DataStorage { + + /** + * 在插件加载存储源时执行。 + * + * @return 是否初始化成功 + */ + boolean initialize(); + + /** + * 在插件被卸载时执行。 + */ + void shutdown(); + + /** + * 用于加载用户数据的方法。该方法将会被异步运行! + *
该方法一般无需自行执行,见 {@link UserManager#loadData(UUID)} + *
+ *
若不存在该用户的数据,请返回 null 。 + *
若加载出现任何错误,请抛出异常。 + * + * @param uuid 用户UUID + * @throws Exception 当出现任何错误时抛出 + */ + @Nullable + UserData loadData(@NotNull UUID uuid) throws Exception; + + /** + * 用于保存用户数据的方法。 该方法将会被异步运行! + *
该方法一般无需自行执行,见 {@link UserManager#saveData(UserData)} + * + * @param data 用户数据 + * @throws Exception 当出现任何错误时抛出 + */ + void saveUserData(@NotNull UserData data) throws Exception; + + /** + * 为某用户设定一个家的位置。 + * + * @param uuid 用户UUID + * @param homeName 家的名称 + * @param homeLocation 家的位置 + * @throws Exception 当出现任何错误时抛出 + */ + void setHome(@NotNull UUID uuid, @NotNull String homeName, @NotNull DataLocation homeLocation) throws Exception; + + + /** + * 为某用户移除一个家的位置。 + * + * @param uuid 用户UUID + * @param homeName 家的名称 + * @return 是否有一个家被移除 + * @throws Exception 当出现任何错误时抛出 + */ + boolean delHome(@NotNull UUID uuid, @NotNull String homeName) throws Exception; + +} diff --git a/src/main/java/cc/carm/plugin/moeteleport/storage/StorageMethod.java b/src/main/java/cc/carm/plugin/moeteleport/storage/StorageMethod.java new file mode 100644 index 0000000..b8d2b53 --- /dev/null +++ b/src/main/java/cc/carm/plugin/moeteleport/storage/StorageMethod.java @@ -0,0 +1,64 @@ +package cc.carm.plugin.moeteleport.storage; + +import cc.carm.plugin.moeteleport.storage.impl.CustomStorage; +import cc.carm.plugin.moeteleport.storage.file.JSONStorage; +import cc.carm.plugin.moeteleport.storage.database.MySQLStorage; +import cc.carm.plugin.moeteleport.storage.file.YAMLStorage; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.Optional; +import java.util.function.Supplier; + +public enum StorageMethod { + + CUSTOM(0, CustomStorage::new), + YAML(1, YAMLStorage::new), + JSON(2, JSONStorage::new), + MYSQL(3, MySQLStorage::new); + + private final int id; + private @NotNull Supplier<@NotNull DataStorage> storageSupplier; + + StorageMethod(int id, @NotNull Supplier<@NotNull DataStorage> storageSupplier) { + this.id = id; + this.storageSupplier = storageSupplier; + } + + public int getID() { + return id; + } + + public @NotNull Supplier<@NotNull DataStorage> getStorageSupplier() { + return storageSupplier; + } + + public void setStorageSupplier(@NotNull Supplier<@NotNull DataStorage> storageSupplier) { + this.storageSupplier = storageSupplier; + } + + public @NotNull DataStorage createStorage() { + return getStorageSupplier().get(); + } + + public static @NotNull StorageMethod read(String s) { + StorageMethod byName = readByName(s); + if (byName != null) return byName; + try { + return Optional.ofNullable(readByID(Integer.parseInt(s))).orElse(YAML); + } catch (Exception ex) { + return YAML; + } + } + + + public static @Nullable StorageMethod readByName(String name) { + return Arrays.stream(values()).filter(value -> value.name().equalsIgnoreCase(name)).findFirst().orElse(null); + } + + + public static @Nullable StorageMethod readByID(int id) { + return Arrays.stream(values()).filter(value -> value.getID() == id).findFirst().orElse(null); + } +} diff --git a/src/main/java/cc/carm/plugin/moeteleport/storage/database/DBConfiguration.java b/src/main/java/cc/carm/plugin/moeteleport/storage/database/DBConfiguration.java new file mode 100644 index 0000000..d1b04a5 --- /dev/null +++ b/src/main/java/cc/carm/plugin/moeteleport/storage/database/DBConfiguration.java @@ -0,0 +1,27 @@ +package cc.carm.plugin.moeteleport.storage.database; + +import cc.carm.plugin.moeteleport.configuration.values.ConfigValue; + +public class DBConfiguration { + + protected static final ConfigValue DRIVER_NAME = new ConfigValue<>( + "storage.mysql.driver", String.class, + "com.mysql.cj.jdbc.Driver" + ); + + protected static final ConfigValue URL = new ConfigValue<>( + "storage.mysql.url", String.class, + "jdbc:mysql://127.0.0.1:3306/minecraft" + ); + + protected static final ConfigValue USERNAME = new ConfigValue<>( + "storage.mysql.username", String.class, + "root" + ); + + protected static final ConfigValue PASSWORD = new ConfigValue<>( + "storage.mysql.password", String.class, + "password" + ); + +} diff --git a/src/main/java/cc/carm/plugin/moeteleport/storage/database/DBTables.java b/src/main/java/cc/carm/plugin/moeteleport/storage/database/DBTables.java new file mode 100644 index 0000000..b57f7e5 --- /dev/null +++ b/src/main/java/cc/carm/plugin/moeteleport/storage/database/DBTables.java @@ -0,0 +1,52 @@ +package cc.carm.plugin.moeteleport.storage.database; + +import cc.carm.plugin.moeteleport.configuration.values.ConfigValue; + +public class DBTables { + + protected static class UserLastLocations { + + protected static final ConfigValue TABLE_NAME = new ConfigValue<>( + "storage.mysql.tables.last-location", String.class, + "mt_user_last_locations" + ); + + protected static final String[] TABLE_COLUMNS = new String[]{ + "`uuid` VARCHAR(36) NOT NULL PRIMARY KEY", // 用户的UUID + "`world` VARCHAR(128) NOT NULL",// 最后地址的所在世界 + "`x` DOUBLE NOT NULL",// 最后地址的X坐标 + "`y` DOUBLE NOT NULL",// 最后地址的Y坐标 + "`z` DOUBLE NOT NULL",// 最后地址的Z坐标 + "`yaw` DOUBLE NOT NULL",// 最后地址的Yaw角度 + "`pitch` DOUBLE NOT NULL",// 最后地址的Pitch角度 + "`update` DATETIME NOT NULL " + + "DEFAULT CURRENT_TIMESTAMP " + + "ON UPDATE CURRENT_TIMESTAMP " + }; + + } + + protected static class UserHomes { + + protected static final ConfigValue TABLE_NAME = new ConfigValue<>( + "storage.mysql.tables.home", String.class, + "mt_user_homes" + ); + + protected static final String[] TABLE_COLUMNS = new String[]{ + "`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '背包ID'", + "`uuid` VARCHAR(36) NOT NULL", // 用户的UUID + "`name` VARCHAR(32) NOT NULL", + "`world` VARCHAR(128) NOT NULL", + "`x` DOUBLE NOT NULL", + "`y` DOUBLE NOT NULL", + "`z` DOUBLE NOT NULL", + "`yaw` DOUBLE NOT NULL", + "`pitch` DOUBLE NOT NULL", + "INDEX `user`(`uuid`)", + "UNIQUE KEY `home`(`uuid`,`name`)" + }; + + } + +} \ No newline at end of file diff --git a/src/main/java/cc/carm/plugin/moeteleport/storage/database/MySQLStorage.java b/src/main/java/cc/carm/plugin/moeteleport/storage/database/MySQLStorage.java new file mode 100644 index 0000000..ab9b3b4 --- /dev/null +++ b/src/main/java/cc/carm/plugin/moeteleport/storage/database/MySQLStorage.java @@ -0,0 +1,152 @@ +package cc.carm.plugin.moeteleport.storage.database; + +import cc.carm.lib.easysql.EasySQL; +import cc.carm.lib.easysql.api.SQLManager; +import cc.carm.plugin.moeteleport.Main; +import cc.carm.plugin.moeteleport.configuration.location.DataLocation; +import cc.carm.plugin.moeteleport.model.UserData; +import cc.carm.plugin.moeteleport.storage.DataStorage; +import org.bukkit.Location; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.LinkedHashMap; +import java.util.UUID; + +public class MySQLStorage implements DataStorage { + + SQLManager sqlManager; + + @Override + public boolean initialize() { + try { + Main.log(" 尝试连接到数据库..."); + this.sqlManager = EasySQL.createManager( + DBConfiguration.DRIVER_NAME.get(), DBConfiguration.URL.get(), + DBConfiguration.USERNAME.get(), DBConfiguration.PASSWORD.get() + ); + } catch (Exception exception) { + Main.error("无法连接到数据库,请检查配置文件。"); + exception.printStackTrace(); + return false; + } + + try { + Main.log(" 创建插件所需表..."); + getSQLManager().createTable(DBTables.UserLastLocations.TABLE_NAME.get()) + .setColumns(DBTables.UserLastLocations.TABLE_COLUMNS) + .build().execute(); + + getSQLManager().createTable(DBTables.UserHomes.TABLE_NAME.get()) + .setColumns(DBTables.UserHomes.TABLE_COLUMNS) + .build().execute(); + + } catch (SQLException exception) { + Main.error("无法创建插件所需的表,请检查数据库权限。"); + exception.printStackTrace(); + return false; + } + + return true; + } + + @Override + public void shutdown() { + Main.log(" 关闭数据库连接..."); + EasySQL.shutdownManager(getSQLManager()); + this.sqlManager = null; + } + + @Override + public @Nullable UserData loadData(@NotNull UUID uuid) throws Exception { + LinkedHashMap homes = loadHomes(uuid); + DataLocation lastLocation = loadLastLocation(uuid); + return new UserData(uuid, lastLocation, homes); + } + + @Override + public void saveUserData(@NotNull UserData data) throws Exception { + Location location = data.getLastLocation(); + if (location != null && location.getWorld() != null) { + getSQLManager().createReplace(DBTables.UserLastLocations.TABLE_NAME.get()) + .setColumnNames("uuid", "world", "x", "y", "z", "yaw", "pitch") + .setParams( + data.getUserUUID(), location.getWorld(), + location.getX(), location.getY(), location.getZ(), + location.getYaw(), location.getPitch() + ).execute(); + } else { + getSQLManager().createDelete(DBTables.UserLastLocations.TABLE_NAME.get()) + .addCondition("uuid", data.getUserUUID()).setLimit(1) + .build().execute(); + } + } + + private @NotNull LinkedHashMap loadHomes(@NotNull UUID uuid) throws Exception { + return getSQLManager().createQuery().inTable(DBTables.UserHomes.TABLE_NAME.get()) + .addCondition("uuid", uuid).build() + .executeFunction((query) -> { + LinkedHashMap homes = new LinkedHashMap<>(); + ResultSet resultSet = query.getResultSet(); + if (resultSet == null) return homes; + while (resultSet.next()) { + String name = resultSet.getString("name"); + if (name == null) continue; + homes.put(name, new DataLocation( + resultSet.getString("world"), + resultSet.getDouble("x"), + resultSet.getDouble("y"), + resultSet.getDouble("z"), + resultSet.getFloat("yaw"), + resultSet.getFloat("pitch") + )); + } + return homes; + }, new LinkedHashMap<>()); + } + + private @Nullable DataLocation loadLastLocation(@NotNull UUID uuid) throws Exception { + return getSQLManager().createQuery().inTable(DBTables.UserLastLocations.TABLE_NAME.get()) + .addCondition("uuid", uuid).setLimit(1).build() + .executeFunction((query) -> { + ResultSet resultSet = query.getResultSet(); + if (resultSet == null || !resultSet.next()) return null; + return new DataLocation( + resultSet.getString("world"), + resultSet.getDouble("x"), + resultSet.getDouble("y"), + resultSet.getDouble("z"), + resultSet.getFloat("yaw"), + resultSet.getFloat("pitch") + ); + }); + } + + @Override + public void setHome(@NotNull UUID uuid, @NotNull String homeName, @NotNull DataLocation location) throws Exception { + getSQLManager().createReplace(DBTables.UserHomes.TABLE_NAME.get()) + .setColumnNames("uuid", "name", "world", "x", "y", "z", "yaw", "pitch") + .setParams( + uuid, homeName, location.getWorldName(), + location.getX(), location.getY(), location.getZ(), + location.getYaw(), location.getPitch() + ).execute(); + } + + + @Override + public boolean delHome(@NotNull UUID uuid, @NotNull String homeName) throws Exception { + return getSQLManager().createDelete(DBTables.UserHomes.TABLE_NAME.get()) + .addCondition("uuid", uuid) + .addCondition("name", homeName) + .build().executeFunction((i) -> i > 0, false); + } + + public SQLManager getSQLManager() { + return sqlManager; + } + + +} diff --git a/src/main/java/cc/carm/plugin/moeteleport/storage/file/FileBasedStorage.java b/src/main/java/cc/carm/plugin/moeteleport/storage/file/FileBasedStorage.java new file mode 100644 index 0000000..a97a5bf --- /dev/null +++ b/src/main/java/cc/carm/plugin/moeteleport/storage/file/FileBasedStorage.java @@ -0,0 +1,56 @@ +package cc.carm.plugin.moeteleport.storage.file; + +import cc.carm.plugin.moeteleport.Main; +import cc.carm.plugin.moeteleport.configuration.location.DataLocation; +import cc.carm.plugin.moeteleport.configuration.values.ConfigValue; +import cc.carm.plugin.moeteleport.storage.DataStorage; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.util.UUID; + +public abstract class FileBasedStorage implements DataStorage { + + private static final ConfigValue FILE_PATH = new ConfigValue<>( + "storage.file-path", String.class, "data" + ); + + protected @Nullable File dataFolder; + + @Override + public boolean initialize() { + try { + this.dataFolder = new File(Main.getInstance().getDataFolder(), FILE_PATH.get()); + if (!dataFolder.exists()) { + return dataFolder.mkdir(); + } else { + return dataFolder.isDirectory(); + } + } catch (Exception ex) { + return false; + } + } + + @Override + public void shutdown() { + // 似乎没什么需要做的? + dataFolder = null; + } + + @Override + public void setHome(@NotNull UUID uuid, @NotNull String homeName, @NotNull DataLocation homeLocation) throws Exception { + // saveData 方法即保存所有数据,不需要针对单个数据进行变更。 + } + + @Override + public boolean delHome(@NotNull UUID uuid, @NotNull String homeName) { + // saveData 方法即保存所有数据,不需要针对单个数据进行变更。 + return true; + } + + public @Nullable File getDataFolder() { + return dataFolder; + } + +} diff --git a/src/main/java/cc/carm/plugin/moeteleport/storage/file/JSONStorage.java b/src/main/java/cc/carm/plugin/moeteleport/storage/file/JSONStorage.java new file mode 100644 index 0000000..c012a24 --- /dev/null +++ b/src/main/java/cc/carm/plugin/moeteleport/storage/file/JSONStorage.java @@ -0,0 +1,68 @@ +package cc.carm.plugin.moeteleport.storage.file; + +import cc.carm.plugin.moeteleport.Main; +import cc.carm.plugin.moeteleport.configuration.location.DataLocation; +import cc.carm.plugin.moeteleport.model.UserData; +import cc.carm.plugin.moeteleport.storage.DataSerializer; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.util.LinkedHashMap; +import java.util.Optional; +import java.util.UUID; + +public class JSONStorage extends FileBasedStorage { + + protected static final Gson GSON = new Gson(); + protected static final JsonParser PARSER = new JsonParser(); + + @Override + public @Nullable UserData loadData(@NotNull UUID uuid) throws Exception { + File userDataFile = new File(getDataFolder(), uuid + ".json"); + if (!userDataFile.exists()) { + Main.debug("当前文件夾内不存在玩家 " + uuid + " 的数据,视作新档。"); + return null; + } + + JsonElement dataElement = PARSER.parse(new FileReader(userDataFile)); + if (!dataElement.isJsonObject()) throw new NullPointerException(userDataFile.getName()); + + JsonObject dataObject = dataElement.getAsJsonObject(); + + DataLocation lastLocation = Optional + .ofNullable(dataObject.get("lastLocation").getAsString()) + .map(DataLocation::deserializeText) + .orElse(null); + + LinkedHashMap homeData = new LinkedHashMap<>(); + JsonObject homesObject = dataObject.getAsJsonObject("homes"); + if (homesObject != null) { + homesObject.entrySet().forEach(entry -> { + DataLocation location = DataLocation.deserializeText(entry.getValue().getAsString()); + if (location != null) homeData.put(entry.getKey(), location); + }); + } + + return new UserData(uuid, lastLocation, homeData); + } + + @Override + public void saveUserData(@NotNull UserData data) throws Exception { + JsonObject dataObject = new JsonObject(); + dataObject.addProperty("lastLocation", DataSerializer.serializeLocation(data.getLastLocation())); + dataObject.add("homes", GSON.toJsonTree(DataSerializer.serializeLocationsMap(data.getHomeLocations()))); + + FileWriter writer = new FileWriter(new File(getDataFolder(), data.getUserUUID() + ".json")); + writer.write(GSON.toJson(dataObject)); + writer.flush(); + writer.close(); + } + +} diff --git a/src/main/java/cc/carm/plugin/moeteleport/storage/file/YAMLStorage.java b/src/main/java/cc/carm/plugin/moeteleport/storage/file/YAMLStorage.java new file mode 100644 index 0000000..d738830 --- /dev/null +++ b/src/main/java/cc/carm/plugin/moeteleport/storage/file/YAMLStorage.java @@ -0,0 +1,61 @@ +package cc.carm.plugin.moeteleport.storage.file; + +import cc.carm.plugin.moeteleport.Main; +import cc.carm.plugin.moeteleport.configuration.location.DataLocation; +import cc.carm.plugin.moeteleport.model.UserData; +import cc.carm.plugin.moeteleport.storage.DataSerializer; +import org.bukkit.configuration.file.YamlConfiguration; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.util.LinkedHashMap; +import java.util.Optional; +import java.util.UUID; + +public class YAMLStorage extends FileBasedStorage { + + @Override + public @Nullable UserData loadData(@NotNull UUID uuid) { + if (getDataFolder() == null || !getDataFolder().exists() || !getDataFolder().isDirectory()) { + throw new NullPointerException("Storage data folder is not initialized."); + } + File userDataFile = new File(getDataFolder(), uuid + ".yml"); + if (!userDataFile.exists()) { + Main.debug("当前文件夾内不存在玩家 " + uuid + " 的数据,视作新档。"); + return null; + } + + YamlConfiguration userConfiguration = YamlConfiguration.loadConfiguration(userDataFile); + + DataLocation lastLocation = Optional + .ofNullable(userConfiguration.getString("lastLocation")) + .map(DataLocation::deserializeText) + .orElse(null); + + LinkedHashMap homeData = new LinkedHashMap<>(); + Optional.ofNullable(userConfiguration.getConfigurationSection("homes")).ifPresent( + section -> section.getKeys(false).forEach(homeName -> { + DataLocation location = DataLocation.deserializeText(section.getString(homeName)); + if (location != null) homeData.put(homeName, location); + })); + + return new UserData(uuid, lastLocation, homeData); + } + + @Override + public void saveUserData(@NotNull UserData data) throws Exception { + if (getDataFolder() == null || !getDataFolder().exists() || !getDataFolder().isDirectory()) { + throw new NullPointerException("Storage data folder is not initialized."); + } + + YamlConfiguration userConfiguration = new YamlConfiguration(); + if (data.getLastLocation() != null) { + userConfiguration.set("lastLocation", DataSerializer.serializeLocation(data.getLastLocation())); + } + + userConfiguration.createSection("homes", DataSerializer.serializeLocationsMap(data.getHomeLocations())); + userConfiguration.save(new File(getDataFolder(), data.getUserUUID() + ".yml")); + } + +} diff --git a/src/main/java/cc/carm/plugin/moeteleport/storage/impl/CustomStorage.java b/src/main/java/cc/carm/plugin/moeteleport/storage/impl/CustomStorage.java new file mode 100644 index 0000000..555bc64 --- /dev/null +++ b/src/main/java/cc/carm/plugin/moeteleport/storage/impl/CustomStorage.java @@ -0,0 +1,50 @@ +package cc.carm.plugin.moeteleport.storage.impl; + +import cc.carm.plugin.moeteleport.Main; +import cc.carm.plugin.moeteleport.configuration.location.DataLocation; +import cc.carm.plugin.moeteleport.model.UserData; +import cc.carm.plugin.moeteleport.storage.DataStorage; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.TestOnly; + +import java.util.UUID; + +public class CustomStorage implements DataStorage { + + @Override + @TestOnly + public boolean initialize() { + Main.error("您选择使用自定义存储,但并没有应用成功。"); + Main.error("You are using CustomStorage, but not overwrite the methods."); + return false; + } + + @Override + @TestOnly + public void shutdown() { + + } + + @Override + @TestOnly + public @Nullable UserData loadData(@NotNull UUID uuid) { + return null; + } + + @Override + @TestOnly + public void saveUserData(@NotNull UserData data) { + + } + + @Override + public void setHome(@NotNull UUID uuid, @NotNull String homeName, @NotNull DataLocation homeLocation) throws Exception { + } + + @Override + public boolean delHome(@NotNull UUID uuid, @NotNull String homeName) { + return true; + } + +} diff --git a/src/main/java/cc/carm/plugin/moeteleport/util/DataTaskRunner.java b/src/main/java/cc/carm/plugin/moeteleport/util/DataTaskRunner.java new file mode 100644 index 0000000..2b35d47 --- /dev/null +++ b/src/main/java/cc/carm/plugin/moeteleport/util/DataTaskRunner.java @@ -0,0 +1,8 @@ +package cc.carm.plugin.moeteleport.util; + +import cc.carm.plugin.moeteleport.storage.DataStorage; + +@FunctionalInterface +public interface DataTaskRunner { + void run(DataStorage storage) throws Exception; +} diff --git a/src/main/java/cc/carm/plugin/moeteleport/util/SchedulerUtils.java b/src/main/java/cc/carm/plugin/moeteleport/util/SchedulerUtils.java new file mode 100644 index 0000000..881ba5b --- /dev/null +++ b/src/main/java/cc/carm/plugin/moeteleport/util/SchedulerUtils.java @@ -0,0 +1,250 @@ + + +package cc.carm.plugin.moeteleport.util; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.Queue; +import java.util.concurrent.Callable; +import org.bukkit.Bukkit; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.scheduler.BukkitRunnable; + +public class SchedulerUtils { + private final JavaPlugin plugin; + + public SchedulerUtils(JavaPlugin plugin) { + this.plugin = plugin; + } + + private JavaPlugin getPlugin() { + return this.plugin; + } + + public void run(Runnable runnable) { + Bukkit.getScheduler().runTask(this.getPlugin(), runnable); + } + + public void runAsync(Runnable runnable) { + Bukkit.getScheduler().runTaskAsynchronously(this.getPlugin(), runnable); + } + + public void runLater(long delay, Runnable runnable) { + Bukkit.getScheduler().runTaskLater(this.getPlugin(), runnable, delay); + } + + public void runLaterAsync(long delay, Runnable runnable) { + Bukkit.getScheduler().runTaskLaterAsynchronously(this.getPlugin(), runnable, delay); + } + + public void runAtInterval(long interval, Runnable... tasks) { + this.runAtInterval(0L, interval, tasks); + } + + public void runAtInterval(long delay, long interval, Runnable... tasks) { + (new BukkitRunnable() { + private int index; + + public void run() { + if (this.index >= tasks.length) { + this.cancel(); + } else { + tasks[this.index].run(); + ++this.index; + } + } + }).runTaskTimer(this.getPlugin(), delay, interval); + } + + public void runAtIntervalAsync(long interval, Runnable... tasks) { + this.runAtIntervalAsync(0L, interval, tasks); + } + + public void runAtIntervalAsync(long delay, long interval, Runnable... tasks) { + (new BukkitRunnable() { + private int index; + + public void run() { + if (this.index >= tasks.length) { + this.cancel(); + } else { + tasks[this.index].run(); + ++this.index; + } + } + }).runTaskTimerAsynchronously(this.getPlugin(), delay, interval); + } + + public void repeat(int repetitions, long interval, Runnable task, Runnable onComplete) { + (new BukkitRunnable() { + private int index; + + public void run() { + ++this.index; + if (this.index >= repetitions) { + this.cancel(); + if (onComplete != null) { + onComplete.run(); + } + } else { + task.run(); + } + } + }).runTaskTimer(this.getPlugin(), 0L, interval); + } + + public void repeatAsync(int repetitions, long interval, Runnable task, Runnable onComplete) { + (new BukkitRunnable() { + private int index; + + public void run() { + ++this.index; + if (this.index >= repetitions) { + this.cancel(); + if (onComplete != null) { + onComplete.run(); + } + } else { + task.run(); + } + } + }).runTaskTimerAsynchronously(this.getPlugin(), 0L, interval); + } + + public void repeatWhile(long interval, Callable predicate, Runnable task, Runnable onComplete) { + (new BukkitRunnable() { + public void run() { + try { + if (!(Boolean)predicate.call()) { + this.cancel(); + if (onComplete == null) { + return; + } + + onComplete.run(); + return; + } + + task.run(); + } catch (Exception var2) { + var2.printStackTrace(); + } + + } + }).runTaskTimer(this.getPlugin(), 0L, interval); + } + + public void repeatWhileAsync(long interval, Callable predicate, Runnable task, Runnable onComplete) { + (new BukkitRunnable() { + public void run() { + try { + if (!(Boolean)predicate.call()) { + this.cancel(); + if (onComplete == null) { + return; + } + + onComplete.run(); + return; + } + + task.run(); + } catch (Exception var2) { + var2.printStackTrace(); + } + + } + }).runTaskTimerAsynchronously(this.getPlugin(), 0L, interval); + } + + public class TaskBuilder { + private final Queue taskList = new LinkedList<>(); + + public TaskBuilder() { + } + + public SchedulerUtils.TaskBuilder append(SchedulerUtils.TaskBuilder builder) { + this.taskList.addAll(builder.taskList); + return this; + } + + public SchedulerUtils.TaskBuilder appendDelay(long delay) { + this.taskList.add((onComplete) -> SchedulerUtils.this.runLater(delay, onComplete)); + return this; + } + + public SchedulerUtils.TaskBuilder appendTask(Runnable task) { + this.taskList.add((onComplete) -> { + task.run(); + onComplete.run(); + }); + return this; + } + + public SchedulerUtils.TaskBuilder appendTask(SchedulerUtils.Task task) { + this.taskList.add(task); + return this; + } + + public SchedulerUtils.TaskBuilder appendDelayedTask(long delay, Runnable task) { + this.taskList.add((onComplete) -> SchedulerUtils.this.runLater(delay, () -> { + task.run(); + onComplete.run(); + })); + return this; + } + + public SchedulerUtils.TaskBuilder appendTasks(long delay, long interval, Runnable... tasks) { + this.taskList.add((onComplete) -> { + Runnable[] runnables = Arrays.copyOf(tasks, tasks.length + 1); + runnables[runnables.length - 1] = onComplete; + SchedulerUtils.this.runAtInterval(delay, interval, runnables); + }); + return this; + } + + public SchedulerUtils.TaskBuilder appendRepeatingTask(int repetitions, long interval, Runnable task) { + this.taskList.add((onComplete) -> SchedulerUtils.this.repeat(repetitions, interval, task, onComplete)); + return this; + } + + public SchedulerUtils.TaskBuilder appendConditionalRepeatingTask(long interval, Callable predicate, Runnable task) { + this.taskList.add((onComplete) -> SchedulerUtils.this.repeatWhile(interval, predicate, task, onComplete)); + return this; + } + + public SchedulerUtils.TaskBuilder waitFor(Callable predicate) { + this.taskList.add((onComplete) -> (new BukkitRunnable() { + public void run() { + try { + if (!(Boolean)predicate.call()) { + return; + } + + this.cancel(); + onComplete.run(); + } catch (Exception var2) { + var2.printStackTrace(); + } + + } + }).runTaskTimer(SchedulerUtils.this.getPlugin(), 0L, 1L)); + return this; + } + + public void runTasks() { + this.startNext(); + } + + private void startNext() { + SchedulerUtils.Task task = this.taskList.poll(); + if (task != null) { + task.start(this::startNext); + } + } + } + + public interface Task { + void start(Runnable onComplete); + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index d68b5f6..e6287b3 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,11 +1,50 @@ -version: ${project.version} +# ${project.name} - ${project.description} +# 项目地址: ${project.url} +# 下载地址: ${project.distributionManagement.downloadUrl} + +version: ${project.version} +debug: false + +# 统计数据设定 +# 该选项用于帮助开发者统计插件版本与使用情况,且绝不会影响性能与使用体验。 +# 当然,您也可以选择在这里关闭,或在plugins/bStats下的配置文件中关闭。 +metrics: true + +# 检查更新设定 +# 该选项用于插件判断是否要检查更新,若您不希望插件检查更新并提示您,可以选择关闭。 +# 检查更新为异步操作,绝不会影响性能与使用体验。 +check-update: true + +# 存储相关配置 +# 注意:存储配置不会通过重载指令生效,如有修改请重新启动服务器。 +storage: + + # 存储方式,可选 [ yaml | json | mysql(跨服推荐) | Essential(须安装ess插件) ] + method: yaml + + # 选择 yaml/json 存储方式时的存储路径 + # 默认为相对路径,相对于插件生成的配置文件夹下的路径 + # 支持绝对路径,如 “/var/data/ud/"(linux) 或 "D:\data\ud\"(windows) + # 使用绝对路径时请注意权限问题 + file-path: data + + # 选择 database 存储方式时的数据库配置 + mysql: + # 数据库驱动路径 + driver: "com.mysql.cj.jdbc.Driver" + url: "jdbc:mysql://127.0.0.1:3306/" + username: "username" + password: "password" + tables: + last-location: "mt_last_locations" + home: "mt_homes" + +# 默认玩家可设置多少家 defaultHome: 1 permissions: - # 以下命令全部为 MoeTeleport 的子节点 - # 如 "home.vip" 的权限全拼就是 "MoeTeleport.home.vip" - 10: "home.vip" # 最多可以设置10个家 + 10: "MoeTeleport.home.vip" # 最多可以设置10个家 # 传送请求过期时间 expireTime: 30 diff --git a/src/main/resources/messages.yml b/src/main/resources/messages.yml index 6c62075..1ae7ba4 100644 --- a/src/main/resources/messages.yml +++ b/src/main/resources/messages.yml @@ -61,6 +61,8 @@ home-list-header: home-list-object: "&8# &f%(id) &d%(location) [&7✈](show_text=点击返回家 %(id) run_command=/home %(id))" home-not-found: - "&f您还没有设置这个家,请先输入 &5/setHome <家名称> &f设置一个吧!" +name-too-long: + - "&f您所输入的家的名字太长,家的名称不应当超过 &d30 &f个字符。" home-set: - "&f成功设定名为 &d%(name) &f的家传送点。" home-removed: From 947799e8b289486eaf6f1aed067324a4a31d4a4a Mon Sep 17 00:00:00 2001 From: CarmJos Date: Fri, 25 Feb 2022 04:38:58 +0800 Subject: [PATCH 2/2] =?UTF-8?q?[2.1.0]=20=E7=89=88=E6=9C=AC=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=20-=20[F]=20=E4=BF=AE=E5=A4=8D=E7=8E=A9=E5=AE=B6?= =?UTF-8?q?=E7=BB=99=E8=87=AA=E5=B7=B1=E5=8F=91=E9=80=81=E4=BC=A0=E9=80=81?= =?UTF-8?q?=E8=AF=B7=E6=B1=82=E7=9A=84=E9=97=AE=E9=A2=98=E3=80=82=20-=20[A?= =?UTF-8?q?]=20=E6=B7=BB=E5=8A=A0=20=E6=8F=92=E4=BB=B6=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=20=E6=8F=90=E7=A4=BA=EF=BC=8C=E5=85=81=E8=AE=B8=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E5=BC=80=E5=85=B3=E3=80=82=20-=20[A]=20?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20=E6=8F=92=E4=BB=B6=E7=BB=9F=E8=AE=A1?= =?UTF-8?q?=EF=BC=8C=E5=85=81=E8=AE=B8=E8=87=AA=E5=AE=9A=E4=B9=89=E5=BC=80?= =?UTF-8?q?=E5=85=B3=E3=80=82=20-=20[A]=20=E5=AE=9E=E7=8E=B0=20#6=20?= =?UTF-8?q?=E6=8F=90=E5=88=B0=E7=9A=84=E5=A4=9A=E6=95=B0=E6=8D=AE=E6=BA=90?= =?UTF-8?q?=E6=94=AF=E6=8C=81=EF=BC=8C=E7=9B=AE=E5=89=8D=E6=94=AF=E6=8C=81?= =?UTF-8?q?=20YAML=E3=80=81JSON=E4=B8=8EMySQL=EF=BC=8C=E5=B9=B6=E6=8F=90?= =?UTF-8?q?=E4=BE=9BCUSTOM=E6=95=B0=E6=8D=AE=E6=BA=90=E5=85=81=E8=AE=B8?= =?UTF-8?q?=E5=BC=80=E5=8F=91=E8=80=85=E9=87=8D=E5=86=99=E3=80=82=20-=20[A?= =?UTF-8?q?]=20=E5=AF=B9=E4=BA=8E=E5=AE=B6=E7=9A=84=E5=90=8D=E5=AD=97?= =?UTF-8?q?=E9=95=BF=E5=BA=A6=E5=81=9A=E5=87=BA=E9=99=90=E5=88=B6=EF=BC=8C?= =?UTF-8?q?=E4=B8=BA=2030=20=E4=B8=AA=E5=AD=97=E7=AC=A6=E3=80=82=20-=20[U]?= =?UTF-8?q?=20=E5=AF=B9=E4=BA=8E=E5=88=A4=E6=96=AD=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E6=9C=80=E5=A4=9A=E8=AE=BE=E7=BD=AE=E5=AE=B6=E6=95=B0=E9=87=8F?= =?UTF-8?q?=E7=9A=84=E6=9D=83=E9=99=90=EF=BC=8C=E4=B8=8D=E5=86=8D=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E2=80=9CMoeTeleport=E2=80=9D=E5=BC=80=E5=A4=B4?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cc/carm/plugin/moeteleport/util/SchedulerUtils.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/cc/carm/plugin/moeteleport/util/SchedulerUtils.java b/src/main/java/cc/carm/plugin/moeteleport/util/SchedulerUtils.java index 881ba5b..906df78 100644 --- a/src/main/java/cc/carm/plugin/moeteleport/util/SchedulerUtils.java +++ b/src/main/java/cc/carm/plugin/moeteleport/util/SchedulerUtils.java @@ -196,9 +196,9 @@ public class SchedulerUtils { public SchedulerUtils.TaskBuilder appendTasks(long delay, long interval, Runnable... tasks) { this.taskList.add((onComplete) -> { - Runnable[] runnables = Arrays.copyOf(tasks, tasks.length + 1); - runnables[runnables.length - 1] = onComplete; - SchedulerUtils.this.runAtInterval(delay, interval, runnables); + Runnable[] all = Arrays.copyOf(tasks, tasks.length + 1); + all[all.length - 1] = onComplete; + SchedulerUtils.this.runAtInterval(delay, interval, all); }); return this; }