From 8b4e63be69ac48d54344e23a945c0d18793970f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A9=BB=E9=AD=82=E5=9C=A3=E4=BD=BF?= Date: Thu, 26 Dec 2024 01:50:19 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=81=E8=AE=B8=E7=8E=A9=E5=AE=B6=E5=9C=A8?= =?UTF-8?q?=E6=B2=99=E9=9B=95=E5=80=BC=E4=B8=BA0=E6=97=B6=E4=BD=BF?= =?UTF-8?q?=E7=94=A830=E7=82=B9=E6=B2=99=E9=9B=95=E5=80=BC=E6=83=A9?= =?UTF-8?q?=E7=BD=9A=E6=8D=A2=E5=8F=96=E6=8C=87=E4=BB=A4=E4=B9=8B=E5=89=91?= =?UTF-8?q?=EF=BC=8C=E5=BC=95=E5=85=A5ItemShowService=E7=94=A8=E4=BA=8E?= =?UTF-8?q?=E5=9C=A8=E6=B8=B8=E6=88=8F=E4=B8=AD=E5=90=91=E5=85=B6=E4=BB=96?= =?UTF-8?q?=E7=8E=A9=E5=AE=B6=E5=B1=95=E7=A4=BA=E7=89=A9=E5=93=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Commands/ZbCommand.java | 53 +++++-- .../CoordinateRecorder.java | 13 +- .../Listener/CheatItemListener.java | 61 ++++++++ .../Listener/PlayerEventListener.java | 3 +- .../Service/ItemShowService.java | 144 ++++++++++++++++++ .../data/CheatItemsData.java | 81 ++++++++++ src/main/resources/plugin.yml | 4 +- 7 files changed, 343 insertions(+), 16 deletions(-) create mode 100644 src/main/java/ling/coordinateRecorder/Listener/CheatItemListener.java create mode 100644 src/main/java/ling/coordinateRecorder/Service/ItemShowService.java create mode 100644 src/main/java/ling/coordinateRecorder/data/CheatItemsData.java diff --git a/src/main/java/ling/coordinateRecorder/Commands/ZbCommand.java b/src/main/java/ling/coordinateRecorder/Commands/ZbCommand.java index b4f17ae..1588dec 100644 --- a/src/main/java/ling/coordinateRecorder/Commands/ZbCommand.java +++ b/src/main/java/ling/coordinateRecorder/Commands/ZbCommand.java @@ -1,11 +1,15 @@ package ling.coordinateRecorder.Commands; +import ling.coordinateRecorder.Config; import ling.coordinateRecorder.CoordinateRecorder; import ling.coordinateRecorder.Listener.PlayerMap; +import ling.coordinateRecorder.data.CheatItemsData; import ling.coordinateRecorder.data.PlayerData; import ling.coordinateRecorder.data.TransmitData; import ling.database.tables.records.LocationnotepadPO; import ling.database.tables.records.PlayersettingsPO; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.TextComponent; import org.bukkit.*; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; @@ -17,8 +21,8 @@ import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.BookMeta; import org.bukkit.inventory.meta.Damageable; -import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.Repairable; +import org.bukkit.persistence.PersistentDataContainer; import org.jetbrains.annotations.NotNull; import org.jooq.Record; import org.jooq.UpdateSetMoreStep; @@ -36,7 +40,8 @@ import static ling.database.tables.PlayersettingsTB.PLAYERSETTINGS; public class ZbCommand implements CommandExecutor, TabCompleter { protected final static List TAB_1 = Arrays.asList("add", "remove", "help", "reload", "list", "fixed", - "unfixed", "unlock", "sb", "tp", "yes", "no", "setting"); + "unfixed", "unlock", "sb", "tp", "yes", "no", "setting" + , "give"); public ZbCommand() { @@ -445,23 +450,51 @@ public class ZbCommand implements CommandExecutor, TabCompleter { } protected void giveSwordOfCommand(Player player) { - if (!player.isOp()) { - player.sendMessage(ChatColor.RED + "需要管理员权限!"); + PlayerData data = PlayerMap.getCurrent().getPlayerData(player); + if (data == null) { + player.sendMessage(ChatColor.RED + "玩家还没有登录!"); return; } + if (data.getFraction() != 0) { + player.sendMessage("召唤指令之剑需要沙雕值为0!"); + return; + } + + try { + data.addFraction(30); + PlayerMap.getCurrent().broadcastScore(data); + } catch (SQLException e) { + player.sendMessage(ChatColor.RED + "附加沙雕值惩罚失败:服务器内部错误"); + return; + } + + ItemStack sword = new ItemStack(Material.DIAMOND_SWORD, 1); - ItemMeta meta = sword.getItemMeta(); + Damageable meta = (Damageable) sword.getItemMeta(); assert meta != null; - meta.addEnchant(Enchantment.SHARPNESS, 32767, true); + meta.addEnchant(Enchantment.SHARPNESS, 40, true); + meta.addEnchant(Enchantment.SMITE, 40, true); + meta.addEnchant(Enchantment.BANE_OF_ARTHROPODS, 40, true); meta.addEnchant(Enchantment.VANISHING_CURSE, 1, true); meta.setDisplayName(ChatColor.YELLOW + "指令之剑"); - meta.setLore(Arrays.asList(ChatColor.YELLOW + "拥有无上权威的命令之剑", ChatColor.YELLOW + "仅限管理员使用!")); + meta.setLore(Arrays.asList(ChatColor.YELLOW + "蕴含管理员权柄的命令武器", + ChatColor.YELLOW + "由 " + player.getName() + " 召唤", + ChatColor.RED + "仅限玩家!")); + //设置修订次数,阻止玩家修复耐久,以及附加其他附魔 ((Repairable) meta).setRepairCost(100); - if (meta instanceof Damageable damageable) { - damageable.setDamage(sword.getType().getMaxDurability() - 1); - } + meta.setDamage(sword.getType().getMaxDurability() - 3); + + PersistentDataContainer per = meta.getPersistentDataContainer(); + CheatItemsData cheat = new CheatItemsData(player); + cheat.save(per); + sword.setItemMeta(meta); player.getInventory().addItem(sword); + + BaseComponent message = new TextComponent("玩家 " + player.getName() + " 召唤了"); + message.addExtra(CoordinateRecorder.getItemShowService().addItem(sword)); + message.addExtra(",受到" + Config.getFractionTimeMessage(30) + "点沙雕值惩罚!"); + Bukkit.spigot().broadcast(message); } protected boolean execute(Player player, String[] strings) { diff --git a/src/main/java/ling/coordinateRecorder/CoordinateRecorder.java b/src/main/java/ling/coordinateRecorder/CoordinateRecorder.java index e634f9e..c760980 100644 --- a/src/main/java/ling/coordinateRecorder/CoordinateRecorder.java +++ b/src/main/java/ling/coordinateRecorder/CoordinateRecorder.java @@ -1,10 +1,8 @@ package ling.coordinateRecorder; import ling.coordinateRecorder.Commands.ZbCommand; -import ling.coordinateRecorder.Listener.InterruptTransmit; -import ling.coordinateRecorder.Listener.PlayerAuthMeLoginEventListener; -import ling.coordinateRecorder.Listener.PlayerEventListener; -import ling.coordinateRecorder.Listener.PlayerLoginEventListener; +import ling.coordinateRecorder.Listener.*; +import ling.coordinateRecorder.Service.ItemShowService; import ling.coordinateRecorder.Service.ServiceManager; import ling.coordinateRecorder.Service.TransmitService; import ling.coordinateRecorder.data.Database; @@ -20,6 +18,7 @@ public final class CoordinateRecorder extends JavaPlugin { private static Database database; private static Plugin authMePlugin; private static TransmitService transmitService; + private static ItemShowService itemShowService; private static void start() throws SQLException { database.installPlugin(); @@ -28,6 +27,7 @@ public final class CoordinateRecorder extends JavaPlugin { private void loadListener() { Bukkit.getPluginManager().registerEvents(new PlayerEventListener(), this); Bukkit.getPluginManager().registerEvents(new InterruptTransmit(), this); + Bukkit.getPluginManager().registerEvents(new CheatItemListener(), this); if (authMePlugin != null && authMePlugin.isEnabled()) { getLogger().info("找到AuthMe插件,将使用AuthMe兼容接口"); Bukkit.getPluginManager().registerEvents(new PlayerAuthMeLoginEventListener(), this); @@ -52,6 +52,7 @@ public final class CoordinateRecorder extends JavaPlugin { command.setExecutor(zb); command.setTabCompleter(zb); transmitService = (TransmitService) ServiceManager.getCurrent().startService(TransmitService.class, 4L); + itemShowService = (ItemShowService) ServiceManager.getCurrent().startService(ItemShowService.class, 200L); getLogger().info("加载完毕"); } catch (SQLException e) { throw new RuntimeException("插件初始化失败", e); @@ -60,6 +61,10 @@ public final class CoordinateRecorder extends JavaPlugin { } } + public static ItemShowService getItemShowService() { + return itemShowService; + } + public static TransmitService getTransmitService() { return transmitService; } diff --git a/src/main/java/ling/coordinateRecorder/Listener/CheatItemListener.java b/src/main/java/ling/coordinateRecorder/Listener/CheatItemListener.java new file mode 100644 index 0000000..5f52f81 --- /dev/null +++ b/src/main/java/ling/coordinateRecorder/Listener/CheatItemListener.java @@ -0,0 +1,61 @@ +package ling.coordinateRecorder.Listener; + +import ling.coordinateRecorder.data.CheatItemsData; +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.ProjectileHitEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.persistence.PersistentDataContainer; + +/// 保护作弊物品,仅限召唤者使用 +public class CheatItemListener implements Listener { + + protected boolean onEvent(Player player, ItemStack item) { + ItemMeta meta = item.getItemMeta(); + if (meta == null) + return false; + PersistentDataContainer per = meta.getPersistentDataContainer(); + CheatItemsData data = CheatItemsData.getInstance(per); + if (data == null) + return false; + if (!data.getUuid().equals(player.getUniqueId().toString())) { + player.sendMessage(ChatColor.RED + "该物品只允许 " + data.getName() + " 使用"); + return true; + } + return false; + } + + @EventHandler(ignoreCancelled = true) + public void onEntityDamageByEntity(EntityDamageByEntityEvent event) { + if (event.getDamager() instanceof Player player) { + ItemStack weapon = player.getInventory().getItemInMainHand(); + if (onEvent(player, weapon)) { + event.setCancelled(true); + } + } + } + + @EventHandler(ignoreCancelled = true) + public void onProjectileHit(ProjectileHitEvent event) { + if (event.getHitEntity() != null && event.getHitEntity() instanceof Player player) { + ItemStack weapon = player.getInventory().getItemInMainHand(); + if (onEvent(player, weapon)) + event.setCancelled(true); + } + } + + @EventHandler(ignoreCancelled = true) + public void onBlockBreak(BlockBreakEvent event) { + Player player = event.getPlayer(); + ItemStack weapon = player.getInventory().getItemInMainHand(); + if (onEvent(player, weapon)) + event.setCancelled(true); + } + + +} diff --git a/src/main/java/ling/coordinateRecorder/Listener/PlayerEventListener.java b/src/main/java/ling/coordinateRecorder/Listener/PlayerEventListener.java index ef0b91c..e1e0800 100644 --- a/src/main/java/ling/coordinateRecorder/Listener/PlayerEventListener.java +++ b/src/main/java/ling/coordinateRecorder/Listener/PlayerEventListener.java @@ -15,6 +15,7 @@ import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockExplodeEvent; @@ -343,7 +344,7 @@ public class PlayerEventListener implements Listener { } /// 在玩家受到伤害时,记录伤害来源信息,用于击杀判定 - @EventHandler + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onPlayerDamageEvent(EntityDamageByEntityEvent event) { //仅处理玩家对玩家造成的伤害 if (event.getEntity() instanceof Player player && event.getDamager() instanceof Player from) { diff --git a/src/main/java/ling/coordinateRecorder/Service/ItemShowService.java b/src/main/java/ling/coordinateRecorder/Service/ItemShowService.java new file mode 100644 index 0000000..7f57f2c --- /dev/null +++ b/src/main/java/ling/coordinateRecorder/Service/ItemShowService.java @@ -0,0 +1,144 @@ +package ling.coordinateRecorder.Service; + +import ling.coordinateRecorder.CoordinateRecorder; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.HoverEvent; +import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.api.chat.hover.content.Text; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +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; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; + +/// 物品展示服务 +public class ItemShowService implements Service { + private final AtomicBoolean lockFlag = new AtomicBoolean(false); + private long nextIndex = 0; + /// 展示物品栈 + private final HashMap hashMap = new HashMap<>(); + public static final String TITLE = "物品展示框"; + + public ItemShowService() { + Bukkit.getPluginManager().registerEvents(new InventoryClickEventListener(), CoordinateRecorder.getCurrent()); + var command = Bukkit.getPluginCommand("show_item_stack"); + assert command != null; + command.setExecutor(new ShowItemCommand()); + } + + private void lock() { + while (!lockFlag.compareAndSet(false, true)) { + } + } + + private void unlock() { + lockFlag.set(false); + } + + public BaseComponent addItem(ItemStack item) { + assert item.getItemMeta() != null; + lock(); + long index = nextIndex++; + hashMap.put(index, new ItemData(item.clone())); + BaseComponent message = new TextComponent("[" + item.getItemMeta().getDisplayName() + "]"); + message.setColor(ChatColor.YELLOW); + message.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("点击查看物品详情"))); + message.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/show_item_stack " + index)); + unlock(); + return message; + } + + public void showItem(Player player, long index) { + lock(); + if (!hashMap.containsKey(index)) { + player.sendMessage(ChatColor.RED + "该物品已经过期!"); + unlock(); + return; + } + ItemStack item = hashMap.get(index).item; + unlock(); + + Inventory inv = Bukkit.createInventory(player, 9, TITLE); + //将要展示的物品放置在中间 + inv.setItem(4, item); + player.openInventory(inv); + } + + @Override + public void start() { + lock(); + Iterator> iterator = hashMap.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + var data = entry.getValue(); + /// 移除过期数据 + if (data.time + 60 * 30 * 1000 < System.currentTimeMillis()) { + iterator.remove(); + } + } + unlock(); + } + + private static class ItemData { + public final ItemStack item; + public final long time; + + public ItemData(ItemStack item) { + this.item = item; + time = System.currentTimeMillis(); + } + } + + /// 用于保护展示框内的物品不被玩家拿取 + private static class InventoryClickEventListener implements Listener { + + @EventHandler + public void onClick(InventoryClickEvent event) { + Player player = (Player) event.getWhoClicked(); + InventoryView inv = player.getOpenInventory(); + if (inv.getTitle().equals(TITLE)) { + event.setCancelled(true); + } + } + } + + /// 物品展示命令 + private static class ShowItemCommand implements CommandExecutor { + + @Override + public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, + @NotNull String s, @NotNull String[] strings) { + if (!(commandSender instanceof Player player)) { + commandSender.sendMessage(ChatColor.RED + "该命令只能由玩家执行"); + return false; + } + if (strings.length < 1) { + player.sendMessage(ChatColor.RED + "下标不存在"); + return true; + } + long index = 0; + try { + index = Long.parseLong(strings[0]); + } catch (NumberFormatException e) { + player.sendMessage(ChatColor.RED + "下标解析错误!"); + return true; + } + CoordinateRecorder.getItemShowService().showItem(player, index); + return true; + } + } +} diff --git a/src/main/java/ling/coordinateRecorder/data/CheatItemsData.java b/src/main/java/ling/coordinateRecorder/data/CheatItemsData.java new file mode 100644 index 0000000..1284cd5 --- /dev/null +++ b/src/main/java/ling/coordinateRecorder/data/CheatItemsData.java @@ -0,0 +1,81 @@ +package ling.coordinateRecorder.data; + +import ling.coordinateRecorder.CoordinateRecorder; +import org.bukkit.NamespacedKey; +import org.bukkit.entity.Player; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.persistence.PersistentDataType; + +import java.io.IOException; + +/// 作弊物品数据 +public class CheatItemsData implements PersistentData { + + private static final CoordinateRecorder current = CoordinateRecorder.getCurrent(); + /// 主人名称 + private static final NamespacedKey OWNER_NAME = new NamespacedKey(current, "CheatItemsOwnerName"); + /// 主人uid + private static final NamespacedKey OWNER_UUID = new NamespacedKey(current, "CheatItemsOwnerUUID"); + + protected String name; + protected String uuid; + + public CheatItemsData(Player player) { + this.name = player.getName(); + this.uuid = player.getUniqueId().toString(); + } + + protected CheatItemsData() { + + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public static CheatItemsData getInstance(PersistentDataContainer data) { + CheatItemsData curr = new CheatItemsData(); + try { + curr.load(data); + return curr; + } catch (IOException e) { + return null; + } + } + + @Override + public void save(PersistentDataContainer data) { + data.set(OWNER_NAME, PersistentDataType.STRING, name); + data.set(OWNER_UUID, PersistentDataType.STRING, uuid); + } + + public static boolean isCheatItem(PersistentDataContainer data) { + return data.has(OWNER_NAME) && data.has(OWNER_UUID); + } + + @Override + public void load(PersistentDataContainer data) throws IOException { + if (!isCheatItem(data)) + throw new IOException("没有有效数据!"); + this.name = data.get(OWNER_NAME, PersistentDataType.STRING); + this.uuid = data.get(OWNER_UUID, PersistentDataType.STRING); + } + + @Override + public void remove(PersistentDataContainer data) { + data.remove(OWNER_NAME); + data.remove(OWNER_UUID); + } +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 6f1f4b0..41ef186 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -7,4 +7,6 @@ softdepend: commands: zb: usage: "/zb <选项> <参数>" - description: "使用坐标管理器来记录坐标位置" \ No newline at end of file + description: "使用坐标管理器来记录坐标位置" + show_item_stack: + description: "查看其他玩家展示的物品" \ No newline at end of file