From d854a9b1efdffeef9223db8603d0866bdb3b1939 Mon Sep 17 00:00:00 2001 From: zhangyuheng Date: Mon, 13 May 2024 16:57:09 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E4=BA=86=20Homes=20=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E6=8C=87=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/lunadeer/essentialsd/EssentialsD.java | 17 ++- .../essentialsd/commands/home/DelHome.java | 62 ++++++++++ .../essentialsd/commands/home/Home.java | 60 ++++++++++ .../essentialsd/commands/home/Homes.java | 36 ++++++ .../essentialsd/commands/home/SetHome.java | 58 +++++++++ .../lunadeer/essentialsd/dtos/HomeInfo.java | 85 +++++++++++++ .../essentialsd/dtos/LoginRecord.java | 48 ++++++++ .../lunadeer/essentialsd/dtos/NameRecord.java | 64 ++++++++++ .../lunadeer/essentialsd/dtos/PlayerName.java | 42 +++++++ .../events/ArmorStandHandsEvent.java | 36 ++++++ .../essentialsd/{ => events}/ChairEvent.java | 3 +- .../essentialsd/events/CrowEvent.java | 57 +++++++++ .../essentialsd/events/ExpBottleEvent.java | 18 +++ .../InvisibleItemFrameEvent.java} | 112 +----------------- .../essentialsd/events/PlayerRecordEvent.java | 33 ++++++ .../essentialsd/events/ShowItemEvent.java | 24 ++++ .../essentialsd/events/TeleportEvent.java | 17 +++ .../essentialsd/managers/ConfigManager.java | 28 ++++- .../essentialsd/managers/DatabaseManager.java | 25 ++-- .../essentialsd/managers/TeleportManager.java | 112 ++++++++++++------ .../lunadeer/essentialsd/tuis/HomeList.java | 44 +++++++ .../essentialsd/utils/LocationUtils.java | 21 ++++ .../lunadeer/essentialsd/utils/STUI/Line.java | 15 ++- .../essentialsd/utils/STUI/ListView.java | 30 ++++- .../essentialsd/utils/STUI/Pagination.java | 7 +- .../lunadeer/essentialsd/utils/STUI/View.java | 45 ++++--- src/main/resources/config.yml | 15 ++- src/main/resources/plugin.yml | 14 ++- 28 files changed, 942 insertions(+), 186 deletions(-) create mode 100644 src/main/java/cn/lunadeer/essentialsd/commands/home/DelHome.java create mode 100644 src/main/java/cn/lunadeer/essentialsd/commands/home/Home.java create mode 100644 src/main/java/cn/lunadeer/essentialsd/commands/home/Homes.java create mode 100644 src/main/java/cn/lunadeer/essentialsd/commands/home/SetHome.java create mode 100644 src/main/java/cn/lunadeer/essentialsd/dtos/HomeInfo.java create mode 100644 src/main/java/cn/lunadeer/essentialsd/dtos/LoginRecord.java create mode 100644 src/main/java/cn/lunadeer/essentialsd/dtos/NameRecord.java create mode 100644 src/main/java/cn/lunadeer/essentialsd/dtos/PlayerName.java create mode 100644 src/main/java/cn/lunadeer/essentialsd/events/ArmorStandHandsEvent.java rename src/main/java/cn/lunadeer/essentialsd/{ => events}/ChairEvent.java (99%) create mode 100644 src/main/java/cn/lunadeer/essentialsd/events/CrowEvent.java create mode 100644 src/main/java/cn/lunadeer/essentialsd/events/ExpBottleEvent.java rename src/main/java/cn/lunadeer/essentialsd/{Events.java => events/InvisibleItemFrameEvent.java} (50%) create mode 100644 src/main/java/cn/lunadeer/essentialsd/events/PlayerRecordEvent.java create mode 100644 src/main/java/cn/lunadeer/essentialsd/events/ShowItemEvent.java create mode 100644 src/main/java/cn/lunadeer/essentialsd/events/TeleportEvent.java create mode 100644 src/main/java/cn/lunadeer/essentialsd/tuis/HomeList.java create mode 100644 src/main/java/cn/lunadeer/essentialsd/utils/LocationUtils.java diff --git a/src/main/java/cn/lunadeer/essentialsd/EssentialsD.java b/src/main/java/cn/lunadeer/essentialsd/EssentialsD.java index 8559e14..44d5f00 100644 --- a/src/main/java/cn/lunadeer/essentialsd/EssentialsD.java +++ b/src/main/java/cn/lunadeer/essentialsd/EssentialsD.java @@ -1,6 +1,11 @@ package cn.lunadeer.essentialsd; import cn.lunadeer.essentialsd.commands.*; +import cn.lunadeer.essentialsd.commands.home.DelHome; +import cn.lunadeer.essentialsd.commands.home.Home; +import cn.lunadeer.essentialsd.commands.home.Homes; +import cn.lunadeer.essentialsd.commands.home.SetHome; +import cn.lunadeer.essentialsd.events.*; import cn.lunadeer.essentialsd.managers.ConfigManager; import cn.lunadeer.essentialsd.managers.DatabaseManager; import cn.lunadeer.essentialsd.managers.TeleportManager; @@ -27,8 +32,14 @@ public final class EssentialsD extends JavaPlugin { scheduler = new Scheduler(this); tpManager = new TeleportManager(); - Bukkit.getPluginManager().registerEvents(new Events(), this); + Bukkit.getPluginManager().registerEvents(new InvisibleItemFrameEvent(), this); Bukkit.getPluginManager().registerEvents(new ChairEvent(), this); + Bukkit.getPluginManager().registerEvents(new ArmorStandHandsEvent(), this); + Bukkit.getPluginManager().registerEvents(new CrowEvent(), this); + Bukkit.getPluginManager().registerEvents(new ExpBottleEvent(), this); + Bukkit.getPluginManager().registerEvents(new ShowItemEvent(), this); + Bukkit.getPluginManager().registerEvents(new TeleportEvent(), this); + Bukkit.getPluginManager().registerEvents(new PlayerRecordEvent(), this); // op 指令 Objects.requireNonNull(Bukkit.getPluginCommand("fly")).setExecutor(new Fly()); Objects.requireNonNull(Bukkit.getPluginCommand("god")).setExecutor(new God()); @@ -50,6 +61,10 @@ public final class EssentialsD extends JavaPlugin { Objects.requireNonNull(Bukkit.getPluginCommand("tpa")).setExecutor(new Tpa()); Objects.requireNonNull(Bukkit.getPluginCommand("rtp")).setExecutor(new Rtp()); Objects.requireNonNull(Bukkit.getPluginCommand("back")).setExecutor(new Back()); + Objects.requireNonNull(Bukkit.getPluginCommand("home")).setExecutor(new Home()); + Objects.requireNonNull(Bukkit.getPluginCommand("homes")).setExecutor(new Homes()); + Objects.requireNonNull(Bukkit.getPluginCommand("sethome")).setExecutor(new SetHome()); + Objects.requireNonNull(Bukkit.getPluginCommand("delhome")).setExecutor(new DelHome()); if (config.getRecipesCrowbar()) { diff --git a/src/main/java/cn/lunadeer/essentialsd/commands/home/DelHome.java b/src/main/java/cn/lunadeer/essentialsd/commands/home/DelHome.java new file mode 100644 index 0000000..0a3fc63 --- /dev/null +++ b/src/main/java/cn/lunadeer/essentialsd/commands/home/DelHome.java @@ -0,0 +1,62 @@ +package cn.lunadeer.essentialsd.commands.home; + +import cn.lunadeer.essentialsd.EssentialsD; +import cn.lunadeer.essentialsd.dtos.HomeInfo; +import cn.lunadeer.essentialsd.tuis.HomeList; +import cn.lunadeer.essentialsd.utils.Notification; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabExecutor; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +public class DelHome implements TabExecutor { + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + if (!(sender instanceof Player)) { + Notification.warn(sender, "只有玩家可以使用此命令"); + return true; + } + if (!EssentialsD.config.getCommandsHome()) { + Notification.error(sender, "这个命令已被管理员禁用"); + return false; + } + Player player = (Player) sender; + String homeName; + if (args.length == 0) { + homeName = "default"; + } else { + homeName = args[0]; + } + boolean res = HomeInfo.deleteHome(player.getUniqueId(), homeName); + if (res) { + Notification.info(player, "成功删除家 " + homeName); + } else { + Notification.error(player, "删除家 " + homeName + " 失败, 请联系管理员"); + } + if (args.length == 2) { + try { + int page = Integer.parseInt(args[1]); + String[] newArgs = new String[1]; + newArgs[0] = String.valueOf(page); + HomeList.show(sender, newArgs); + } catch (Exception ignored) { + } + } + return true; + } + + @Override + public @Nullable List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + List homes = HomeInfo.getHomesOf(((Player) sender).getUniqueId()); + List res = new ArrayList<>(); + for (HomeInfo home : homes) { + res.add(home.homeName); + } + return res; + } +} diff --git a/src/main/java/cn/lunadeer/essentialsd/commands/home/Home.java b/src/main/java/cn/lunadeer/essentialsd/commands/home/Home.java new file mode 100644 index 0000000..75cc801 --- /dev/null +++ b/src/main/java/cn/lunadeer/essentialsd/commands/home/Home.java @@ -0,0 +1,60 @@ +package cn.lunadeer.essentialsd.commands.home; + +import cn.lunadeer.essentialsd.EssentialsD; +import cn.lunadeer.essentialsd.dtos.HomeInfo; +import cn.lunadeer.essentialsd.utils.Notification; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabExecutor; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +public class Home implements TabExecutor { + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + if (!(sender instanceof Player)) { + Notification.warn(sender, "只有玩家可以使用此命令"); + return true; + } + if (!EssentialsD.config.getCommandsHome()) { + Notification.error(sender, "这个命令已被管理员禁用"); + return false; + } + Player player = (Player) sender; + String homeName; + if (args.length == 0) { + homeName = "default"; + } else { + homeName = args[0]; + } + HomeInfo home = HomeInfo.getHome(player.getUniqueId(), homeName); + if (home == null) { + Notification.error(player, "不存在名为 " + homeName + " 的家"); + return true; + } + try { + EssentialsD.tpManager.doTeleportDelayed(player, home.location, EssentialsD.config.getTpDelay(), () -> { + Notification.info(player, "正在传送到家 " + homeName); + }, () -> { + Notification.info(player, "成功传送到家 " + homeName); + }); + } catch (RuntimeException e) { + Notification.error(player, "传送到家 " + homeName + " 失败: " + e.getMessage()); + } + return true; + } + + @Override + public @Nullable List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + List homes = HomeInfo.getHomesOf(((Player) sender).getUniqueId()); + List res = new ArrayList<>(); + for (HomeInfo home : homes) { + res.add(home.homeName); + } + return res; + } +} diff --git a/src/main/java/cn/lunadeer/essentialsd/commands/home/Homes.java b/src/main/java/cn/lunadeer/essentialsd/commands/home/Homes.java new file mode 100644 index 0000000..8142dc9 --- /dev/null +++ b/src/main/java/cn/lunadeer/essentialsd/commands/home/Homes.java @@ -0,0 +1,36 @@ +package cn.lunadeer.essentialsd.commands.home; + +import cn.lunadeer.essentialsd.EssentialsD; +import cn.lunadeer.essentialsd.dtos.HomeInfo; +import cn.lunadeer.essentialsd.tuis.HomeList; +import cn.lunadeer.essentialsd.utils.Notification; +import cn.lunadeer.essentialsd.utils.STUI.Button; +import cn.lunadeer.essentialsd.utils.STUI.Line; +import cn.lunadeer.essentialsd.utils.STUI.ListView; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextComponent; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabExecutor; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class Homes implements TabExecutor { + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + if (!EssentialsD.config.getCommandsHome()) { + Notification.error(sender, "这个命令已被管理员禁用"); + return false; + } + HomeList.show(sender, args); + return true; + } + + @Override + public @Nullable List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + return null; + } +} diff --git a/src/main/java/cn/lunadeer/essentialsd/commands/home/SetHome.java b/src/main/java/cn/lunadeer/essentialsd/commands/home/SetHome.java new file mode 100644 index 0000000..1353133 --- /dev/null +++ b/src/main/java/cn/lunadeer/essentialsd/commands/home/SetHome.java @@ -0,0 +1,58 @@ +package cn.lunadeer.essentialsd.commands.home; + +import cn.lunadeer.essentialsd.EssentialsD; +import cn.lunadeer.essentialsd.dtos.HomeInfo; +import cn.lunadeer.essentialsd.utils.Notification; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabExecutor; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class SetHome implements TabExecutor { + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + if (!(sender instanceof Player)) { + Notification.warn(sender, "只有玩家可以使用此命令"); + return true; + } + if (!EssentialsD.config.getCommandsHome()) { + Notification.error(sender, "这个命令已被管理员禁用"); + return false; + } + Player player = (Player) sender; + List homes = HomeInfo.getHomesOf(((Player) sender).getUniqueId()); + if (homes.size() > EssentialsD.config.getHomeLimitAmount()) { + Notification.error(player, "你的家数量已达上限"); + return true; + } + HomeInfo info = new HomeInfo(); + info.uuid = player.getUniqueId(); + if (args.length == 0) { + info.homeName = "default"; + } else { + info.homeName = args[0]; + } + info.location = player.getLocation(); + HomeInfo exist = HomeInfo.getHome(player.getUniqueId(), info.homeName); + if (exist != null) { + Notification.error(player, "已经存在名为 " + info.homeName + " 的家"); + return true; + } + boolean res = HomeInfo.newHome(info); + if (res) { + Notification.info(player, "成功设置家 " + info.homeName); + } else { + Notification.error(player, "设置家 " + info.homeName + " 失败, 请联系管理员"); + } + return true; + } + + @Override + public @Nullable List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + return null; + } +} diff --git a/src/main/java/cn/lunadeer/essentialsd/dtos/HomeInfo.java b/src/main/java/cn/lunadeer/essentialsd/dtos/HomeInfo.java new file mode 100644 index 0000000..a5149d3 --- /dev/null +++ b/src/main/java/cn/lunadeer/essentialsd/dtos/HomeInfo.java @@ -0,0 +1,85 @@ +package cn.lunadeer.essentialsd.dtos; + +import cn.lunadeer.essentialsd.managers.DatabaseManager; +import cn.lunadeer.essentialsd.utils.LocationUtils; +import cn.lunadeer.essentialsd.utils.XLogger; +import org.bukkit.Location; + +import java.sql.ResultSet; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class HomeInfo { + /* + "CREATE TABLE IF NOT EXISTS home_info (" + + " id SERIAL PRIMARY KEY," + + " uuid VARCHAR(36) NOT NULL," + + " home_name TEXT NOT NULL," + + " location TEXT NOT NULL," + + + " FOREIGN KEY (uuid) REFERENCES player_name(uuid) ON DELETE CASCADE" + + ");" + */ + + public UUID uuid; + public String homeName; + public Location location; + + public static boolean newHome(HomeInfo info) { + String sql = "INSERT INTO home_info (uuid, home_name, location) " + + "VALUES " + + "('" + info.uuid.toString() + "', '" + info.homeName + "', '" + LocationUtils.Serialize(info.location) + "')"; + try (ResultSet rs = DatabaseManager.query(sql)) { + return true; + } catch (Exception e) { + XLogger.err("Database query failed: " + e.getMessage()); + return false; + } + } + + public static boolean deleteHome(UUID uuid, String homeName) { + String sql = "DELETE FROM home_info WHERE uuid = '" + uuid.toString() + "' AND home_name = '" + homeName + "'"; + try (ResultSet rs = DatabaseManager.query(sql)) { + return true; + } catch (Exception e) { + XLogger.err("Database query failed: " + e.getMessage()); + return false; + } + } + + public static List getHomesOf(UUID uuid) { + List homes = new ArrayList<>(); + String sql = "SELECT * FROM home_info WHERE uuid = '" + uuid.toString() + "'"; + try (ResultSet rs = DatabaseManager.query(sql)) { + if (rs == null) return homes; + while (rs.next()) { + HomeInfo home = new HomeInfo(); + home.uuid = UUID.fromString(rs.getString("uuid")); + home.homeName = rs.getString("home_name"); + home.location = LocationUtils.Deserialize(rs.getString("location")); + homes.add(home); + } + } catch (Exception e) { + XLogger.err("Database query failed: " + e.getMessage()); + } + return homes; + } + + public static HomeInfo getHome(UUID uuid, String homeName) { + String sql = "SELECT * FROM home_info WHERE uuid = '" + uuid.toString() + "' AND home_name = '" + homeName + "'"; + try (ResultSet rs = DatabaseManager.query(sql)) { + if (rs == null) return null; + if (rs.next()) { + HomeInfo home = new HomeInfo(); + home.uuid = UUID.fromString(rs.getString("uuid")); + home.homeName = rs.getString("home_name"); + home.location = LocationUtils.Deserialize(rs.getString("location")); + return home; + } + } catch (Exception e) { + XLogger.err("Database query failed: " + e.getMessage()); + } + return null; + } +} diff --git a/src/main/java/cn/lunadeer/essentialsd/dtos/LoginRecord.java b/src/main/java/cn/lunadeer/essentialsd/dtos/LoginRecord.java new file mode 100644 index 0000000..49ff34b --- /dev/null +++ b/src/main/java/cn/lunadeer/essentialsd/dtos/LoginRecord.java @@ -0,0 +1,48 @@ +package cn.lunadeer.essentialsd.dtos; + +import cn.lunadeer.essentialsd.managers.DatabaseManager; +import cn.lunadeer.essentialsd.utils.LocationUtils; +import cn.lunadeer.essentialsd.utils.XLogger; +import org.bukkit.entity.Player; + +import java.net.InetSocketAddress; +import java.sql.ResultSet; +import java.util.UUID; + +public class LoginRecord { + /* + "CREATE TABLE IF NOT EXISTS login_record (" + + " id SERIAL PRIMARY KEY," + + " uuid VARCHAR(36) NOT NULL," + + " ip VARCHAR(15) NOT NULL," + + " name TEXT NOT NULL," + + " login_time TIMESTAMP NOT NULL," + + " logout_location TEXT," + + " logout_time TIMESTAMP," + + + " FOREIGN KEY (uuid) REFERENCES player_name(uuid) ON DELETE CASCADE" + + ");" + */ + + public static void newLoginRecord(Player player) { + UUID uuid = player.getUniqueId(); + InetSocketAddress address = player.getAddress(); + String ip; + if (address == null) { + ip = "unknown"; + } else { + ip = address.getAddress().getHostAddress(); + } + long login_time = player.getLastLogin(); + long logout_time = System.currentTimeMillis(); + String logout_location = LocationUtils.Serialize(player.getLocation()); + String sql = "INSERT INTO login_record (uuid, ip, login_time, logout_location, logout_time) " + + "VALUES " + + "('" + uuid.toString() + "', '" + ip + "', '" + login_time + "', '" + logout_location + "', '" + logout_time + "')"; + try (ResultSet rs = DatabaseManager.query(sql)) { + if (rs == null) return; + } catch (Exception e) { + XLogger.err("Database query failed: " + e.getMessage()); + } + } +} diff --git a/src/main/java/cn/lunadeer/essentialsd/dtos/NameRecord.java b/src/main/java/cn/lunadeer/essentialsd/dtos/NameRecord.java new file mode 100644 index 0000000..ec3444b --- /dev/null +++ b/src/main/java/cn/lunadeer/essentialsd/dtos/NameRecord.java @@ -0,0 +1,64 @@ +package cn.lunadeer.essentialsd.dtos; + +import cn.lunadeer.essentialsd.managers.DatabaseManager; +import cn.lunadeer.essentialsd.utils.XLogger; +import com.destroystokyo.paper.profile.PlayerProfile; +import org.bukkit.entity.Player; + +import java.net.InetSocketAddress; +import java.sql.ResultSet; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class NameRecord { + /* + "CREATE TABLE IF NOT EXISTS name_record (" + + " id SERIAL PRIMARY KEY," + + " uuid VARCHAR(36) NOT NULL," + + " name TEXT NOT NULL," + + " time TIMESTAMP NOT NULL," + + + " FOREIGN KEY (uuid) REFERENCES player_name(uuid) ON DELETE CASCADE" + + ");" + */ + public UUID uuid; + public String name; + public Long time; + public String timeString; + + public static boolean newNameRecord(Player player){ + UUID uuid = player.getUniqueId(); + String name = player.getName(); + String sql = "INSERT INTO name_record (uuid, name, time) VALUES ('" + uuid.toString() + "', '" + name + "', '" + System.currentTimeMillis() + "')"; + try (ResultSet rs = DatabaseManager.query(sql)) { + return true; + } catch (Exception e) { + XLogger.err("Database query failed: " + e.getMessage()); + return false; + } + } + + public static List get5NameHistoryOf(UUID uuid) { + return getNameHistoryOf(uuid, 5); + } + + public static List getNameHistoryOf(UUID uuid, Integer limit) { + List records = new ArrayList<>(); + String sql = "SELECT * FROM name_record WHERE uuid = '" + uuid.toString() + "' ORDER BY time DESC LIMIT " + limit; + try (ResultSet rs = DatabaseManager.query(sql)) { + if (rs == null) return records; + while (rs.next()) { + NameRecord record = new NameRecord(); + record.uuid = UUID.fromString(rs.getString("uuid")); + record.name = rs.getString("name"); + record.time = rs.getTimestamp("time").getTime(); + record.timeString = rs.getTimestamp("time").toString(); + records.add(record); + } + } catch (Exception e) { + XLogger.err("Database query failed: " + e.getMessage()); + } + return records; + } +} diff --git a/src/main/java/cn/lunadeer/essentialsd/dtos/PlayerName.java b/src/main/java/cn/lunadeer/essentialsd/dtos/PlayerName.java new file mode 100644 index 0000000..ba105d9 --- /dev/null +++ b/src/main/java/cn/lunadeer/essentialsd/dtos/PlayerName.java @@ -0,0 +1,42 @@ +package cn.lunadeer.essentialsd.dtos; + +import cn.lunadeer.essentialsd.managers.DatabaseManager; +import cn.lunadeer.essentialsd.utils.XLogger; + +import java.sql.ResultSet; +import java.util.UUID; + +public class PlayerName { + public static String getName(UUID uuid) { + String sql = "SELECT last_known_name FROM player_name WHERE uuid = '" + uuid.toString() + "'"; + try (ResultSet rs = DatabaseManager.query(sql)) { + if (rs == null) return null; + if (rs.next()) { + return rs.getString("last_known_name"); + } + } catch (Exception e) { + XLogger.err("Database query failed: " + e.getMessage()); + } + return null; + } + + public static boolean setName(UUID uuid, String name) { + String sql = "INSERT INTO player_name (uuid, last_known_name) VALUES ('" + uuid.toString() + "', '" + name + "') ON CONFLICT (uuid) DO UPDATE SET last_known_name = '" + name + "'"; + try (ResultSet rs = DatabaseManager.query(sql)) { + return true; + } catch (Exception e) { + XLogger.err("Database query failed: " + e.getMessage()); + return false; + } + } + + public static boolean updateName(UUID uuid, String name) { + String sql = "UPDATE player_name SET last_known_name = '" + name + "' WHERE uuid = '" + uuid.toString() + "'"; + try (ResultSet rs = DatabaseManager.query(sql)) { + return true; + } catch (Exception e) { + XLogger.err("Database query failed: " + e.getMessage()); + return false; + } + } +} diff --git a/src/main/java/cn/lunadeer/essentialsd/events/ArmorStandHandsEvent.java b/src/main/java/cn/lunadeer/essentialsd/events/ArmorStandHandsEvent.java new file mode 100644 index 0000000..fdc4111 --- /dev/null +++ b/src/main/java/cn/lunadeer/essentialsd/events/ArmorStandHandsEvent.java @@ -0,0 +1,36 @@ +package cn.lunadeer.essentialsd.events; + +import org.bukkit.Material; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.inventory.ItemStack; + +public class ArmorStandHandsEvent implements Listener { + // put stick as arms for an ArmorStand + @EventHandler + public void onAddArmsForArmStand(EntityDamageByEntityEvent event) { + Entity entity = event.getEntity(); + if (!(entity instanceof ArmorStand)) { + return; + } + if (!(event.getDamager() instanceof Player)) { + return; + } + Player player = (Player) event.getDamager(); + ItemStack mainHand = player.getInventory().getItemInMainHand(); + if (mainHand.getType() != Material.STICK) { + return; + } + event.setCancelled(true); + ArmorStand armorStand = (ArmorStand) entity; + if (armorStand.hasArms()) { + return; + } + armorStand.setArms(true); + mainHand.setAmount(mainHand.getAmount() - 1); + } +} diff --git a/src/main/java/cn/lunadeer/essentialsd/ChairEvent.java b/src/main/java/cn/lunadeer/essentialsd/events/ChairEvent.java similarity index 99% rename from src/main/java/cn/lunadeer/essentialsd/ChairEvent.java rename to src/main/java/cn/lunadeer/essentialsd/events/ChairEvent.java index 70fd73e..c529d92 100644 --- a/src/main/java/cn/lunadeer/essentialsd/ChairEvent.java +++ b/src/main/java/cn/lunadeer/essentialsd/events/ChairEvent.java @@ -1,5 +1,6 @@ -package cn.lunadeer.essentialsd; +package cn.lunadeer.essentialsd.events; +import cn.lunadeer.essentialsd.EssentialsD; import cn.lunadeer.essentialsd.utils.XLogger; import org.bukkit.Location; import org.bukkit.block.Block; diff --git a/src/main/java/cn/lunadeer/essentialsd/events/CrowEvent.java b/src/main/java/cn/lunadeer/essentialsd/events/CrowEvent.java new file mode 100644 index 0000000..38d2ee0 --- /dev/null +++ b/src/main/java/cn/lunadeer/essentialsd/events/CrowEvent.java @@ -0,0 +1,57 @@ +package cn.lunadeer.essentialsd.events; + +import cn.lunadeer.essentialsd.EssentialsD; +import cn.lunadeer.essentialsd.recipes.Crowbar; +import cn.lunadeer.essentialsd.utils.Notification; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.block.Block; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.Rail; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.bukkit.persistence.PersistentDataType; + +import java.util.Objects; + +public class CrowEvent implements Listener { + // change Rail's state with Crowbar + @EventHandler(priority = EventPriority.HIGHEST) + public void onCrowbarUse(PlayerInteractEvent event) { + Player player = event.getPlayer(); + ItemStack mainHand = player.getInventory().getItemInMainHand(); + if (event.getHand() == EquipmentSlot.OFF_HAND) { + return; + } + if (mainHand.getType() != Crowbar.getItemStack().getType()) { + return; + } + if (!Objects.requireNonNull(mainHand.getItemMeta()).getPersistentDataContainer().has(new NamespacedKey(EssentialsD.instance, "this_is_crowbar"), PersistentDataType.BYTE)) { + return; + } + if (event.getClickedBlock() == null) { + return; + } + Block block = event.getClickedBlock(); + if (block.getType() == Material.POWERED_RAIL || block.getType() == Material.DETECTOR_RAIL || block.getType() == Material.ACTIVATOR_RAIL) { + return; + } + BlockData blockData = block.getBlockData(); + if (!(blockData instanceof Rail)) { + return; + } + Rail rail = (Rail) blockData; + if (!Crowbar.changeable(rail.getShape())) { + Notification.warn(player, "无法使用撬棍修改此铁轨的方向"); + return; + } + rail.setShape(Crowbar.changeToNext(rail.getShape())); + block.setBlockData(rail); + event.setCancelled(true); + } +} diff --git a/src/main/java/cn/lunadeer/essentialsd/events/ExpBottleEvent.java b/src/main/java/cn/lunadeer/essentialsd/events/ExpBottleEvent.java new file mode 100644 index 0000000..7bb67d0 --- /dev/null +++ b/src/main/java/cn/lunadeer/essentialsd/events/ExpBottleEvent.java @@ -0,0 +1,18 @@ +package cn.lunadeer.essentialsd.events; + +import cn.lunadeer.essentialsd.EssentialsD; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; + +public class ExpBottleEvent implements Listener { + // on throw ExpBottle set multiplier for ExpBottle + @EventHandler(priority = EventPriority.HIGHEST) + public void onExpBottleUsage(org.bukkit.event.entity.ExpBottleEvent event) { + int exp = event.getExperience(); + if (exp < 0) { + event.setExperience(0); + } + event.setExperience((int) (exp * EssentialsD.config.getExpBottleRatio())); + } +} diff --git a/src/main/java/cn/lunadeer/essentialsd/Events.java b/src/main/java/cn/lunadeer/essentialsd/events/InvisibleItemFrameEvent.java similarity index 50% rename from src/main/java/cn/lunadeer/essentialsd/Events.java rename to src/main/java/cn/lunadeer/essentialsd/events/InvisibleItemFrameEvent.java index 4120bd1..5396120 100644 --- a/src/main/java/cn/lunadeer/essentialsd/Events.java +++ b/src/main/java/cn/lunadeer/essentialsd/events/InvisibleItemFrameEvent.java @@ -1,53 +1,23 @@ -package cn.lunadeer.essentialsd; +package cn.lunadeer.essentialsd.events; -import cn.lunadeer.essentialsd.commands.ShowItem; -import cn.lunadeer.essentialsd.recipes.Crowbar; +import cn.lunadeer.essentialsd.EssentialsD; import cn.lunadeer.essentialsd.recipes.InvisibleGlowItemFrame; import cn.lunadeer.essentialsd.recipes.InvisibleItemFrame; -import cn.lunadeer.essentialsd.utils.Notification; -import cn.lunadeer.essentialsd.utils.XLogger; -import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.NamespacedKey; -import org.bukkit.block.Block; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.Rail; import org.bukkit.entity.*; import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityDamageByEntityEvent; -import org.bukkit.event.entity.ExpBottleEvent; -import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.hanging.HangingBreakByEntityEvent; import org.bukkit.event.hanging.HangingPlaceEvent; -import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.player.PlayerInteractEntityEvent; -import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.event.player.PlayerTeleportEvent; -import org.bukkit.inventory.EquipmentSlot; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.InventoryView; import org.bukkit.inventory.ItemStack; import org.bukkit.persistence.PersistentDataType; import java.util.Objects; -public class Events implements Listener { - // cancel click ShowItem - @EventHandler - public void onShowItemClick(InventoryClickEvent event) { - Inventory inv = event.getClickedInventory(); - if (inv == null) { - return; - } - InventoryView view = event.getView(); - if (!ShowItem.cache.containsKey(view.getOriginalTitle())) { - return; - } - event.setCancelled(true); - } - +public class InvisibleItemFrameEvent implements Listener { // place an Invisible Item Frame @EventHandler public void placeItemFrame(HangingPlaceEvent event) { @@ -127,80 +97,4 @@ public class Events implements Listener { itemFrame.setVisible(true); } } - - // put stick as arms for an ArmorStand - @EventHandler - public void onAddArmsForArmStand(EntityDamageByEntityEvent event) { - Entity entity = event.getEntity(); - if (!(entity instanceof ArmorStand)) { - return; - } - if (!(event.getDamager() instanceof Player)) { - return; - } - Player player = (Player) event.getDamager(); - ItemStack mainHand = player.getInventory().getItemInMainHand(); - if (mainHand.getType() != Material.STICK) { - return; - } - event.setCancelled(true); - ArmorStand armorStand = (ArmorStand) entity; - if (armorStand.hasArms()) { - return; - } - armorStand.setArms(true); - mainHand.setAmount(mainHand.getAmount() - 1); - } - - // change Rail's state with Crowbar - @EventHandler(priority = EventPriority.HIGHEST) - public void onCrowbarUse(PlayerInteractEvent event) { - Player player = event.getPlayer(); - ItemStack mainHand = player.getInventory().getItemInMainHand(); - if (event.getHand() == EquipmentSlot.OFF_HAND) { - return; - } - if (mainHand.getType() != Crowbar.getItemStack().getType()) { - return; - } - if (!Objects.requireNonNull(mainHand.getItemMeta()).getPersistentDataContainer().has(new NamespacedKey(EssentialsD.instance, "this_is_crowbar"), PersistentDataType.BYTE)) { - return; - } - if (event.getClickedBlock() == null) { - return; - } - Block block = event.getClickedBlock(); - if (block.getType() == Material.POWERED_RAIL || block.getType() == Material.DETECTOR_RAIL || block.getType() == Material.ACTIVATOR_RAIL) { - return; - } - BlockData blockData = block.getBlockData(); - if (!(blockData instanceof Rail)) { - return; - } - Rail rail = (Rail) blockData; - if (!Crowbar.changeable(rail.getShape())) { - Notification.warn(player, "无法使用撬棍修改此铁轨的方向"); - return; - } - rail.setShape(Crowbar.changeToNext(rail.getShape())); - block.setBlockData(rail); - event.setCancelled(true); - } - - // on throw ExpBottle set multiplier for ExpBottle - @EventHandler(priority = EventPriority.HIGHEST) - public void onExpBottleUsage(ExpBottleEvent event) { - int exp = event.getExperience(); - if (exp < 0) { - event.setExperience(0); - } - event.setExperience((int) (exp * EssentialsD.config.getExpBottleRatio())); - } - - // on player death update tpManager - @EventHandler(priority = EventPriority.HIGHEST) - public void onPlayerDeath(PlayerDeathEvent event) { - Player player = event.getEntity(); - EssentialsD.tpManager.updateLastTpLocation(player); - } } diff --git a/src/main/java/cn/lunadeer/essentialsd/events/PlayerRecordEvent.java b/src/main/java/cn/lunadeer/essentialsd/events/PlayerRecordEvent.java new file mode 100644 index 0000000..5652ce3 --- /dev/null +++ b/src/main/java/cn/lunadeer/essentialsd/events/PlayerRecordEvent.java @@ -0,0 +1,33 @@ +package cn.lunadeer.essentialsd.events; + +import cn.lunadeer.essentialsd.dtos.LoginRecord; +import cn.lunadeer.essentialsd.dtos.NameRecord; +import cn.lunadeer.essentialsd.dtos.PlayerName; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerLoginEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +public class PlayerRecordEvent implements Listener { + + @EventHandler(priority = EventPriority.HIGHEST) + public void onPlayerLogin(PlayerLoginEvent event) { + Player player = event.getPlayer(); + String last_name = PlayerName.getName(player.getUniqueId()); + if (last_name == null) { + PlayerName.setName(player.getUniqueId(), player.getName()); + NameRecord.newNameRecord(player); + } else if (!last_name.equals(player.getName())) { + PlayerName.updateName(player.getUniqueId(), player.getName()); + NameRecord.newNameRecord(player); + } + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onPlayerLogout(PlayerQuitEvent event) { + Player player = event.getPlayer(); + LoginRecord.newLoginRecord(player); + } +} diff --git a/src/main/java/cn/lunadeer/essentialsd/events/ShowItemEvent.java b/src/main/java/cn/lunadeer/essentialsd/events/ShowItemEvent.java new file mode 100644 index 0000000..fd462d2 --- /dev/null +++ b/src/main/java/cn/lunadeer/essentialsd/events/ShowItemEvent.java @@ -0,0 +1,24 @@ +package cn.lunadeer.essentialsd.events; + +import cn.lunadeer.essentialsd.commands.ShowItem; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; + +public class ShowItemEvent implements Listener { + // cancel click ShowItem + @EventHandler + public void onShowItemClick(InventoryClickEvent event) { + Inventory inv = event.getClickedInventory(); + if (inv == null) { + return; + } + InventoryView view = event.getView(); + if (!ShowItem.cache.containsKey(view.getOriginalTitle())) { + return; + } + event.setCancelled(true); + } +} diff --git a/src/main/java/cn/lunadeer/essentialsd/events/TeleportEvent.java b/src/main/java/cn/lunadeer/essentialsd/events/TeleportEvent.java new file mode 100644 index 0000000..4d83879 --- /dev/null +++ b/src/main/java/cn/lunadeer/essentialsd/events/TeleportEvent.java @@ -0,0 +1,17 @@ +package cn.lunadeer.essentialsd.events; + +import cn.lunadeer.essentialsd.EssentialsD; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.PlayerDeathEvent; + +public class TeleportEvent implements Listener { + // on player death update tpManager + @EventHandler(priority = EventPriority.HIGHEST) + public void onPlayerDeath(PlayerDeathEvent event) { + Player player = event.getEntity(); + EssentialsD.tpManager.updateLastTpLocation(player); + } +} diff --git a/src/main/java/cn/lunadeer/essentialsd/managers/ConfigManager.java b/src/main/java/cn/lunadeer/essentialsd/managers/ConfigManager.java index 0b6c616..b3c88e4 100644 --- a/src/main/java/cn/lunadeer/essentialsd/managers/ConfigManager.java +++ b/src/main/java/cn/lunadeer/essentialsd/managers/ConfigManager.java @@ -33,10 +33,12 @@ public class ConfigManager { _commands_hat = _file.getBoolean("Commands.Hat", true); _commands_showitem = _file.getBoolean("Commands.ShowItem", true); _commands_skull = _file.getBoolean("Commands.Skull", true); + _commands_home = _file.getBoolean("Commands.Home", true); _tp_delay = _file.getInt("Teleport.Delay", 0); _tp_cool_down = _file.getInt("Teleport.CoolDown", 0); _tp_tpa_expire = _file.getInt("Teleport.TpaExpire", 30); - _tp_rtp_radius = _file.getInt("Teleport.RtpRadius", 10000); + _tp_rtp_radius = _file.getInt("Teleport.RtpRadius", 1000); + _tp_world_blacklist = _file.getStringList("Teleport.WorldBlackList"); _chair_enable = _file.getBoolean("Chair.Enable", true); _chair_max_width = _file.getInt("Chair.MaxWidth", 4); _chair_sign_check = _file.getBoolean("Chair.SignCheck", true); @@ -45,6 +47,8 @@ public class ConfigManager { _recipes_invisible_item_frame = _file.getBoolean("Recipes.InvisibleItemFrame", true); _recipes_light_block = _file.getBoolean("Recipes.LightBlock", true); _recipes_stacked_enchant_book = _file.getBoolean("Recipes.StackedEnchantBook", true); + _home_limit_amount = _file.getInt("HomeLimit.Amount", 5); + _home_world_blacklist = _file.getStringList("HomeLimit.WorldBlacklist"); _db_type = _file.getString("Database.Type", "sqlite"); if (!_db_type.equals("pgsql") && !_db_type.equals("sqlite")) { XLogger.err("当前数据库只支持 pgsql 或 sqlite,已重置为 sqlite"); @@ -107,6 +111,10 @@ public class ConfigManager { return _tp_rtp_radius; } + public List getTpWorldBlackList() { + return _tp_world_blacklist; + } + public Boolean getCommandsTpa() { return _commands_tpa; } @@ -139,6 +147,10 @@ public class ConfigManager { return _commands_skull; } + public Boolean getCommandsHome() { + return _commands_home; + } + public Boolean getChairEnable() { return _chair_enable; @@ -210,6 +222,14 @@ public class ConfigManager { }, _chunk_operate_delay * 20); } + public Integer getHomeLimitAmount() { + return _home_limit_amount; + } + + public List getHomeWorldBlacklist() { + return _home_world_blacklist; + } + public String getDbType() { return _db_type; } @@ -269,6 +289,7 @@ public class ConfigManager { private Integer _tp_delay; private Integer _tp_cool_down; private Integer _tp_rtp_radius; + private List _tp_world_blacklist; private Boolean _chair_enable; private Integer _chair_max_width; private Boolean _chair_sign_check; @@ -283,12 +304,17 @@ public class ConfigManager { private Boolean _commands_hat; private Boolean _commands_showitem; private Boolean _commands_skull; + private Boolean _commands_home; // recipes private Boolean _recipes_crowbar; private Boolean _recipes_invisible_item_frame; private Boolean _recipes_light_block; private Boolean _recipes_stacked_enchant_book; + // home limit + private Integer _home_limit_amount; + private List _home_world_blacklist; + // database private String _db_type; private String _db_host; diff --git a/src/main/java/cn/lunadeer/essentialsd/managers/DatabaseManager.java b/src/main/java/cn/lunadeer/essentialsd/managers/DatabaseManager.java index 9f7d11c..208e435 100644 --- a/src/main/java/cn/lunadeer/essentialsd/managers/DatabaseManager.java +++ b/src/main/java/cn/lunadeer/essentialsd/managers/DatabaseManager.java @@ -102,10 +102,11 @@ public class DatabaseManager { " id SERIAL PRIMARY KEY," + " uuid VARCHAR(36) NOT NULL," + " ip VARCHAR(15) NOT NULL," + - " name TEXT NOT NULL," + " login_time TIMESTAMP NOT NULL," + " logout_location TEXT," + - " logout_time TIMESTAMP" + + " logout_time TIMESTAMP," + + + " FOREIGN KEY (uuid) REFERENCES player_name(uuid) ON DELETE CASCADE" + ");"; query(sql); @@ -114,7 +115,9 @@ public class DatabaseManager { " id SERIAL PRIMARY KEY," + " uuid VARCHAR(36) NOT NULL," + " name TEXT NOT NULL," + - " time TIMESTAMP NOT NULL" + + " time TIMESTAMP NOT NULL," + + + " FOREIGN KEY (uuid) REFERENCES player_name(uuid) ON DELETE CASCADE" + ");"; query(sql); @@ -126,7 +129,9 @@ public class DatabaseManager { " to_location TEXT NOT NULL," + " type TEXT NOT NULL," + " success BOOLEAN NOT NULL," + - " time TIMESTAMP NOT NULL" + + " time TIMESTAMP NOT NULL," + + + " FOREIGN KEY (initiator_uuid) REFERENCES player_name(uuid) ON DELETE CASCADE" + ");"; query(sql); @@ -135,7 +140,9 @@ public class DatabaseManager { " id SERIAL PRIMARY KEY," + " sender_uuid VARCHAR(36) NOT NULL," + " message TEXT NOT NULL," + - " time TIMESTAMP NOT NULL" + + " time TIMESTAMP NOT NULL," + + + " FOREIGN KEY (sender_uuid) REFERENCES player_name(uuid) ON DELETE CASCADE" + ");"; query(sql); @@ -144,7 +151,9 @@ public class DatabaseManager { " id SERIAL PRIMARY KEY," + " executor_uuid VARCHAR(36) NOT NULL," + " command TEXT NOT NULL," + - " time TIMESTAMP NOT NULL" + + " time TIMESTAMP NOT NULL," + + + " FOREIGN KEY (executor_uuid) REFERENCES player_name(uuid) ON DELETE CASCADE" + ");"; query(sql); @@ -153,7 +162,9 @@ public class DatabaseManager { " id SERIAL PRIMARY KEY," + " uuid VARCHAR(36) NOT NULL," + " home_name TEXT NOT NULL," + - " location TEXT NOT NULL" + + " location TEXT NOT NULL," + + + " FOREIGN KEY (uuid) REFERENCES player_name(uuid) ON DELETE CASCADE" + ");"; query(sql); } diff --git a/src/main/java/cn/lunadeer/essentialsd/managers/TeleportManager.java b/src/main/java/cn/lunadeer/essentialsd/managers/TeleportManager.java index cfbe6e3..4dd3dd6 100644 --- a/src/main/java/cn/lunadeer/essentialsd/managers/TeleportManager.java +++ b/src/main/java/cn/lunadeer/essentialsd/managers/TeleportManager.java @@ -18,6 +18,7 @@ import org.bukkit.event.player.PlayerTeleportEvent; import java.time.LocalDateTime; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; public class TeleportManager { @@ -44,14 +45,9 @@ public class TeleportManager { return; } - LocalDateTime now = LocalDateTime.now(); - LocalDateTime next_time = _next_time_allow_tp.get(initiator.getUniqueId()); - if (next_time != null) { - if (now.isBefore(next_time)) { - long secs_until_next = now.until(next_time, java.time.temporal.ChronoUnit.SECONDS); - Notification.error(initiator, "请等待 " + secs_until_next + " 秒后再次传送"); - return; - } + if (EssentialsD.config.getTpWorldBlackList().contains(target.getWorld().getName())) { + Notification.error(initiator, "目的地所在世界 " + initiator.getWorld().getName() + " 不允许传送"); + return; } TpTask task = new TpTask(); @@ -70,7 +66,6 @@ public class TeleportManager { Notification.info(target, Component.text("| ", main_color).append(acceptBtn).append(Component.text(" ", main_color)).append(denyBtn)); Notification.info(target, Component.text("━━━━━━━━━━━━━━━━━━━━━━━━━━━━", main_color)); - _next_time_allow_tp.put(initiator.getUniqueId(), now.plusSeconds(EssentialsD.config.getTpCoolDown())); EssentialsD.scheduler.global.runDelayed(EssentialsD.instance, (instance) -> { _tasks.remove(task.taskId); }, 20L * EssentialsD.config.getTpTpaExpire()); @@ -110,17 +105,19 @@ public class TeleportManager { if (!task.initiator.isOnline() || !task.target.isOnline()) { return; } + Notification.info(task.target, "已接受 " + task.initiator.getName() + " 的传送请求"); - if (EssentialsD.config.getTpDelay() > 0) { - Notification.info(task.initiator, "玩家 " + task.target.getName() + " 已接受你的传送请求,传送将在 " + EssentialsD.config.getTpDelay() + " 秒后进行"); - } - EssentialsD.scheduler.global.runDelayed(EssentialsD.instance, (instance) -> { - if (task.initiator.isOnline() && task.target.isOnline()) { - doTeleportSafely(task.initiator, task.target.getLocation()); - Notification.info(task.initiator, "已传送到 " + task.target.getName() + " 的位置"); + Notification.info(task.initiator, "玩家 " + task.target.getName() + " 已接受你的传送请求"); + try { + doTeleportDelayed(task.initiator, task.target.getLocation(), EssentialsD.config.getTpDelay(), () -> { + Notification.info(task.initiator, "正在传送到 " + task.initiator.getName() + " 的位置"); + }, () -> { + Notification.info(task.initiator, "已传送到 " + task.initiator.getName() + " 的位置"); Notification.info(task.target, "玩家 " + task.initiator.getName() + " 已传送到你的位置"); - } - }, EssentialsD.config.getTpDelay() <= 0 ? 1 : 20L * EssentialsD.config.getTpDelay()); + }); + } catch (RuntimeException e) { + Notification.error(player, e.getMessage()); + } } public void back(Player player) { @@ -128,29 +125,68 @@ public class TeleportManager { Notification.error(player, "没有找到可返回的位置"); return; } + Location target = _last_tp_location.get(player.getUniqueId()); + if (EssentialsD.config.getTpWorldBlackList().contains(target.getWorld().getName())) { + Notification.error(player, "目的地所在世界 " + target.getWorld().getName() + " 不允许传送"); + return; + } if (EssentialsD.config.getTpDelay() > 0) { Notification.info(player, "将在 " + EssentialsD.config.getTpDelay() + " 秒后返回上次传送的位置"); } - EssentialsD.scheduler.global.runDelayed(EssentialsD.instance, (instance) -> { - doTeleportSafely(player, _last_tp_location.get(player.getUniqueId())); - Notification.info(player, "已返回上次传送的位置"); - }, EssentialsD.config.getTpDelay() <= 0 ? 1 : 20L * EssentialsD.config.getTpDelay()); + try { + doTeleportDelayed(player, target, EssentialsD.config.getTpDelay(), () -> { + Notification.info(player, "正在返回上次传送的位置"); + }, () -> { + Notification.info(player, "已返回上次传送的位置"); + }); + } catch (RuntimeException e) { + Notification.error(player, e.getMessage()); + } } public void rtp(Player player) { + if (EssentialsD.config.getTpWorldBlackList().contains(player.getWorld().getName())) { + Notification.error(player, "此世界 " + player.getWorld().getName() + " 不允许传送"); + return; + } int radius = EssentialsD.config.getTpRtpRadius(); World world = player.getWorld(); int x = (int) (Math.random() * radius * 2) - radius + (int) player.getLocation().getX(); int z = (int) (Math.random() * radius * 2) - radius + (int) player.getLocation().getZ(); XLogger.debug("RTP: " + x + " " + z); Location location = new Location(world, x + 0.5, player.getY(), z + 0.5); - if (EssentialsD.config.getTpDelay() > 0) { - Notification.info(player, "将在 " + EssentialsD.config.getTpDelay() + " 秒后传送到随机位置"); + try { + doTeleportDelayed(player, location, EssentialsD.config.getTpDelay(), () -> { + Notification.info(player, "正在传送到随机位置"); + }, () -> { + Notification.info(player, "已传送到随机位置"); + }); + } catch (RuntimeException e) { + Notification.error(player, e.getMessage()); } - EssentialsD.scheduler.global.runDelayed(EssentialsD.instance, (instance) -> { + } + + public void doTeleportDelayed(Player player, Location location, Integer delay, Runnable before, Runnable after) { + doTeleportDelayed(player, location, delay.longValue(), before, after); + } + + public void doTeleportDelayed(Player player, Location location, Long delay, Runnable before, Runnable after) throws RuntimeException { + if (EssentialsD.config.getTpWorldBlackList().contains(location.getWorld().getName())) { + Notification.error(player, "目的地所在世界 " + location.getWorld().getName() + " 不允许传送"); + return; + } + if (delay > 0) { + Notification.info(player, "将在 " + EssentialsD.config.getTpDelay() + " 秒后执行传送"); + EssentialsD.scheduler.global.runDelayed(EssentialsD.instance, (instance) -> { + before.run(); + doTeleportSafely(player, location); + after.run(); + }, 20L * delay); + } else { + before.run(); doTeleportSafely(player, location); - Notification.info(player, "已传送到随机位置"); - }, EssentialsD.config.getTpDelay() <= 0 ? 1 : 20L * EssentialsD.config.getTpDelay()); + after.run(); + } } /** @@ -159,16 +195,23 @@ public class TeleportManager { * @param player 玩家 * @param location 位置 */ - public void doTeleportSafely(Player player, Location location) { + public void doTeleportSafely(Player player, Location location) throws RuntimeException { + LocalDateTime now = LocalDateTime.now(); + LocalDateTime next_time = _next_time_allow_tp.get(player.getUniqueId()); + if (next_time != null) { + if (now.isBefore(next_time)) { + long secs_until_next = now.until(next_time, java.time.temporal.ChronoUnit.SECONDS); + throw new RuntimeException("请等待 " + secs_until_next + " 秒后再次传送"); + } + } + _next_time_allow_tp.put(player.getUniqueId(), now.plusSeconds(EssentialsD.config.getTpCoolDown())); location.getWorld().getChunkAtAsyncUrgently(location).thenAccept((chunk) -> { - updateLastTpLocation(player); int max_attempts = 512; while (location.getBlock().isPassable()) { location.setY(location.getY() - 1); max_attempts--; if (max_attempts <= 0) { - Notification.error(player, "传送目的地不安全,已取消传送"); - return; + throw new RuntimeException("传送目的地不安全,已取消传送"); } } Block up1 = location.getBlock().getRelative(BlockFace.UP); @@ -180,15 +223,14 @@ public class TeleportManager { up2 = up1.getRelative(BlockFace.UP); max_attempts--; if (max_attempts <= 0) { - Notification.error(player, "传送目的地不安全,已取消传送"); - return; + throw new RuntimeException("传送目的地不安全,已取消传送"); } } location.setY(location.getY() + 1); if (location.getBlock().getRelative(BlockFace.DOWN).getType() == Material.LAVA) { - Notification.error(player, "传送目的地不安全,已取消传送"); - return; + throw new RuntimeException("传送目的地不安全,已取消传送"); } + updateLastTpLocation(player); player.teleportAsync(location, PlayerTeleportEvent.TeleportCause.PLUGIN); }); } diff --git a/src/main/java/cn/lunadeer/essentialsd/tuis/HomeList.java b/src/main/java/cn/lunadeer/essentialsd/tuis/HomeList.java new file mode 100644 index 0000000..c9e0dba --- /dev/null +++ b/src/main/java/cn/lunadeer/essentialsd/tuis/HomeList.java @@ -0,0 +1,44 @@ +package cn.lunadeer.essentialsd.tuis; + +import cn.lunadeer.essentialsd.EssentialsD; +import cn.lunadeer.essentialsd.dtos.HomeInfo; +import cn.lunadeer.essentialsd.utils.Notification; +import cn.lunadeer.essentialsd.utils.STUI.Button; +import cn.lunadeer.essentialsd.utils.STUI.Line; +import cn.lunadeer.essentialsd.utils.STUI.ListView; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.List; + +public class HomeList { + public static void show(CommandSender sender, String[] args) { + if (!(sender instanceof Player)) { + Notification.warn(sender, "只有玩家可以使用此命令"); + return; + } + Player player = (Player) sender; + List homes = HomeInfo.getHomesOf(((Player) sender).getUniqueId()); + if (homes.size() == 0) { + Notification.warn(player, "你还没有设置家"); + return; + } + int page = 1; + if (args.length == 1) { + try { + page = Integer.parseInt(args[0]); + } catch (Exception ignored) { + } + } + ListView view = ListView.create(5, "/homes"); + view.title("Home 列表"); + for (HomeInfo home : homes) { + Line line = Line.create() + .append(home.homeName) + .append(Button.create("传送", "/home " + home.homeName)) + .append(Button.createRed("删除", "/delhome " + home.homeName + " " + page)); + view.add(line); + } + view.showOn(player, page); + } +} diff --git a/src/main/java/cn/lunadeer/essentialsd/utils/LocationUtils.java b/src/main/java/cn/lunadeer/essentialsd/utils/LocationUtils.java new file mode 100644 index 0000000..226d1a9 --- /dev/null +++ b/src/main/java/cn/lunadeer/essentialsd/utils/LocationUtils.java @@ -0,0 +1,21 @@ +package cn.lunadeer.essentialsd.utils; + +import org.bukkit.Location; +import org.bukkit.World; + +public class LocationUtils { + public static String Serialize(Location loc) { + return loc.getWorld().getName() + "," + loc.getX() + "," + loc.getY() + "," + loc.getZ() + "," + loc.getYaw() + "," + loc.getPitch(); + } + + public static Location Deserialize(String loc) { + String[] parts = loc.split(","); + World world = org.bukkit.Bukkit.getWorld(parts[0]); + double x = Double.parseDouble(parts[1]); + double y = Double.parseDouble(parts[2]); + double z = Double.parseDouble(parts[3]); + float yaw = Float.parseFloat(parts[4]); + float pitch = Float.parseFloat(parts[5]); + return new Location(world, x, y, z, yaw, pitch); + } +} diff --git a/src/main/java/cn/lunadeer/essentialsd/utils/STUI/Line.java b/src/main/java/cn/lunadeer/essentialsd/utils/STUI/Line.java index f183ac3..bc22430 100644 --- a/src/main/java/cn/lunadeer/essentialsd/utils/STUI/Line.java +++ b/src/main/java/cn/lunadeer/essentialsd/utils/STUI/Line.java @@ -8,14 +8,14 @@ import java.util.List; public class Line { + private String d = " - "; private final List elements = new ArrayList<>(); - private final TextComponent divider = Component.text(" - ", ViewStyles.sub_color); - public Line() { } public TextComponent build() { + TextComponent divider = Component.text(d, ViewStyles.sub_color); TextComponent.Builder builder = Component.text(); for (int i = 0; i < elements.size(); i++) { builder.append(elements.get(i)); @@ -30,11 +30,20 @@ public class Line { return new Line(); } + List getElements() { + return elements; + } + public Line append(TextComponent component) { elements.add(component); return this; } + public Line setDivider(String d) { + this.d = d; + return this; + } + public Line append(Component component) { elements.add(component); return this; @@ -45,4 +54,4 @@ public class Line { return this; } -} +} \ No newline at end of file diff --git a/src/main/java/cn/lunadeer/essentialsd/utils/STUI/ListView.java b/src/main/java/cn/lunadeer/essentialsd/utils/STUI/ListView.java index 3852f02..bc2d482 100644 --- a/src/main/java/cn/lunadeer/essentialsd/utils/STUI/ListView.java +++ b/src/main/java/cn/lunadeer/essentialsd/utils/STUI/ListView.java @@ -1,6 +1,8 @@ package cn.lunadeer.essentialsd.utils.STUI; import cn.lunadeer.essentialsd.utils.Notification; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextComponent; import org.bukkit.entity.Player; import java.util.ArrayList; @@ -13,7 +15,7 @@ public class ListView { private String command = ""; private final View view = View.create(); - public ListView(int page_size, String command) { + private ListView(int page_size, String command) { super(); this.page_size = page_size; this.command = command; @@ -39,6 +41,16 @@ public class ListView { return this; } + public ListView subtitle(TextComponent subtitle) { + view.subtitle(subtitle); + return this; + } + + public ListView subtitle(Line line) { + view.subtitle(line); + return this; + } + public ListView add(Line line) { lines.add(line); return this; @@ -61,11 +73,23 @@ public class ListView { } for (int i = offset; i < offset + page_size; i++) { if (i >= lines.size()) { + for (int j = 0; j < page_size - lines.size() % page_size; j++) { + view.addLine(Line.create()); + } break; } view.addLine(lines.get(i)); } - view.actionBar(Pagination.create(page, lines.size(), this.command)); + view.actionBar(Pagination.create(page, lines.size(), page_size, this.command)); view.showOn(player); } -} + + public ListView navigator(Line line) { + Line nav = Line.create().setDivider("->").append(Component.text("导航", ViewStyles.main_color)); + for (Component component : line.getElements()) { + nav.append(component); + } + view.subtitle(nav); + return this; + } +} \ No newline at end of file diff --git a/src/main/java/cn/lunadeer/essentialsd/utils/STUI/Pagination.java b/src/main/java/cn/lunadeer/essentialsd/utils/STUI/Pagination.java index 43cc6c0..8f9ca28 100644 --- a/src/main/java/cn/lunadeer/essentialsd/utils/STUI/Pagination.java +++ b/src/main/java/cn/lunadeer/essentialsd/utils/STUI/Pagination.java @@ -11,9 +11,8 @@ import static cn.lunadeer.essentialsd.utils.STUI.ViewStyles.sub_color; public class Pagination { - public static TextComponent create(int page, int item_size, String command) { + public static TextComponent create(int page, int item_size, int page_size, String command) { // 第 x/y 页 [上一页] [下一页] - int page_size = 4; int page_count = (int) Math.ceil((double) item_size / page_size); if (page_count == 0) { page_count = 1; @@ -26,9 +25,13 @@ public class Pagination { componentList.add(Component.text(" 页 ", main_color)); if (page > 1) { componentList.add(Button.create("上一页", command + " " + (page - 1))); + } else { + componentList.add(Component.text("[上一页]", sub_color)); } if (page < page_count) { componentList.add(Button.create("下一页", command + " " + (page + 1))); + } else { + componentList.add(Component.text("[下一页]", sub_color)); } TextComponent.Builder builder = Component.text(); for (Component component : componentList) { diff --git a/src/main/java/cn/lunadeer/essentialsd/utils/STUI/View.java b/src/main/java/cn/lunadeer/essentialsd/utils/STUI/View.java index 157387f..18c0a12 100644 --- a/src/main/java/cn/lunadeer/essentialsd/utils/STUI/View.java +++ b/src/main/java/cn/lunadeer/essentialsd/utils/STUI/View.java @@ -10,33 +10,25 @@ import java.util.List; import static cn.lunadeer.essentialsd.utils.STUI.ViewStyles.main_color; public class View { - protected TextComponent title_decorate = Component.text("━", main_color); protected TextComponent space = Component.text(" "); protected TextComponent sub_title_decorate = Component.text("- ", main_color); protected TextComponent line_decorate = Component.text("⌗ ", main_color); protected TextComponent action_decorate = Component.text("▸ ", main_color); protected TextComponent title = Component.text(" "); - protected TextComponent subtitle = Component.text(""); + protected TextComponent subtitle = null; protected List content_lines = new ArrayList<>(); - protected TextComponent actionbar = Component.text(" "); - protected TextComponent edge = Component.text("━━━━━━━━━━━━━━━━━━━━━━━━━━━━", main_color); - protected TextComponent divide_line = Component.text("━━━━━━━━━━━━━━━━━━━━━━━━━━━━", main_color); + protected TextComponent actionbar = null; + protected TextComponent edge = Component.text("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━", main_color); + protected TextComponent divide_line = Component.text("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━", main_color); public void showOn(Player player) { - player.sendMessage(edge); + // player.sendMessage(edge); TextComponent.Builder builder = Component.text(); - int title_length = title.content().length(); - int title_width = title_length * 2 + 2; - int decorate_count = divide_line.content().length() - title_width; - for (int i = 0; i < decorate_count / 2; i++) { - builder.append(title_decorate); - } + builder.append(Component.text("__/", main_color)); builder.append(space).append(title).append(space); - for (int i = 0; i < decorate_count / 2; i++) { - builder.append(title_decorate); - } + builder.append(Component.text("\\__", main_color)); player.sendMessage(builder.build()); - if (subtitle.content().length() > 0) { + if (subtitle != null) { player.sendMessage(divide_line); player.sendMessage(Component.text().append(sub_title_decorate).append(subtitle).build()); } @@ -44,8 +36,10 @@ public class View { for (TextComponent content_line : content_lines) { player.sendMessage(Component.text().append(line_decorate).append(content_line).build()); } - player.sendMessage(divide_line); - player.sendMessage(Component.text().append(action_decorate).append(actionbar).build()); + if (actionbar != null) { + player.sendMessage(divide_line); + player.sendMessage(Component.text().append(action_decorate).append(actionbar).build()); + } player.sendMessage(edge); player.sendMessage(Component.text(" ")); } @@ -69,6 +63,19 @@ public class View { return this; } + public View subtitle(Line line) { + this.subtitle = line.build(); + return this; + } + + public View navigator(Line line) { + Line nav = Line.create().setDivider("->").append(Component.text("导航", ViewStyles.main_color)); + for (Component component : line.getElements()) { + nav.append(component); + } + return this.subtitle(nav); + } + public View subtitle(TextComponent subtitle) { this.subtitle = subtitle; return this; @@ -103,4 +110,4 @@ public class View { this.content_lines.add(component.build()); return this; } -} +} \ No newline at end of file diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index c959429..77a00ac 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -37,13 +37,15 @@ Commands: Tpa: true # 传送到其他玩家 Rtp: true # 随机传送到附近 Back: true # 回到上一个传送位置 + Home: true # home相关指令 包含 sethome delhome home homes # 传送 Teleport: - Delay: 0 # 传送延迟 秒 - CoolDown: 0 # 冷却时间 秒 - TpaExpire: 30 # 传送请求有效期 秒 - RtpRadius: 1000 # 随机传送最大半径 + Delay: 0 # 传送延迟 秒 + CoolDown: 0 # 冷却时间 秒 + TpaExpire: 30 # 传送请求有效期 秒 + RtpRadius: 1000 # 随机传送最大半径 + WorldBlackList: [ ] # 不允许传送的世界 # 把楼梯当作椅子使用 Chair: @@ -52,6 +54,11 @@ Chair: SignCheck: false SitHeight: -0.95 +# home 相关限制 +HomeLimit: + Amount: 5 # 数量限制 + WorldBlackList: [ ] # 不允许设置 home 的世界 + Debug: false CheckUpdate: true \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index ab9471e..f17142d 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -63,4 +63,16 @@ commands: usage: /back inspect: description: 检查玩家背包 - usage: /inspect \ No newline at end of file + usage: /inspect + home: + description: 回家 + usage: /home [名称] + homes: + description: 查看家列表 + usage: /homes + sethome: + description: 设置家 + usage: /sethome [名称] + delhome: + description: 删除家 + usage: /delhome <名称> \ No newline at end of file