允许玩家在沙雕值为0时使用30点沙雕值惩罚换取指令之剑,引入ItemShowService用于在游戏中向其他玩家展示物品

This commit is contained in:
2024-12-26 01:50:19 +08:00
parent e0c1fee55f
commit 8b4e63be69
7 changed files with 343 additions and 16 deletions

View File

@@ -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<String> 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) {

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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) {

View File

@@ -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<Long, ItemData> 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<Map.Entry<Long, ItemData>> iterator = hashMap.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<Long, ItemData> 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;
}
}
}

View File

@@ -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);
}
}

View File

@@ -8,3 +8,5 @@ commands:
zb:
usage: "/zb <选项> <参数>"
description: "使用坐标管理器来记录坐标位置"
show_item_stack:
description: "查看其他玩家展示的物品"