update all

This commit is contained in:
2025-11-02 19:29:09 +08:00
parent 3015ba5a7c
commit a375365d29
17 changed files with 2665 additions and 0 deletions

View File

@@ -0,0 +1,31 @@
package org.xgqy.survival;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.UUID;
// 实现Serializable接口并添加版本号关键修复
public class FriendData implements Serializable {
private static final long serialVersionUID = 1L; // 序列化版本号
private Map<UUID, List<UUID>> friends;
private Map<UUID, List<UUID>> waiting;
// getter和setter
public Map<UUID, List<UUID>> getFriends() {
return friends;
}
public void setFriends(Map<UUID, List<UUID>> friends) {
this.friends = friends;
}
public Map<UUID, List<UUID>> getWaiting() {
return waiting;
}
public void setWaiting(Map<UUID, List<UUID>> waiting) {
this.waiting = waiting;
}
}

View File

@@ -0,0 +1,486 @@
package org.xgqy.survival.command;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.ComponentBuilder;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import org.jetbrains.annotations.NotNull;
import org.xgqy.survival.FriendData;
import org.xgqy.survival.Survival;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
public class FriendCommandExecutor implements CommandExecutor {
private final Survival plugin;
// 好友请求超时任务缓存(使用字符串键确保精准匹配)
private final Map<String, BukkitRunnable> timeoutTasks = new ConcurrentHashMap<>();
// 待确认的删除请求
private final Map<Player, Player> pendingDelete = new ConcurrentHashMap<>();
// 删除确认超时任务缓存
private final Map<Player, BukkitRunnable> deleteTimeoutTasks = new ConcurrentHashMap<>();
// 持久化文件
private final File friendDataFile;
public FriendCommandExecutor(Survival plugin) {
this.plugin = plugin;
this.friendDataFile = new File(plugin.getDataFolder(), "friend_data.ser");
loadFriendData(); // 加载数据
}
// 提供外部访问任务的方法(用于插件禁用时清理)
public Map<String, BukkitRunnable> getTimeoutTasks() {
return timeoutTasks;
}
public Map<Player, BukkitRunnable> getDeleteTimeoutTasks() {
return deleteTimeoutTasks;
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (!(sender instanceof Player player)) {
sender.sendMessage(ChatColor.RED + "❌ 无法对非玩家类使用该命令!");
return true;
}
// 处理隐式命令(点击按钮触发)
if (args.length >= 3 && args[0].equals("confirmDelete")) {
handleConfirmDelete(player, args[1], args[2]);
return true;
}
if (args.length >= 2 && args[0].equals("prepareDelete")) {
handlePrepareDeleteCommand(player, args);
return true;
}
// 基础参数校验
if (args.length < 1) {
sendUsage(player);
return true;
}
String subCommand = args[0].toLowerCase();
if (subCommand.equals("list")) {
handleList(player);
return true;
}
if (args.length < 2) {
sendUsage(player);
return true;
}
String targetName = args[1];
Player target = Bukkit.getPlayerExact(targetName); // 精准匹配玩家名(修复)
// 目标玩家有效性校验
if (!subCommand.equals("remove") && (target == null || !target.isOnline())) {
player.sendMessage(ChatColor.RED + "❌ 目标玩家不存在或未在线!");
return true;
}
// 分发子命令
switch (subCommand) {
case "add" -> handleAdd(player, target);
case "remove" -> handleRemove(player, targetName);
case "allow" -> handleAllow(player, target);
case "deny" -> handleDeny(player, target);
default -> sendUsage(player);
}
return true;
}
/**
* 加载历史好友数据
*/
public void loadFriendData() {
if (!friendDataFile.exists()) {
plugin.getLogger().info("好友数据文件不存在,初始化空数据!");
return;
}
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(friendDataFile))) {
FriendData data = (FriendData) ois.readObject();
Map<UUID, List<UUID>> uuidFriends = data.getFriends();
Map<UUID, List<UUID>> uuidWaiting = data.getWaiting();
// 转换好友数据UUID -> Player在线玩家
for (Map.Entry<UUID, List<UUID>> entry : uuidFriends.entrySet()) {
Player player = Bukkit.getPlayer(entry.getKey());
if (player != null && player.isOnline()) {
List<Player> friends = entry.getValue().stream()
.map(Bukkit::getPlayer)
.filter(p -> p != null && p.isOnline()) // 只保留在线玩家
.collect(Collectors.toList());
plugin.friends.put(player, friends);
}
}
// 转换等待请求数据UUID -> Player在线玩家
for (Map.Entry<UUID, List<UUID>> entry : uuidWaiting.entrySet()) {
Player receiver = Bukkit.getPlayer(entry.getKey());
if (receiver != null && receiver.isOnline()) {
List<Player> requesters = entry.getValue().stream()
.map(Bukkit::getPlayer)
.filter(p -> p != null && p.isOnline()) // 只保留在线玩家
.collect(Collectors.toList());
plugin.waiting.put(receiver, requesters);
}
}
plugin.getLogger().info("好友数据加载成功!");
} catch (IOException e) {
plugin.getLogger().severe("好友数据文件读取失败:" + e.getMessage());
} catch (ClassNotFoundException e) {
plugin.getLogger().severe("好友数据模型类未找到:" + e.getMessage());
} catch (Exception e) {
plugin.getLogger().severe("好友数据加载异常:" + e.getMessage());
}
}
/**
* 保存好友数据(使用副本遍历避免并发异常)
*/
public void saveFriendData() {
FriendData data = new FriendData();
Map<UUID, List<UUID>> uuidFriends = new ConcurrentHashMap<>();
Map<UUID, List<UUID>> uuidWaiting = new ConcurrentHashMap<>();
// 遍历副本避免ConcurrentModificationException关键修复
new ArrayList<>(plugin.friends.entrySet()).forEach(entry -> {
UUID playerUuid = entry.getKey().getUniqueId();
List<UUID> friendUuids = entry.getValue().stream()
.map(Player::getUniqueId)
.collect(Collectors.toList());
uuidFriends.put(playerUuid, friendUuids);
});
new ArrayList<>(plugin.waiting.entrySet()).forEach(entry -> {
UUID receiverUuid = entry.getKey().getUniqueId();
List<UUID> requesterUuids = entry.getValue().stream()
.map(Player::getUniqueId)
.collect(Collectors.toList());
uuidWaiting.put(receiverUuid, requesterUuids);
});
data.setFriends(uuidFriends);
data.setWaiting(uuidWaiting);
try {
if (!friendDataFile.getParentFile().exists()) {
friendDataFile.getParentFile().mkdirs();
}
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(friendDataFile))) {
oos.writeObject(data);
}
} catch (IOException e) {
plugin.getLogger().severe("好友数据保存失败:" + e.getMessage());
}
}
/**
* 处理好友列表命令
*/
private void handleList(Player player) {
plugin.friends.computeIfAbsent(player, k -> new ArrayList<>());
List<Player> friends = plugin.friends.get(player);
if (friends.isEmpty()) {
player.sendMessage(ChatColor.RED + "📭 你的好友列表为空!使用 /friend add <玩家名> 添加好友");
return;
}
player.sendMessage(ChatColor.GREEN + "===== 你的好友列表(共" + friends.size() + "人)=====");
for (Player friend : friends) {
BaseComponent[] friendEntry = new ComponentBuilder()
.append(ChatColor.WHITE + "" + friend.getName())
.append(ChatColor.RED + " [删除]")
.event(new ClickEvent(
ClickEvent.Action.RUN_COMMAND,
"/friend prepareDelete " + friend.getName()
))
.create();
player.spigot().sendMessage(friendEntry);
}
player.sendMessage(ChatColor.GREEN + "======================================");
}
/**
* 准备删除好友(触发二次确认)
*/
private void handlePrepareDelete(Player player, String targetName) {
Player target = Bukkit.getPlayerExact(targetName);
plugin.friends.computeIfAbsent(player, k -> new ArrayList<>());
if (target == null || !plugin.friends.get(player).contains(target)) {
player.sendMessage(ChatColor.RED + "❌ 该玩家不在你的好友列表中!");
return;
}
if (pendingDelete.containsKey(player)) {
player.sendMessage(ChatColor.RED + "❌ 你有未完成的删除确认请求,请先处理!");
return;
}
pendingDelete.put(player, target);
player.sendMessage(ChatColor.YELLOW + "⚠️ 确认要删除好友 " + target.getName() + "10秒内有效");
BaseComponent[] confirmButtons = new ComponentBuilder()
.append(ChatColor.GREEN + "[确认删除] ")
.event(new ClickEvent(
ClickEvent.Action.RUN_COMMAND,
"/friend confirmDelete " + target.getName() + " yes"
))
.append(ChatColor.GRAY + "[取消]")
.event(new ClickEvent(
ClickEvent.Action.RUN_COMMAND,
"/friend confirmDelete " + target.getName() + " no"
))
.create();
player.spigot().sendMessage(confirmButtons);
// 设置超时任务
BukkitRunnable timeoutTask = new BukkitRunnable() {
@Override
public void run() {
if (pendingDelete.containsKey(player) && pendingDelete.get(player).equals(target)) {
pendingDelete.remove(player);
player.sendMessage(ChatColor.RED + "⌛ 删除好友请求已超时,自动取消!");
}
deleteTimeoutTasks.remove(player);
}
};
deleteTimeoutTasks.put(player, timeoutTask);
timeoutTask.runTaskLater(plugin, 20 * 10);
}
/**
* 处理删除确认/取消操作
*/
private void handleConfirmDelete(Player player, String targetName, String action) {
Player target = Bukkit.getPlayerExact(targetName);
if (!pendingDelete.containsKey(player) || !pendingDelete.get(player).equals(target)) {
player.sendMessage(ChatColor.RED + "❌ 无对应的删除确认请求!");
return;
}
if (deleteTimeoutTasks.containsKey(player)) {
deleteTimeoutTasks.get(player).cancel();
deleteTimeoutTasks.remove(player);
}
if (action.equalsIgnoreCase("yes")) {
handleRemove(player, targetName);
} else if (action.equalsIgnoreCase("no")) {
player.sendMessage(ChatColor.GREEN + "✅ 已取消删除好友 " + target.getName() + "");
}
pendingDelete.remove(player);
}
/**
* 处理添加好友命令(添加异常捕获)
*/
private void handleAdd(Player requester, Player target) {
try {
if (requester.equals(target)) {
requester.sendMessage(ChatColor.RED + "❌ 无法添加自己为好友!");
return;
}
plugin.friends.computeIfAbsent(requester, k -> new ArrayList<>());
plugin.friends.computeIfAbsent(target, k -> new ArrayList<>());
plugin.waiting.computeIfAbsent(target, k -> new ArrayList<>());
if (plugin.friends.get(requester).contains(target)) {
requester.sendMessage(ChatColor.RED + "" + target.getName() + " 已是你的好友!");
return;
}
if (plugin.waiting.get(target).contains(requester)) {
requester.sendMessage(ChatColor.RED + "⌛ 你已向 " + target.getName() + " 发送过好友请求,等待对方确认中!");
return;
}
if (plugin.waiting.get(requester).contains(target)) {
requester.sendMessage(ChatColor.YELLOW + "📩 " + target.getName() + " 已向你发送好友请求,请使用 /friend allow " + target.getName() + " 同意!");
return;
}
// 发送请求
plugin.waiting.get(target).add(requester);
requester.sendMessage(ChatColor.GREEN + "✅ 好友请求已发送给 " + target.getName() + "对方需在5分钟内确认");
target.sendMessage(ChatColor.AQUA + "📩 收到 " + requester.getName() + " 的好友请求!");
target.sendMessage(ChatColor.AQUA + "使用 /friend allow " + requester.getName() + " 同意,或 /friend deny " + requester.getName() + " 拒绝");
// 生成按钮
BaseComponent[] message = new ComponentBuilder()
.append(new ComponentBuilder(ChatColor.GREEN + "[同意] ").event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/friend allow " + requester.getName())).create())
.append(new ComponentBuilder(ChatColor.RED + "[拒绝]").event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/friend deny " + requester.getName())).create())
.create();
target.spigot().sendMessage(message);
// 超时任务(使用唯一键精准管理)
String taskKey = requester.getUniqueId() + "_" + target.getUniqueId();
BukkitRunnable timeoutTask = new BukkitRunnable() {
@Override
public void run() {
if (plugin.waiting.getOrDefault(target, new ArrayList<>()).contains(requester)) {
plugin.waiting.get(target).remove(requester);
requester.sendMessage(ChatColor.RED + "⌛ 向 " + target.getName() + " 发送的好友请求已超时5分钟");
target.sendMessage(ChatColor.RED + "" + requester.getName() + " 的好友请求已超时,已自动拒绝!");
saveFriendData();
}
timeoutTasks.remove(taskKey);
}
};
timeoutTasks.put(taskKey, timeoutTask);
timeoutTask.runTaskLater(plugin, 20 * 60 * 5);
saveFriendData();
} catch (Exception e) {
requester.sendMessage(ChatColor.RED + "❌ 添加好友失败!请联系管理员。");
plugin.getLogger().severe("添加好友异常(" + requester.getName() + " -> " + target.getName() + "" + e.getMessage());
e.printStackTrace();
}
}
/**
* 处理删除好友命令
*/
private void handleRemove(Player player, String targetName) {
Player target = Bukkit.getPlayerExact(targetName);
plugin.friends.computeIfAbsent(player, k -> new ArrayList<>());
if (target == null) {
boolean removed = false;
List<Player> friends = plugin.friends.get(player);
for (Player friend : new ArrayList<>(friends)) { // 遍历副本
if (friend.getName().equals(targetName)) {
friends.remove(friend);
plugin.friends.computeIfAbsent(friend, k -> new ArrayList<>()).remove(player);
removed = true;
break;
}
}
if (removed) {
player.sendMessage(ChatColor.GREEN + "✅ 已成功删除好友 " + targetName + "");
saveFriendData();
} else {
player.sendMessage(ChatColor.RED + "❌ 你未添加 " + targetName + " 为好友!");
}
return;
}
if (plugin.friends.get(player).contains(target)) {
plugin.friends.get(player).remove(target);
plugin.friends.computeIfAbsent(target, k -> new ArrayList<>()).remove(player);
player.sendMessage(ChatColor.GREEN + "✅ 已成功删除好友 " + target.getName() + "");
target.sendMessage(ChatColor.RED + "📢 " + player.getName() + " 已将你从好友列表中移除!");
saveFriendData();
} else {
player.sendMessage(ChatColor.RED + "❌ 你未添加 " + target.getName() + " 为好友!");
}
}
/**
* 处理同意好友请求命令(精准取消超时任务)
*/
private void handleAllow(Player receiver, Player requester) {
plugin.waiting.computeIfAbsent(receiver, k -> new ArrayList<>());
plugin.friends.computeIfAbsent(receiver, k -> new ArrayList<>());
plugin.friends.computeIfAbsent(requester, k -> new ArrayList<>());
if (!plugin.waiting.get(receiver).contains(requester)) {
receiver.sendMessage(ChatColor.RED + "❌ 未收到 " + requester.getName() + " 的好友请求!");
return;
}
// 双向添加好友
plugin.friends.get(receiver).add(requester);
plugin.friends.get(requester).add(receiver);
plugin.waiting.get(receiver).remove(requester);
// 精准取消超时任务(关键修复)
String taskKey = requester.getUniqueId() + "_" + receiver.getUniqueId();
BukkitRunnable task = timeoutTasks.get(taskKey);
if (task != null && !task.isCancelled()) {
task.cancel();
timeoutTasks.remove(taskKey);
}
receiver.sendMessage(ChatColor.GREEN + "🎉 已同意 " + requester.getName() + " 的好友请求,你们现在是好友啦!");
requester.sendMessage(ChatColor.GREEN + "🎉 " + receiver.getName() + " 已同意你的好友请求,你们现在是好友啦!");
saveFriendData();
}
/**
* 处理拒绝好友请求命令(精准取消超时任务)
*/
private void handleDeny(Player receiver, Player requester) {
plugin.waiting.computeIfAbsent(receiver, k -> new ArrayList<>());
if (!plugin.waiting.get(receiver).contains(requester)) {
receiver.sendMessage(ChatColor.RED + "❌ 未收到 " + requester.getName() + " 的好友请求!");
return;
}
plugin.waiting.get(receiver).remove(requester);
// 精准取消超时任务(关键修复)
String taskKey = requester.getUniqueId() + "_" + receiver.getUniqueId();
BukkitRunnable task = timeoutTasks.get(taskKey);
if (task != null && !task.isCancelled()) {
task.cancel();
timeoutTasks.remove(taskKey);
}
receiver.sendMessage(ChatColor.GRAY + "✅ 已拒绝 " + requester.getName() + " 的好友请求!");
requester.sendMessage(ChatColor.RED + "" + receiver.getName() + " 已拒绝你的好友请求!");
saveFriendData();
}
/**
* 发送命令用法提示
*/
private void sendUsage(Player player) {
player.sendMessage(ChatColor.GREEN + "===== 好友系统命令指南 =====");
player.sendMessage(ChatColor.YELLOW + "/friend add <玩家名> - 向指定玩家发送好友请求");
player.sendMessage(ChatColor.YELLOW + "/friend remove <玩家名> - 从好友列表中删除指定玩家");
player.sendMessage(ChatColor.YELLOW + "/friend allow <玩家名> - 同意指定玩家的好友请求");
player.sendMessage(ChatColor.YELLOW + "/friend deny <玩家名> - 拒绝指定玩家的好友请求");
player.sendMessage(ChatColor.YELLOW + "/friend list - 查看你的好友列表(支持快捷删除)");
player.sendMessage(ChatColor.GREEN + "==========================");
}
/**
* 处理prepareDelete隐式命令
*/
private void handlePrepareDeleteCommand(Player player, String[] args) {
if (args.length < 2) {
player.sendMessage(ChatColor.RED + "❌ 用法错误!正确格式:/friend prepareDelete <玩家名>");
return;
}
handlePrepareDelete(player, args[1]);
}
}

View File

@@ -0,0 +1,114 @@
package org.xgqy.survival.command;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.jetbrains.annotations.NotNull;
import org.xgqy.survival.Survival;
public class InventoryCommandExecutor implements CommandExecutor {
private final Survival plugin;
public InventoryCommandExecutor(Survival plugin) {
this.plugin = plugin;
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
// 检查命令发送者是否为玩家
if (!(sender instanceof Player)) {
sender.sendMessage(ChatColor.RED + "此命令只能由玩家执行!");
return true;
}
Player senderPlayer = (Player) sender;
// 检查权限OP
if (!senderPlayer.isOp()) {
sender.sendMessage(ChatColor.RED + "你没有权限使用此命令!");
return true;
}
// 检查参数是否正确
if (args.length < 1) {
sender.sendMessage(ChatColor.RED + "用法: /inventory <玩家名>");
return true;
}
// 获取目标玩家
Player targetPlayer = Bukkit.getPlayer(args[0]);
if (targetPlayer == null || !targetPlayer.isOnline()) {
sender.sendMessage(ChatColor.RED + "目标玩家不存在或未在线!");
return true;
}
// 创建显示用的GUI54格=6行×9列
Inventory gui = Bukkit.createInventory(
null,
54,
targetPlayer.getDisplayName() + ChatColor.WHITE + " 的背包"
);
PlayerInventory targetInv = targetPlayer.getInventory();
// 1. 填充主背包上3行0-26格
for (int i = 0; i < 27; i++) {
ItemStack item = targetInv.getItem(i);
gui.setItem(i, getItemOrBarrier(item));
}
// 2. 第4行分割线淡灰色玻璃板27-35格
ItemStack divider = new ItemStack(Material.LIGHT_GRAY_STAINED_GLASS_PANE);
for (int i = 27; i < 36; i++) {
gui.setItem(i, divider);
}
// 3. 第5行快捷栏36-44格对应玩家快捷栏27-35格
for (int i = 27; i < 36; i++) {
ItemStack item = targetInv.getItem(i);
// 快捷栏在GUI中偏移9格因为第4行占了9格
gui.setItem(i + 9, getItemOrBarrier(item));
}
// 4. 第6行装备和副手45-53格
// 装备槽对应关系:靴子(36)、护腿(37)、胸甲(38)、头盔(39)、副手(40)
ItemStack boots = targetInv.getItem(36);
ItemStack leggings = targetInv.getItem(37);
ItemStack chestplate = targetInv.getItem(38);
ItemStack helmet = targetInv.getItem(39);
ItemStack offhand = targetInv.getItem(40);
// 放置装备(按常规装备顺序排列)
gui.setItem(45, getItemOrBarrier(boots)); // 靴子
gui.setItem(46, getItemOrBarrier(leggings)); // 护腿
gui.setItem(47, getItemOrBarrier(chestplate)); // 胸甲
gui.setItem(48, getItemOrBarrier(helmet)); // 头盔
gui.setItem(52, getItemOrBarrier(offhand)); // 副手
// 第6行剩余位置用屏障填充
for (int i = 49; i < 52; i++) {
gui.setItem(i, new ItemStack(Material.BARRIER));
}
gui.setItem(53, new ItemStack(Material.BARRIER));
// 打开GUI给命令发送者
senderPlayer.openInventory(gui);
return true;
}
/**
* 工具方法:如果物品为空则返回屏障,否则返回原物品
*/
private ItemStack getItemOrBarrier(ItemStack item) {
return (item == null || item.getType() == Material.AIR)
? new ItemStack(Material.AIR)
: item;
}
}

View File

@@ -0,0 +1,29 @@
package org.xgqy.survival.command;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.xgqy.survival.Survival;
public class KillCommandExecutor implements CommandExecutor {
private Survival plugin;
public KillCommandExecutor(Survival plugin) {
this.plugin = plugin;
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (sender instanceof Player) {
((Player) sender).damage(11451.0f);
return true;
} else {
sender.sendMessage(ChatColor.RED + "无法对非玩家类使用");
return true;
}
}
}

View File

@@ -0,0 +1,237 @@
package org.xgqy.survival.command;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.xgqy.survival.Survival;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
public class LandCommandExecutor implements CommandExecutor {
private final Survival plugin;
// 临时存储玩家正在创建的领地
private final Map<Player, Survival.Land> tempLands = new HashMap<>();
public LandCommandExecutor(Survival plugin) {
this.plugin = plugin;
}
// 判断位置是否在x-z区域内
public static boolean isLocationBetween(Location target, Location a, Location b) {
if (target == null || a == null || b == null) return false;
if (!Objects.equals(target.getWorld(), a.getWorld()) || !Objects.equals(target.getWorld(), b.getWorld())) {
return false;
}
double minX = Math.min(a.getX(), b.getX());
double maxX = Math.max(a.getX(), b.getX());
double minZ = Math.min(a.getZ(), b.getZ());
double maxZ = Math.max(a.getZ(), b.getZ());
return target.getX() >= minX && target.getX() <= maxX && target.getZ() >= minZ && target.getZ() <= maxZ;
}
// 判断两个x-z区域是否重叠
public static boolean isRegionsTouching(Location loc1, Location loc2, Location loc3, Location loc4) {
if (loc1 == null || loc2 == null || loc3 == null || loc4 == null) return false;
if (!Objects.equals(loc1.getWorld(), loc3.getWorld())) return false;
double minX1 = Math.min(loc1.getX(), loc2.getX());
double maxX1 = Math.max(loc1.getX(), loc2.getX());
double minZ1 = Math.min(loc1.getZ(), loc2.getZ());
double maxZ1 = Math.max(loc1.getZ(), loc2.getZ());
double minX2 = Math.min(loc3.getX(), loc4.getX());
double maxX2 = Math.max(loc3.getX(), loc4.getX());
double minZ2 = Math.min(loc3.getZ(), loc4.getZ());
double maxZ2 = Math.max(loc3.getZ(), loc4.getZ());
return maxX1 >= minX2 && maxX2 >= minX1 && maxZ1 >= minZ2 && maxZ2 >= minZ1;
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (!(sender instanceof Player)) {
sender.sendMessage(ChatColor.RED + "仅玩家可使用领地命令!");
return true;
}
Player player = (Player) sender;
if (args.length == 0) {
sendHelp(player);
return true;
}
switch (args[0].toLowerCase()) {
case "set1":
handleSet1(player);
break;
case "set2":
handleSet2(player);
break;
case "allow":
handleAllowDisallow(player, args, true);
break;
case "disallow":
handleAllowDisallow(player, args, false);
break;
case "reset":
handleReset(player);
break;
case "protect":
handleProtect(player);
break;
default:
sender.sendMessage(ChatColor.RED + "未知命令!输入 /land 查看帮助。");
}
return true;
}
// 设置领地起点
private void handleSet1(Player player) {
if (tempLands.containsKey(player)) {
Survival.Land temp = tempLands.get(player);
if (temp.getStart() != null) {
player.sendMessage(ChatColor.RED + "已设置过起点!使用 /land reset 重置。");
return;
}
} else {
tempLands.put(player, new Survival.Land(null, null, new ArrayList<>(), player.getName(), new HashMap<>()));
}
Survival.Land temp = tempLands.get(player);
temp.setStart(player.getLocation());
player.sendMessage(ChatColor.GREEN + "已设置领地起点!使用 /land set2 设置终点。");
}
// 设置领地终点(完成创建)
private void handleSet2(Player player) {
if (!tempLands.containsKey(player)) {
player.sendMessage(ChatColor.RED + "请先使用 /land set1 设置起点!");
return;
}
Survival.Land temp = tempLands.get(player);
if (temp.getStart() == null) {
player.sendMessage(ChatColor.RED + "请先设置起点!");
return;
}
if (temp.getEnd() != null) {
player.sendMessage(ChatColor.RED + "已设置过终点!使用 /land reset 重置。");
return;
}
Location end = player.getLocation();
temp.setEnd(end);
Location start = temp.getStart();
// 检查是否包含出生点
Location spawn = player.getRespawnLocation() != null ? player.getRespawnLocation() : player.getWorld().getSpawnLocation();
if (!isLocationBetween(spawn, start, end)) {
tempLands.remove(player);
player.sendMessage(ChatColor.RED + "领地必须包含你的出生点!");
return;
}
// 检查与其他领地重叠
for (Survival.Land land : plugin.land) {
if (land.getOwner() == null) continue;
if (land.getOwner().equals(player.getName())) continue; // 跳过自己的领地
if (isRegionsTouching(start, end, land.getStart(), land.getEnd())) {
tempLands.remove(player);
player.sendMessage(ChatColor.RED + "与其他玩家领地重叠!");
return;
}
}
// 初始化默认权限7项破坏、放置、飞行、交互、使用方块、TNT爆炸、玩家伤害
List<Boolean> defaultPerm = new ArrayList<>();
for (int i = 0; i < 7; i++) {
defaultPerm.add(false); // 默认禁止所有权限
}
temp.setDefaultperm(defaultPerm);
// 添加到正式列表
plugin.land.add(temp);
tempLands.remove(player);
player.sendMessage(ChatColor.GREEN + "领地创建成功!");
}
// 允许/禁止玩家权限
private void handleAllowDisallow(Player player, String[] args, boolean allow) {
if (args.length < 3) {
player.sendMessage(ChatColor.RED + "用法:/land " + (allow ? "allow" : "disallow") + " <玩家名> <1-7>");
player.sendMessage(ChatColor.GRAY + "权限索引1=破坏 2=放置 3=飞行 4=交互 5=使用方块 6=TNT爆炸 7=玩家伤害");
return;
}
String targetName = args[1];
int permIndex;
try {
permIndex = Integer.parseInt(args[2]) - 1; // 转为0基索引
if (permIndex < 0 || permIndex > 6) throw new NumberFormatException();
} catch (NumberFormatException e) {
player.sendMessage(ChatColor.RED + "权限索引必须是1-7");
return;
}
// 获取玩家自己的领地
Survival.Land land = getPlayerLand(player);
if (land == null) {
player.sendMessage(ChatColor.RED + "你没有领地!");
return;
}
// 设置权限
Map<String, List<Boolean>> permMap = land.perm;
List<Boolean> targetPerm = permMap.getOrDefault(targetName, new ArrayList<>(land.getDefaultperm()));
while (targetPerm.size() < 7) targetPerm.add(false); // 确保长度
targetPerm.set(permIndex, allow);
permMap.put(targetName, targetPerm);
player.sendMessage(ChatColor.GREEN + "" + (allow ? "允许" : "禁止") + "玩家 " + targetName + "" + getPermName(permIndex) + " 权限!");
}
// 重置领地设置
private void handleReset(Player player) {
tempLands.remove(player);
plugin.land.remove(player);
player.sendMessage(ChatColor.GREEN + "领地设置已重置!");
}
// 开关领地保护示例控制TNT爆炸
private void handleProtect(Player player) {
}
private Survival.Land getPlayerLand(Player player) {
for (Survival.Land land : plugin.land) {
if (land.getOwner() != null && land.getOwner().equals(player.getName())) {
return land;
}
}
return null;
}
// 辅助:获取权限名称
private String getPermName(int index) {
String[] names = {"破坏", "放置", "飞行", "交互", "使用方块", "TNT爆炸", "玩家伤害"};
return names[index];
}
// 发送帮助信息
private void sendHelp(Player player) {
player.sendMessage(ChatColor.YELLOW + "===== 领地命令 =====");
player.sendMessage(ChatColor.GOLD + "/land set1" + ChatColor.WHITE + " - 设置领地起点");
player.sendMessage(ChatColor.GOLD + "/land set2" + ChatColor.WHITE + " - 设置领地终点(完成创建)");
player.sendMessage(ChatColor.GOLD + "/land allow <玩家> <1-7>" + ChatColor.WHITE + " - 允许玩家权限");
player.sendMessage(ChatColor.GOLD + "/land disallow <玩家> <1-7>" + ChatColor.WHITE + " - 禁止玩家权限");
player.sendMessage(ChatColor.GOLD + "/land reset" + ChatColor.WHITE + " - 重置领地设置");
player.sendMessage(ChatColor.GOLD + "/land protect" + ChatColor.WHITE + " - 开关超级保护(测试中)");
}
}

View File

@@ -0,0 +1,54 @@
package org.xgqy.survival.command;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.xgqy.survival.Survival;
public class LoginCommandExecutor implements CommandExecutor {
private final Survival plugin;
public LoginCommandExecutor(Survival plugin) {
this.plugin = plugin;
}
@Override
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
if (!(sender instanceof Player)) {
sender.sendMessage(ChatColor.RED + "只有玩家可以使用此命令!");
return true;
}
Player player = (Player) sender;
// 检查玩家是否已登录
if (plugin.getAccountManager().isLoggedIn(player)) {
player.sendMessage(ChatColor.RED + "你已经登录了!");
return true;
}
// 检查参数是否正确
if (args.length != 1) {
player.sendMessage(ChatColor.RED + "用法: /login <密码>");
return true;
}
// 检查玩家是否已注册
if (!plugin.getAccountManager().isRegistered(player.getUniqueId())) {
player.sendMessage(ChatColor.RED + "你还没有注册! 请使用 /reg <密码> <确认密码> 注册");
return true;
}
// 尝试登录
if (plugin.getAccountManager().login(player, args[0])) {
player.sendMessage(ChatColor.GREEN + "登录成功! 欢迎回来," + player.getName() + "!");
return true;
} else {
player.sendMessage(ChatColor.RED + "登录失败! 密码不正确");
player.kickPlayer(ChatColor.RED+"密码错误!");
return false;
}
}
}

View File

@@ -0,0 +1,386 @@
package org.xgqy.survival.command;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.ComponentBuilder;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.xgqy.survival.Survival;
import java.util.ArrayList;
import java.util.List;
public class PartyCommandExecutor implements CommandExecutor {
private final Survival plugin;
public PartyCommandExecutor(Survival plugin) {
this.plugin = plugin;
}
private void colorfulline(Player player) {
player.sendMessage(
ChatColor.DARK_RED + "---" +
ChatColor.RED + "---" +
ChatColor.GREEN + "---" +
ChatColor.BLUE + "---" +
ChatColor.YELLOW + "---" +
ChatColor.LIGHT_PURPLE + "---" +
ChatColor.AQUA + "---" +
ChatColor.LIGHT_PURPLE + "---" +
ChatColor.DARK_PURPLE + "---"
);
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (!(sender instanceof Player)) {
sender.sendMessage(ChatColor.RED + "无法对非玩家类使用");
return true;
}
Player send = (Player) sender;
if (args.length < 1) {
sender.sendMessage(ChatColor.RED + "参数不足!请输入 /party help 查看命令用法");
return true;
}
switch (args[0].toLowerCase()) {
case "create":
handleCreate(send);
break;
case "join":
handleJoin(send, args);
break;
case "quit":
handleQuit(send);
break;
case "list":
handleList(send);
break;
case "kick":
handleKick(send, args);
break;
case "ban":
handleBan(send, args);
break;
case "disband":
handleDisband(send);
break;
case "help":
handleHelp(send);
break;
case "check":
send.sendMessage(ChatColor.RED + "无权使用该命令!");
break;
default:
sender.sendMessage(ChatColor.RED + "参数错误!请输入 /party help 查看命令用法");
}
return true;
}
// 创建队伍
private void handleCreate(Player player) {
if (plugin.party.getOrDefault(player, null) != null) {
player.sendMessage(ChatColor.RED + "您已经加入了一个队伍!");
return;
}
colorfulline(player);
int pid = (int) (Math.random() * 89999 + 10000);
player.sendMessage(ChatColor.GREEN + "队伍创建成功,队伍编号: " + pid);
plugin.party.put(player, pid);
List<Player> playerList = new ArrayList<>();
playerList.add(player);
plugin.partyp.put(pid, playerList);
plugin.owner.put(pid, player);
colorfulline(player);
}
// 加入队伍
private void handleJoin(Player player, String[] args) {
// 检查参数长度
if (args.length < 2) {
player.sendMessage(ChatColor.RED + "用法错误!正确格式:/party join <队伍号>");
return;
}
// 检查是否已加入队伍
if (plugin.party.getOrDefault(player, null) != null) {
player.sendMessage(ChatColor.RED + "您已经加入了一个队伍!");
return;
}
// 解析队伍ID处理非数字输入
int pid;
try {
pid = Integer.parseInt(args[1]);
} catch (NumberFormatException e) {
player.sendMessage(ChatColor.RED + "队伍编号必须是数字!");
return;
}
// 检查队伍是否存在
if (plugin.partyp.getOrDefault(pid, null) == null) {
player.sendMessage(ChatColor.RED + "队伍不存在!");
return;
}
// 检查是否被ban
List<Player> bannedPlayers = plugin.ban.getOrDefault(pid, new ArrayList<>());
if (bannedPlayers.contains(player)) {
player.sendMessage(ChatColor.RED + "你已被该队伍禁止加入!");
return;
}
// 执行加入逻辑
colorfulline(player);
player.sendMessage(ChatColor.GREEN + "加入队伍成功!");
colorfulline(player);
List<Player> teamPlayers = plugin.partyp.get(pid);
// 通知队伍成员
for (Player member : teamPlayers) {
colorfulline(member);
member.sendMessage(player.getDisplayName() + ChatColor.GREEN + " 加入了队伍");
colorfulline(member);
}
// 添加到队伍
teamPlayers.add(player);
plugin.partyp.put(pid, teamPlayers);
plugin.party.put(player, pid);
}
// 退出队伍
private void handleQuit(Player player) {
Integer pid = plugin.party.getOrDefault(player, null);
if (pid == null) {
player.sendMessage(ChatColor.RED + "您尚未加入队伍!");
return;
}
colorfulline(player);
player.sendMessage(ChatColor.RED + "退出队伍成功!");
colorfulline(player);
List<Player> teamPlayers = plugin.partyp.get(pid);
// 通知队伍成员
for (Player member : teamPlayers) {
colorfulline(member);
member.sendMessage(player.getDisplayName() + ChatColor.RED + " 退出了队伍");
colorfulline(member);
}
// 移除玩家
teamPlayers.remove(player);
// 如果是队长退出,解散队伍
if (plugin.owner.getOrDefault(pid, null).equals(player)) {
for (Player member : teamPlayers) {
colorfulline(member);
member.sendMessage(ChatColor.RED + "队伍已解散");
colorfulline(member);
plugin.party.remove(member);
}
plugin.owner.remove(pid);
plugin.partyp.remove(pid);
plugin.ban.remove(pid);
} else {
plugin.partyp.put(pid, teamPlayers);
}
plugin.party.remove(player);
}
// 队伍列表
private void handleList(Player player) {
Integer pid = plugin.party.getOrDefault(player, null);
if (pid == null) {
player.sendMessage(ChatColor.RED + "您还未加入队伍");
return;
}
colorfulline(player);
List<Player> teamPlayers = plugin.partyp.get(pid);
boolean isOwner = plugin.owner.getOrDefault(pid, null).equals(player);
for (Player member : teamPlayers) {
ComponentBuilder builder = new ComponentBuilder(member.getDisplayName());
// 公共传送按钮
builder.append(ChatColor.GREEN + " [传送到] ")
.event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/teleport " + member.getName()));
builder.append(ChatColor.GREEN + "[传送来] ")
.event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/teleport " + member.getName() + " me"));
// 队长额外的踢出按钮
if (isOwner) {
builder.append(ChatColor.RED + "[踢出]")
.event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/party kick " + member.getName()));
}
player.spigot().sendMessage(builder.create());
}
colorfulline(player);
}
// 踢出玩家
private void handleKick(Player sender, String[] args) {
// 检查参数长度
if (args.length < 2) {
sender.sendMessage(ChatColor.RED + "用法错误!正确格式:/party kick <玩家名>");
return;
}
Integer pid = plugin.party.getOrDefault(sender, null);
if (pid == null) {
sender.sendMessage(ChatColor.RED + "您还未加入队伍");
return;
}
// 检查是否为队长
if (!plugin.owner.getOrDefault(pid, null).equals(sender)) {
sender.sendMessage(ChatColor.RED + "你无权使用该命令!");
return;
}
// 检查目标玩家
Player target = Bukkit.getPlayer(args[1]);
if (target == null || !target.isOnline()) {
sender.sendMessage(ChatColor.RED + "玩家不在线或不存在!");
return;
}
List<Player> teamPlayers = plugin.partyp.get(pid);
// 修正:判断目标是否在队伍内(原逻辑颠倒)
if (!teamPlayers.contains(target)) {
sender.sendMessage(ChatColor.RED + "队伍内无该玩家!");
return;
}
// 执行踢出逻辑
colorfulline(sender);
// 通知被踢出玩家
colorfulline(target);
target.sendMessage(ChatColor.RED + "您已被踢出队伍!");
colorfulline(target);
// 更新数据
plugin.party.remove(target);
teamPlayers.remove(target);
plugin.partyp.put(pid, teamPlayers);
// 通知其他成员
for (Player member : teamPlayers) {
colorfulline(member);
member.sendMessage(target.getDisplayName() + ChatColor.RED + " 已被踢出队伍");
colorfulline(member);
}
colorfulline(sender);
}
// 禁止玩家加入
private void handleBan(Player sender, String[] args) {
// 检查参数长度
if (args.length < 2) {
sender.sendMessage(ChatColor.RED + "用法错误!正确格式:/party ban <玩家名>");
return;
}
Integer pid = plugin.party.getOrDefault(sender, null);
if (pid == null) {
sender.sendMessage(ChatColor.RED + "您还未加入队伍");
return;
}
// 检查是否为队长
if (!plugin.owner.getOrDefault(pid, null).equals(sender)) {
sender.sendMessage(ChatColor.RED + "你无权使用该命令!");
return;
}
// 检查目标玩家
Player target = Bukkit.getPlayer(args[1]);
if (target == null || !target.isOnline()) {
sender.sendMessage(ChatColor.RED + "玩家不在线或不存在!");
return;
}
List<Player> teamPlayers = plugin.partyp.get(pid);
// 修正:判断目标是否在队伍内(原逻辑颠倒)
if (teamPlayers.contains(target)) {
sender.sendMessage(ChatColor.RED + "无法禁止队伍内的玩家请先使用kick命令踢出");
return;
}
// 初始化ban列表避免空指针
List<Player> bannedPlayers = plugin.ban.getOrDefault(pid, new ArrayList<>());
if (bannedPlayers.contains(target)) {
sender.sendMessage(ChatColor.RED + "该玩家已被禁止加入!");
return;
}
// 执行ban逻辑
colorfulline(sender);
sender.sendMessage(ChatColor.GREEN + "成功禁止 " + target.getDisplayName() + " 加入队伍!");
colorfulline(sender);
// 通知目标玩家
colorfulline(target);
target.sendMessage(ChatColor.RED + "您已被 " + sender.getDisplayName() + " 的队伍禁止加入!");
colorfulline(target);
// 更新ban列表
bannedPlayers.add(target);
plugin.ban.put(pid, bannedPlayers);
}
// 解散队伍
private void handleDisband(Player sender) {
Integer pid = plugin.party.getOrDefault(sender, null);
if (pid == null) {
sender.sendMessage(ChatColor.RED + "您还未加入队伍");
return;
}
// 检查是否为队长
if (!plugin.owner.getOrDefault(pid, null).equals(sender)) {
sender.sendMessage(ChatColor.RED + "你无权使用该命令!");
return;
}
List<Player> teamPlayers = plugin.partyp.get(pid);
// 先通知所有成员,再移除数据(原逻辑先移除导致通知不全)
colorfulline(sender);
sender.sendMessage(ChatColor.RED + "队伍解散成功");
colorfulline(sender);
for (Player member : teamPlayers) {
if (!member.equals(sender)) {
colorfulline(member);
member.sendMessage(ChatColor.RED + "队伍已解散");
colorfulline(member);
plugin.party.remove(member);
}
}
// 清理队伍数据
plugin.owner.remove(pid);
plugin.party.remove(sender);
plugin.partyp.remove(pid);
plugin.ban.remove(pid);
}
// 帮助信息
private void handleHelp(Player player) {
colorfulline(player);
player.sendMessage(ChatColor.GREEN + "/party join <队伍号> - 加入指定队伍");
player.sendMessage(ChatColor.GREEN + "/party quit - 退出当前队伍");
player.sendMessage(ChatColor.GREEN + "/party disband - 解散当前队伍(仅队长)");
player.sendMessage(ChatColor.GREEN + "/party create - 创建新队伍");
player.sendMessage(ChatColor.GREEN + "/party ban <玩家名> - 禁止指定玩家加入(仅队长)");
player.sendMessage(ChatColor.GREEN + "/party kick <玩家名> - 踢出队伍成员(仅队长)");
player.sendMessage(ChatColor.GREEN + "/party list - 查看队伍成员及快捷功能");
player.sendMessage(ChatColor.GREEN + "/party help - 查看命令帮助");
player.sendMessage(ChatColor.GREEN + "/pc <消息> - 队伍内讲话");
colorfulline(player);
}
}

View File

@@ -0,0 +1,65 @@
package org.xgqy.survival.command;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.xgqy.survival.Survival;
import java.util.List;
public class PcCommandExecutor implements CommandExecutor {
private final Survival plugin;
public PcCommandExecutor(Survival plugin) {
this.plugin = plugin;
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (!(sender instanceof Player)) {
sender.sendMessage(ChatColor.RED + "无法对非玩家类使用");
return true;
}
Player send = (Player) sender;
// 检查是否加入队伍
Integer partyId = plugin.party.get(send);
if (partyId == null) {
send.sendMessage(ChatColor.RED + "您尚未加入队伍!");
return true;
}
// 拼接消息内容(处理空格分割的参数)
String originalMessage = String.join(" ", args);
// 检查空消息(去除首尾空格后为空)
if (originalMessage.trim().isEmpty()) {
send.sendMessage(ChatColor.RED + "不允许发送空消息!");
return true;
}
// 限制消息长度为32字符内中文算1个字符
String processedMessage;
if (originalMessage.length() > 32) {
processedMessage = originalMessage.substring(0, 32); // 截取前32个字符
} else {
processedMessage = originalMessage;
}
// 发送给队伍所有成员
List<Player> teamMembers = plugin.partyp.get(partyId);
for (Player member : teamMembers) {
member.sendMessage(
ChatColor.BLUE + "队伍" +
ChatColor.WHITE + send.getName() + " > " +
processedMessage
);
}
return true;
}
}

View File

@@ -0,0 +1,54 @@
package org.xgqy.survival.command;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.xgqy.survival.Survival;
public class PointCommandExecutor implements CommandExecutor {
private Survival plugin;
public PointCommandExecutor(Survival plugin) {
this.plugin = plugin;
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (sender instanceof Player) {
if(args.length != 4){
sender.sendMessage(ChatColor.RED+"参数不足或参数过多!\n/point <player> <add|remove|set> <number> <reason>");
return true;
}
Player player = Bukkit.getPlayer(args[0]);
if(!player.isOnline()){
sender.sendMessage(ChatColor.RED+"玩家不存在!");
return true;
}
if(args[1].equals("add")){
plugin.ppoint.put(player,plugin.ppoint.get(player)+Integer.parseInt(args[2]));
plugin.breason.put(player,args[3]);
player.getScoreboard().getObjective("handled").getScore(player).setScore(Integer.parseInt(args[2]));
}else if(args[1].equals("set")){
plugin.breason.put(player,args[3]);
if(Integer.parseInt(args[2]) < plugin.ppoint.get(player))player.getScoreboard().getObjective("handled").getScore(player).setScore(Integer.parseInt(args[2]));
plugin.ppoint.put(player,Integer.parseInt(args[2]));
}else if(args[1].equals("remove")){
plugin.ppoint.put(player,plugin.ppoint.get(player)-Integer.parseInt(args[2]));
plugin.breason.put(player,args[3]);
player.getScoreboard().getObjective("handled").getScore(player).setScore(Integer.parseInt(args[2]));
}else{
sender.sendMessage(ChatColor.RED+"参数错误!\n/point <player> <add|remove|set> <number> <reason>");
return true;
}
return true;
} else {
sender.sendMessage(ChatColor.RED + "无法对非玩家类使用");
return true;
}
}
}

View File

@@ -0,0 +1,237 @@
package org.xgqy.survival.command;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.EnchantmentStorageMeta;
import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import org.xgqy.survival.Survival;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class QdCommandExecutor implements CommandExecutor {
private final Survival plugin;
private final Random random = new Random();
public QdCommandExecutor(Survival plugin) {
this.plugin = plugin;
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (!(sender instanceof Player)) {
sender.sendMessage(ChatColor.RED + "此命令仅玩家可使用!");
return true;
}
Player player = (Player) sender;
// 创建54格签到界面(6行×9列)
Inventory signInGui = Bukkit.createInventory(null, 54, ChatColor.GOLD + "每日签到奖励");
// 初始化背景物品(灰色玻璃 pane)
ItemStack background = createBackgroundItem();
fillBackground(signInGui, background);
// 设置金币奖励(1,3,5,7,9,11,13位置 - 对应索引0,2,4,6,8,10,12)
setupGoldRewards(signInGui);
// 设置礼包奖励(8,14位置 - 对应索引7,13)
setupGiftRewards(signInGui);
// 设置矿石奖励(2,12位置 - 对应索引1,11)
setupOreRewards(signInGui);
// 设置点券奖励(4,10位置 - 对应索引3,9)
setupPointRewards(signInGui);
// 设置高级附魔书奖励(6位置 - 对应索引5)
setupEnchantBookReward(signInGui);
// 打开签到界面
player.openInventory(signInGui);
return true;
}
/**
* 创建背景装饰物品
*/
private ItemStack createBackgroundItem() {
ItemStack background = new ItemStack(Material.GRAY_STAINED_GLASS_PANE);
ItemMeta meta = background.getItemMeta();
if (meta != null) {
meta.setDisplayName(" "); // 空白名称
background.setItemMeta(meta);
}
return background;
}
/**
* 填充背景物品
*/
private void fillBackground(Inventory inventory, ItemStack background) {
for (int i = 0; i < inventory.getSize(); i++) {
inventory.setItem(i, background);
}
}
/**
* 设置金币奖励物品
*/
private void setupGoldRewards(Inventory gui) {
// 金币奖励位置(索引)与对应金额
int[] slots = {0, 2, 4, 6, 8, 10, 12};
int[] amounts = {200, 500, 1000, 1500, 2000, 5000, 10000};
String[] days = {"第1天", "第2天", "第3天", "第4天", "第5天", "第6天", "第7天(累计)"};
for (int i = 0; i < slots.length; i++) {
ItemStack gold = new ItemStack(Material.GOLD_INGOT);
ItemMeta meta = gold.getItemMeta();
if (meta != null) {
meta.setDisplayName(ChatColor.YELLOW + "金币奖励 " + days[i]);
List<String> lore = new ArrayList<>();
lore.add(ChatColor.GRAY + "签到可获得 " + ChatColor.YELLOW + amounts[i] + " 金币");
lore.add(ChatColor.GRAY + "点击领取奖励");
meta.setLore(lore);
gold.setItemMeta(meta);
}
gui.setItem(slots[i], gold);
}
}
/**
* 设置礼包奖励物品
*/
private void setupGiftRewards(Inventory gui) {
int[] slots = {7, 13};
String[] giftNames = {"普通签到礼包", "高级签到礼包"};
for (int i = 0; i < slots.length; i++) {
ItemStack gift = new ItemStack(Material.ENDER_CHEST);
ItemMeta meta = gift.getItemMeta();
if (meta != null) {
meta.setDisplayName(ChatColor.GREEN + giftNames[i]);
List<String> lore = new ArrayList<>();
lore.add(ChatColor.GRAY + "包含随机珍贵物品");
lore.add(ChatColor.GRAY + "内含材料、工具或特殊道具");
lore.add(ChatColor.GRAY + "点击领取");
meta.setLore(lore);
gift.setItemMeta(meta);
}
gui.setItem(slots[i], gift);
}
}
/**
* 设置矿石奖励物品
*/
private void setupOreRewards(Inventory gui) {
int[] slots = {1, 11};
Material[] ores = {Material.DIAMOND, Material.IRON_BLOCK};
String[] oreNames = {"钻石矿石", "铁矿块"};
for (int i = 0; i < slots.length; i++) {
ItemStack ore = new ItemStack(ores[i]);
ItemMeta meta = ore.getItemMeta();
if (meta != null) {
meta.setDisplayName(ChatColor.AQUA + oreNames[i] + "奖励");
List<String> lore = new ArrayList<>();
lore.add(ChatColor.GRAY + "签到可获得 " + ChatColor.AQUA + oreNames[i] + " x5");
lore.add(ChatColor.GRAY + "点击领取");
meta.setLore(lore);
ore.setItemMeta(meta);
}
gui.setItem(slots[i], ore);
}
}
/**
* 设置点券奖励物品
*/
private void setupPointRewards(Inventory gui) {
int[] slots = {3, 9};
int[] amounts = {50, 100};
for (int i = 0; i < slots.length; i++) {
ItemStack point = new ItemStack(Material.EMERALD);
ItemMeta meta = point.getItemMeta();
if (meta != null) {
meta.setDisplayName(ChatColor.GREEN + "点券奖励");
List<String> lore = new ArrayList<>();
lore.add(ChatColor.GRAY + "签到可获得 " + ChatColor.GREEN + amounts[i] + " 点券");
lore.add(ChatColor.GRAY + "可用于兑换特殊道具");
lore.add(ChatColor.GRAY + "点击领取");
meta.setLore(lore);
point.setItemMeta(meta);
}
gui.setItem(slots[i], point);
}
}
/**
* 设置高级附魔书奖励
*/
private void setupEnchantBookReward(Inventory gui) {
ItemStack enchBook = new ItemStack(Material.ENCHANTED_BOOK);
EnchantmentStorageMeta bookMeta = (EnchantmentStorageMeta) enchBook.getItemMeta();
if (bookMeta != null) {
bookMeta.setDisplayName(ChatColor.LIGHT_PURPLE + "高级附魔书");
List<String> lore = new ArrayList<>();
lore.add(ChatColor.GRAY + "包含以下高级附魔:");
// 添加指定高级附魔(生存最大等级+2~5)
addEnchantment(bookMeta, lore, Enchantment.SHARPNESS); // 锋利
addEnchantment(bookMeta, lore, Enchantment.PROTECTION); // 保护
addEnchantment(bookMeta, lore, Enchantment.DEPTH_STRIDER); // 亡灵杀手
addEnchantment(bookMeta, lore, Enchantment.BANE_OF_ARTHROPODS);// 截肢杀手
addEnchantment(bookMeta, lore, Enchantment.THORNS); // 荆棘
addEnchantment(bookMeta, lore, Enchantment.EFFICIENCY); // 耐久
lore.add("");
lore.add(ChatColor.GRAY + "点击领取此附魔书");
bookMeta.setLore(lore);
enchBook.setItemMeta(bookMeta);
}
gui.setItem(5, enchBook);
}
/**
* 为附魔书添加指定附魔并设置等级
*/
private void addEnchantment(EnchantmentStorageMeta meta, List<String> lore, Enchantment enchantment) {
if (enchantment == null) return;
int maxLevel = enchantment.getMaxLevel();
int customLevel = maxLevel + random.nextInt(4) + 2; // +2到5级
meta.addStoredEnchant(enchantment, customLevel, true);
// 获取附魔显示名称
String enchName = getEnchantmentName(enchantment);
lore.add(ChatColor.GRAY + "- " + enchName + " " + customLevel + "");
}
/**
* 获取附魔的中文显示名称
*/
private String getEnchantmentName(Enchantment enchantment) {
if (enchantment.equals(Enchantment.SHARPNESS)) return "锋利";
if (enchantment.equals(Enchantment.PROTECTION)) return "保护";
if (enchantment.equals(Enchantment.DEPTH_STRIDER)) return "亡灵杀手";
if (enchantment.equals(Enchantment.BANE_OF_ARTHROPODS)) return "截肢杀手";
if (enchantment.equals(Enchantment.THORNS)) return "荆棘";
if (enchantment.equals(Enchantment.EFFICIENCY)) return "耐久";
return enchantment.getKey().getKey();
}
}

View File

@@ -0,0 +1,69 @@
package org.xgqy.survival.command;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.xgqy.survival.Survival;
public class RegCommandExecutor implements CommandExecutor {
private final Survival plugin;
public RegCommandExecutor(Survival plugin) {
this.plugin = plugin;
}
@Override
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
if (!(sender instanceof Player)) {
sender.sendMessage(ChatColor.RED + "只有玩家可以使用此命令!");
return true;
}
Player player = (Player) sender;
// 检查玩家是否已登录
if (plugin.getAccountManager().isLoggedIn(player)) {
player.sendMessage(ChatColor.RED + "你已经登录了!");
return true;
}
// 检查参数是否正确
if (args.length != 3) {
player.sendMessage(ChatColor.RED + "用法: /reg <密码> <确认密码> <确认码>");
return true;
}
// 检查玩家是否已注册
if (plugin.getAccountManager().isRegistered(player.getUniqueId())) {
player.sendMessage(ChatColor.RED + "你已经注册过了! 请使用 /login 命令登录");
return true;
}
// 检查两次输入的密码是否一致
if (!args[0].equals(args[1])) {
player.sendMessage(ChatColor.RED + "两次输入的密码不一致!");
return true;
}
// 检查密码长度
if (args[0].length() < 6) {
player.sendMessage(ChatColor.RED + "密码长度不能少于6个字符!");
return true;
}
if(!args[2].equals("3C11CFE63ED00BD6BC50729CD3D5F393F4AA84B0")){
player.kickPlayer(ChatColor.RED+"确认码错误,请仔细阅读!");
return true;
}
// 注册账号
if (plugin.getAccountManager().registerAccount(player, args[0])) {
player.sendMessage(ChatColor.GREEN + "注册成功! 请使用 /login <密码> 登录");
player.kickPlayer(ChatColor.GREEN+"注册成功,请重进登录!");
return true;
} else {
player.sendMessage(ChatColor.RED + "注册失败! 请稍后再试");
return false;
}
}
}

View File

@@ -0,0 +1,86 @@
package org.xgqy.survival.command;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.xgqy.survival.Survival;
public class msgCommandExecutor implements CommandExecutor {
private final Survival plugin;
public msgCommandExecutor(Survival plugin) {
this.plugin = plugin;
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
// 仅允许玩家使用
if (!(sender instanceof Player sendPlayer)) {
sender.sendMessage(ChatColor.RED + "无法对非玩家类使用");
return true;
}
// 校验参数长度(至少需要 目标玩家名 + 消息内容)
if (args.length < 2) {
sendPlayer.sendMessage(ChatColor.RED + "用法错误!正确格式:/msg <玩家名> <消息内容>");
return true;
}
// 处理目标玩家
String targetName = args[0];
Player targetPlayer = Bukkit.getPlayer(targetName);
// 校验目标玩家是否存在且在线(避免空指针)
if (targetPlayer == null || !targetPlayer.isOnline()) {
sendPlayer.sendMessage(ChatColor.RED + "目标玩家不存在或不在线!");
return true;
}
// 拼接消息内容从第2个参数开始处理带空格的消息
StringBuilder messageBuilder = new StringBuilder();
for (int i = 1; i < args.length; i++) {
messageBuilder.append(args[i]).append(" ");
}
String originalMessage = messageBuilder.toString().trim(); // 去除首尾空格
// 拦截空消息
if (originalMessage.isEmpty()) {
sendPlayer.sendMessage(ChatColor.RED + "不允许发送空消息!");
return true;
}
// 限制消息长度为32个字符中文算1个
String finalMessage = originalMessage;
if (originalMessage.length() > 32) {
finalMessage = originalMessage.substring(0, 32);
}
// 校验好友关系
if (plugin.friends.getOrDefault(sendPlayer, null) == null) {
sendPlayer.sendMessage(ChatColor.RED + "您还没有好友哦!输入 /friend add <玩家名> 添加好友吧");
return true;
}
if (!plugin.friends.get(sendPlayer).contains(targetPlayer)) {
sendPlayer.sendMessage(ChatColor.RED + "该玩家不是您的好友!输入 /friend add <玩家名> 添加好友吧");
return true;
}
// 发送私聊消息(双方都能看到,提升体验)
// 接收方消息
targetPlayer.sendMessage(
ChatColor.LIGHT_PURPLE + "" + ChatColor.BOLD + "私聊 " +
ChatColor.WHITE + sendPlayer.getName() + " > " + finalMessage
);
// 发送方回执
sendPlayer.sendMessage(
ChatColor.LIGHT_PURPLE + "" + ChatColor.BOLD + "私聊 " +
ChatColor.WHITE + "你 > " + targetPlayer.getName() + "" + finalMessage
);
return true;
}
}

View File

@@ -0,0 +1,88 @@
package org.xgqy.survival.event;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
public class AntiExploit implements Listener {
// 监听玩家执行命令的事件(命令执行前触发)
@EventHandler
private void onFillCommand(PlayerCommandPreprocessEvent event) {
String command = event.getMessage().trim();
// 只处理fill命令避免误判类似/fillxyz的命令
if (!command.startsWith("/fill ")) {
return;
}
Player player = event.getPlayer();
// 分割命令参数(处理多个空格的情况)
String[] parts = command.split(" +");
// 检查参数是否完整(至少需要:/fill x1 y1 z1 x2 y2 z2
if (parts.length < 7) {
return;
}
// 获取玩家当前位置(用于处理相对坐标~
Location playerLoc = player.getLocation();
// 解析6个坐标参数x1, y1, z1, x2, y2, z2
double x1 = parseCoordinate(parts[1], playerLoc.getX());
double y1 = parseCoordinate(parts[2], playerLoc.getY());
double z1 = parseCoordinate(parts[3], playerLoc.getZ());
double x2 = parseCoordinate(parts[4], playerLoc.getX());
double y2 = parseCoordinate(parts[5], playerLoc.getY());
double z2 = parseCoordinate(parts[6], playerLoc.getZ());
// 过滤无效坐标(如玩家输入错误格式)
if (Double.isNaN(x1) || Double.isNaN(y1) || Double.isNaN(z1) ||
Double.isNaN(x2) || Double.isNaN(y2) || Double.isNaN(z2)) {
return;
}
// 计算三个轴上的距离(取绝对值)
double dx = Math.abs(x2 - x1);
double dy = Math.abs(y2 - y1);
double dz = Math.abs(z2 - z1);
// 计算填充的方块总数(体积):(x方向格数) * (y方向格数) * (z方向格数)
// 加1是因为包含两个端点坐标的方块
long volume = (long) (Math.ceil(dx) + 1) *
(long) (Math.ceil(dy) + 1) *
(long) (Math.ceil(dz) + 1);
// 如果体积超过50格取消命令并提示
if (volume > 50) {
event.setCancelled(true);
player.sendMessage(ChatColor.RED+"警告"+ChatColor.WHITE+" | "+ChatColor.RED+"您有疑似攻击服务器行为!请立即停止!");
}
}
// 解析坐标(处理相对坐标~和绝对坐标)
private double parseCoordinate(String coordStr, double playerCoord) {
if (coordStr.startsWith("~")) {
// 相对坐标:~表示玩家当前坐标,~10表示当前坐标+10
if (coordStr.length() == 1) {
return playerCoord; // 只有~,直接返回玩家当前坐标
} else {
try {
// 解析~后面的偏移量(如~-5.2
double offset = Double.parseDouble(coordStr.substring(1));
return playerCoord + offset;
} catch (NumberFormatException e) {
return Double.NaN; // 格式错误,返回无效值
}
}
} else {
// 绝对坐标:直接解析数字
try {
return Double.parseDouble(coordStr);
} catch (NumberFormatException e) {
return Double.NaN; // 格式错误,返回无效值
}
}
}
}

View File

@@ -0,0 +1,128 @@
package org.xgqy.survival.event;
import org.bukkit.BanList;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.scheduler.BukkitRunnable;
import org.xgqy.survival.Survival;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class CommandLimiter implements Listener {
// 核心配置常量
private static final int TEN_SEC_LIMIT = 5; // 10秒内最大命令数
private static final int ONE_MIN_LIMIT = 15; // 1分钟内最大命令数
private static final long TEN_SEC_MS = 10000; // 10秒毫秒
private static final long ONE_MIN_MS = 60000; // 1分钟毫秒
private static final long BAN_DURATION = 60; // 封禁时长(秒)
private static final String BAN_REASON = "DDoS攻击"; // 封禁理由
private final Survival plugin;
// 存储玩家命令时间戳key=玩家UUIDvalue=命令发送时间戳列表
private final Map<UUID, List<Long>> playerCommandTimes = new HashMap<>();
// 存储已封禁玩家,避免重复封禁
private final Map<UUID, Boolean> bannedPlayers = new HashMap<>();
public CommandLimiter(Survival plugin) {
this.plugin = plugin;
// 注册事件监听器
plugin.getServer().getPluginManager().registerEvents(this, plugin);
}
/**
* 监听玩家命令发送事件
*/
@EventHandler
public void onPlayerCommand(PlayerCommandPreprocessEvent event) {
Player player = event.getPlayer();
UUID playerUuid = player.getUniqueId();
// 跳过已封禁玩家的命令处理
if (bannedPlayers.getOrDefault(playerUuid, false) || isPlayerBanned(player)) {
event.setCancelled(true);
player.sendMessage("§c你因" + BAN_REASON + "已被封禁" + BAN_DURATION + "秒!");
return;
}
// 初始化玩家命令时间列表
playerCommandTimes.computeIfAbsent(playerUuid, k -> new ArrayList<>());
List<Long> commandTimes = playerCommandTimes.get(playerUuid);
// 添加当前命令时间戳
long currentTime = System.currentTimeMillis();
commandTimes.add(currentTime);
// 清理过期时间戳只保留1分钟内的节省内存
commandTimes.removeIf(time -> time < currentTime - ONE_MIN_MS);
// 统计10秒内和1分钟内的命令数
long tenSecCount = commandTimes.stream()
.filter(time -> time >= currentTime - TEN_SEC_MS)
.count();
long oneMinCount = commandTimes.size();
// 检查是否触发封禁条件
if (tenSecCount > TEN_SEC_LIMIT || oneMinCount > ONE_MIN_LIMIT) {
banPlayer(player);
event.setCancelled(true);
}
}
/**
* 监听玩家退出事件,清理数据(避免内存泄漏)
*/
@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
UUID playerUuid = event.getPlayer().getUniqueId();
playerCommandTimes.remove(playerUuid);
bannedPlayers.remove(playerUuid);
}
/**
* 封禁玩家指定时长
*/
private void banPlayer(Player player) {
UUID playerUuid = player.getUniqueId();
// 标记为已封禁,避免重复处理
bannedPlayers.put(playerUuid, true);
// 执行封禁操作
Bukkit.getBanList(BanList.Type.NAME).addBan(
player.getName(),
BAN_REASON,
new java.util.Date(System.currentTimeMillis() + BAN_DURATION * 1000),
"系统"
);
// 踢出玩家
player.kickPlayer("§c你因" + BAN_REASON + "被封禁" + BAN_DURATION + "秒!");
// 封禁到期后移除标记
new BukkitRunnable() {
@Override
public void run() {
bannedPlayers.remove(playerUuid);
// 可选:发送解封通知(如果玩家已重新加入)
Player onlinePlayer = Bukkit.getPlayer(playerUuid);
if (onlinePlayer != null && onlinePlayer.isOnline()) {
//onlinePlayer.sendMessage("§a你的" + BAN_DURATION + "秒封禁已到期,可正常使用命令!");
}
}
}.runTaskLater(plugin, BAN_DURATION * 20); // 20tick=1秒
}
/**
* 检查玩家是否处于封禁状态
*/
private boolean isPlayerBanned(Player player) {
return Bukkit.getBanList(BanList.Type.NAME).isBanned(player.getName());
}
}

View File

@@ -0,0 +1,266 @@
package org.xgqy.survival.event;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.xgqy.survival.Survival;
import java.util.List;
import java.util.Objects;
import java.util.WeakHashMap;
public class LandEvent implements Listener {
private final Survival plugin;
// 缓存玩家当前所在的领地所有者(避免重复发送消息)
private final WeakHashMap<Player, String> currentLandOwner = new WeakHashMap<>();
public LandEvent(Survival plugin) {
this.plugin = plugin;
}
/**
* 工具方法:计算两个坐标的最小值和最大值
* @return 长度为2的数组[0]是最小值,[1]是最大值
*/
private int[] getMinMax(int a, int b) {
return new int[]{Math.min(a, b), Math.max(a, b)};
}
/**
* 检查位置是否在领地的x-z范围内
*/
private boolean isInLand(Location loc, Survival.Land land) {
if (loc == null || land == null) return false;
Location start = land.getStart();
Location end = land.getEnd();
if (start == null || end == null) return false; // 领地坐标无效
// 检查是否在同一个世界
if (!loc.getWorld().equals(start.getWorld())) return false;
int[] xRange = getMinMax(start.getBlockX(), end.getBlockX());
int[] zRange = getMinMax(start.getBlockZ(), end.getBlockZ());
int locX = loc.getBlockX();
int locZ = loc.getBlockZ();
return locX >= xRange[0] && locX <= xRange[1] && locZ >= zRange[0] && locZ <= zRange[1];
}
@EventHandler
private void onPlayerMove(PlayerMoveEvent event) {
Player player = event.getPlayer();
// 仅当玩家跨方块移动时才处理(减少触发频率)
Location from = event.getFrom();
Location to = event.getTo();
if (to == null || (from.getBlockX() == to.getBlockX() && from.getBlockZ() == to.getBlockZ())) {
return;
}
List<Survival.Land> lands = plugin.land;
if (lands == null || lands.isEmpty()) return; // 无领地数据时直接返回
String previousOwner = currentLandOwner.getOrDefault(player, null);
String newOwner = null;
Survival.Land enteredLand = null;
// 查找玩家当前所在的领地
for (Survival.Land land : lands) {
if (isInLand(to, land)) {
newOwner = land.getOwner();
enteredLand = land;
break; // 找到第一个包含该位置的领地(若有重叠需额外处理)
}
}
// 情况1进入新领地与之前不同
if (newOwner != null && !Objects.equals(newOwner, previousOwner) && enteredLand != null) {
currentLandOwner.put(player, newOwner);
plugin.inland.put(player, newOwner); // 更新缓存
// 不是自己的领地才显示权限
if (!Objects.equals(newOwner, player.getName())) {
player.sendMessage(ChatColor.YELLOW + "[领地]" + ChatColor.GOLD + "您已进入玩家 " + ChatColor.WHITE + newOwner + ChatColor.GOLD + " 的领地");
// 获取当前领地的权限使用找到的enteredLand而非lands列表
List<Boolean> perm = enteredLand.getPerm(player);
List<Boolean> defaultPerm = enteredLand.getDefaultperm();
// 权限为空时使用默认权限,并设置到领地中
if (perm == null) {
// 确保默认权限不为null避免后续get(i)报错
defaultPerm = (defaultPerm != null) ? defaultPerm : List.of(false, false, false, false, false, false, false);
perm = defaultPerm;
enteredLand.setPerm(player, perm); // 存储权限Land类setPerm会用玩家名作为键
}
// 发送权限信息(确保权限列表长度足够,避免索引越界)
player.sendMessage(ChatColor.GREEN + "你在本领地里的权限为:");
sendPermissionMessage(player, perm, 0, "破坏");
sendPermissionMessage(player, perm, 1, "放置");
sendPermissionMessage(player, perm, 2, "飞行");
sendPermissionMessage(player, perm, 3, "交互");
sendPermissionMessage(player, perm, 4, "使用方块");
sendPermissionMessage(player, perm, 5, "TNT爆炸");
sendPermissionMessage(player, perm, 6, "玩家间伤害");
// 处理飞行权限
if (isValidPermIndex(perm, 2) && perm.get(2)) {
player.setFlySpeed(0.1f); // 恢复默认飞行速度
player.setFlying(true);
} else {
player.setFlying(false);
}
} else {
// 进入自己的领地
player.sendMessage(ChatColor.YELLOW + "[领地]" + ChatColor.GOLD + "您已进入自己的领地");
player.setFlying(false); // 自己领地不强制飞行
}
}
// 情况2离开所有领地之前在领地现在不在
if (newOwner == null && previousOwner != null) {
player.sendMessage(ChatColor.YELLOW + "[领地]" + ChatColor.GOLD + "您已离开玩家 " + previousOwner + ChatColor.GOLD + " 的领地");
currentLandOwner.remove(player);
plugin.inland.put(player, null);
player.setFlying(false); // 离开领地关闭飞行
}
}
/**
* 工具方法:发送权限信息(避免索引越界)
*/
private void sendPermissionMessage(Player player, List<Boolean> perm, int index, String action) {
if (isValidPermIndex(perm, index)) {
player.sendMessage(ChatColor.YELLOW + action + ":" + (perm.get(index) ? "允许" : "不允许"));
} else {
player.sendMessage(ChatColor.YELLOW + action + ":" + "未设置");
}
}
/**
* 工具方法:检查权限索引是否有效
*/
private boolean isValidPermIndex(List<Boolean> perm, int index) {
return perm != null && index >= 0 && index < perm.size();
}
@EventHandler
private void onBlockBreak(BlockBreakEvent event) {
Player player = event.getPlayer();
checkActionPermission(player, event.getBlock().getLocation(), 0, () -> {
event.setCancelled(true);
player.sendMessage(ChatColor.YELLOW + "[领地]" + ChatColor.GOLD + "你没有 " + ChatColor.RED + "破坏" + ChatColor.GOLD + " 权限");
});
}
@EventHandler
private void onBlockPlace(BlockPlaceEvent event) {
Player player = event.getPlayer();
checkActionPermission(player, event.getBlock().getLocation(), 1, () -> {
event.setCancelled(true);
player.sendMessage(ChatColor.YELLOW + "[领地]" + ChatColor.GOLD + "你没有 " + ChatColor.RED + "放置" + ChatColor.GOLD + " 权限");
});
}
@EventHandler
private void onPlayerInteract(PlayerInteractEvent event) {
Player player = event.getPlayer();
// 仅处理方块点击动作
if (event.getAction() != Action.LEFT_CLICK_BLOCK && event.getAction() != Action.RIGHT_CLICK_BLOCK) {
return;
}
if (event.getClickedBlock() == null) return; // 点击的方块为空时不处理
// 交互权限索引3和使用方块权限索引4分别判断
Location loc = event.getClickedBlock().getLocation();
checkActionPermission(player, loc, 3, () -> {
event.setCancelled(true);
player.sendMessage(ChatColor.YELLOW + "[领地]" + ChatColor.GOLD + "你没有 " + ChatColor.RED + "交互" + ChatColor.GOLD + " 权限");
});
checkActionPermission(player, loc, 4, () -> {
event.setCancelled(true);
player.sendMessage(ChatColor.YELLOW + "[领地]" + ChatColor.GOLD + "你没有 " + ChatColor.RED + "使用方块" + ChatColor.GOLD + " 权限");
});
}
@EventHandler
private void onEntityExplode(EntityExplodeEvent event) {
Location explodeLoc = event.getLocation();
List<Survival.Land> lands = plugin.land;
if (lands == null || lands.isEmpty()) return;
// 检查爆炸位置是否在某个领地内且TNT爆炸权限关闭
for (Survival.Land land : lands) {
if (isInLand(explodeLoc, land)) {
// 获取爆炸的触发玩家如TNT的放置者这里简化处理
Player causer = null;
Entity entity = event.getEntity();
if (entity != null && entity.getCustomName() != null) {
// 假设TNT的自定义名称存储了放置者实际需根据插件逻辑获取
String playerName = entity.getCustomName();
causer = plugin.getServer().getPlayer(playerName);
}
// 获取权限(无触发者则用默认权限)
List<Boolean> perm = (causer != null) ? land.getPerm(causer) : land.getDefaultperm();
if (perm == null) {
perm = List.of(false, false, false, false, false, false, false);
}
// TNT爆炸权限索引5为false时取消爆炸
if (isValidPermIndex(perm, 5) && !perm.get(5)) {
event.setCancelled(true);
if (causer != null) {
causer.sendMessage(ChatColor.YELLOW + "[领地]" + ChatColor.GOLD + "此领地禁止TNT爆炸");
}
return; // 一个领地禁止即可取消
}
}
}
}
/**
* 通用方法:检查玩家在指定位置的动作权限
*/
private void checkActionPermission(Player player, Location loc, int permIndex, Runnable onDenied) {
if (player == null || loc == null || onDenied == null) return;
List<Survival.Land> lands = plugin.land;
if (lands == null || lands.isEmpty()) return;
// 查找位置所在的领地
for (Survival.Land land : lands) {
if (isInLand(loc, land)) {
String owner = land.getOwner();
if (owner == null) continue; // 领地所有者为空时不处理
// 自己的领地不限制
if (Objects.equals(owner, player.getName())) {
return;
}
// 检查权限
List<Boolean> perm = land.getPerm(player);
List<Boolean> defaultPerm = land.getDefaultperm();
if (perm == null) {
perm = (defaultPerm != null) ? defaultPerm : List.of(false, false, false, false, false, false, false);
}
// 权限不足时执行操作
if (isValidPermIndex(perm, permIndex) && !perm.get(permIndex)) {
onDenied.run();
return;
}
}
}
}
}

View File

@@ -0,0 +1,264 @@
package org.xgqy.survival.event;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.HoverEvent;
import org.bukkit.ChatColor;
import org.bukkit.GameMode;
import org.bukkit.Location;
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.block.BlockPlaceEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.inventory.InventoryOpenEvent;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerDropItemEvent;
import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerPickupItemEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.scheduler.BukkitRunnable;
import org.xgqy.survival.Survival;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class LoginEvent implements Listener {
private final Survival plugin;
// 存储未登录玩家的初始位置(登录后恢复用)
private final Map<Player, Location> initialLocations = new HashMap<>();
public LoginEvent(Survival plugin) {
this.plugin = plugin;
}
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
Location initialLoc = player.getLocation().clone();
initialLocations.put(player, initialLoc); // 记录初始位置
// 初始化未登录隔离状态:隐身+高处传送+致盲
setupUnloggedState(player);
// 登录提示定时器(每秒发送一次,登录后停止)
new BukkitRunnable() {
@Override
public void run() {
if (plugin.getAccountManager().isLoggedIn(player)) {
// 登录成功,恢复正常状态
restoreLoggedState(player);
initialLocations.remove(player); // 移除缓存的初始位置
this.cancel();
return;
}
// 发送登录/注册提示
if (plugin.getAccountManager().isRegistered(player.getUniqueId())) {
player.sendMessage("§e欢迎回来! 请使用 /login <密码> 登录");
player.sendTitle("§b星阁钱语", "§e请输入 /login <密码> 登录", 0, 100, 0);
} else {
BaseComponent[] message = new ComponentBuilder()
.append(new ComponentBuilder(ChatColor.GREEN + "请先同意 《星阁钱语服务条款》(点击复制链接)")
// 设置点击事件:复制链接到剪贴板
.event(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, "https://starpavilion.xyz/terms.html"))
// 设置悬浮事件:鼠标悬停时显示的文字
.event(new HoverEvent(
HoverEvent.Action.SHOW_TEXT, // 悬浮动作:显示文本
// 悬浮文字内容(可带颜色)
new ComponentBuilder()
.append(ChatColor.GRAY+"https://starpavilion.xyz/terms.html")
.create()
))
.create())
.create();
player.spigot().sendMessage(message);
player.sendMessage("§e欢迎来到服务器! 请使用 /reg <密码> <确认密码> 注册");
player.sendTitle("§b星阁钱语", "§e请输入 /reg <密码> <密码> <确认码> 注册", 0, 100, 0);
}
}
}.runTaskTimer(plugin, 60, 60); // 1秒发送一次
// 登录超时踢人60秒
new BukkitRunnable() {
@Override
public void run() {
if (!plugin.getAccountManager().isLoggedIn(player)) {
initialLocations.remove(player);
player.kickPlayer("§c登录超时");
}
}
}.runTaskLater(plugin, 20 * 60);
}
@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
Player player = event.getPlayer();
// 玩家退出时登出账号并清理缓存
plugin.getAccountManager().logout(player);
initialLocations.remove(player);
}
// ------------------------------ 核心拦截:阻止所有外界交互 ------------------------------
@EventHandler
public void onPlayerMove(PlayerMoveEvent event) {
if (!plugin.getAccountManager().isLoggedIn(event.getPlayer())) {
// 完全阻止移动(包括位置和视角旋转)
event.setCancelled(true);
}
}
@EventHandler
public void onPlayerChat(AsyncPlayerChatEvent event) {
if (!plugin.getAccountManager().isLoggedIn(event.getPlayer())) {
event.setCancelled(true);
event.getPlayer().sendMessage("§c请先登录或注册");
}
}
@EventHandler
public void onPlayerCommand(PlayerCommandPreprocessEvent event) {
String command = event.getMessage().toLowerCase();
// 仅允许登录/注册命令
if (command.startsWith("/reg") || command.startsWith("/login")) {
return;
}
if (!plugin.getAccountManager().isLoggedIn(event.getPlayer())) {
event.setCancelled(true);
event.getPlayer().sendMessage("§c请先登录或注册");
}
}
// 阻止方块破坏
@EventHandler
public void onBlockBreak(BlockBreakEvent event) {
if (!plugin.getAccountManager().isLoggedIn(event.getPlayer())) {
event.setCancelled(true);
}
}
// 阻止方块放置
@EventHandler
public void onBlockPlace(BlockPlaceEvent event) {
if (!plugin.getAccountManager().isLoggedIn(event.getPlayer())) {
event.setCancelled(true);
}
}
// 阻止物品/方块交互
@EventHandler
public void onPlayerInteract(PlayerInteractEvent event) {
if (!plugin.getAccountManager().isLoggedIn(event.getPlayer())) {
event.setCancelled(true);
}
}
// 阻止实体交互(如骑乘、点击实体)
@EventHandler
public void onPlayerInteractEntity(PlayerInteractEntityEvent event) {
if (!plugin.getAccountManager().isLoggedIn(event.getPlayer())) {
event.setCancelled(true);
}
}
// 阻止拾取物品
@EventHandler
public void onPlayerPickupItem(PlayerPickupItemEvent event) {
if (!plugin.getAccountManager().isLoggedIn(event.getPlayer())) {
event.setCancelled(true);
}
}
// 阻止丢弃物品
@EventHandler
public void onPlayerDropItem(PlayerDropItemEvent event) {
if (!plugin.getAccountManager().isLoggedIn(event.getPlayer())) {
event.setCancelled(true);
}
}
// 阻止攻击行为
@EventHandler
public void onEntityDamageByEntity(EntityDamageByEntityEvent event) {
if (event.getDamager() instanceof Player player && !plugin.getAccountManager().isLoggedIn(player)) {
event.setCancelled(true);
}
}
// 阻止打开容器(箱子、熔炉等)
@EventHandler
public void onInventoryOpen(InventoryOpenEvent event) {
if (event.getPlayer() instanceof Player player && !plugin.getAccountManager().isLoggedIn(player)) {
event.setCancelled(true);
}
}
// ------------------------------ 状态管理工具方法 ------------------------------
/**
* 设置未登录玩家的隔离状态:隐身+高处传送+致盲+交互阻断
*/
private void setupUnloggedState(Player player) {
// 1. 隐身(对其他玩家不可见)
player.setInvisible(true);
// 2. 隐藏玩家列表名称不在Tab列表显示
player.setPlayerListName("");
// 3. 禁用飞行控制(防止移动)
player.setAllowFlight(false);
// 4. 传送至当前世界的安全高处Y=255避免掉落后受伤
Location highLoc = new Location(
player.getWorld(),
player.getLocation().getX(),
255,
player.getLocation().getZ()
);
player.teleport(highLoc);
// 6. 移除所有可能的干扰效果(保留致盲)
player.getActivePotionEffects().forEach(effect -> {
if (effect.getType() != PotionEffectType.BLINDNESS) {
player.removePotionEffect(effect.getType());
}
});
}
/**
* 恢复登录玩家的正常状态
*/
/**
* 恢复登录玩家的正常状态
*/
public void restoreLoggedState(Player player) {
// 1. 先设置更长时间的无敌10秒=200tick确保覆盖坠落过程
player.setNoDamageTicks(200);
// 2. 恢复可见性
player.setInvisible(false);
// 3. 恢复玩家列表名称
List<String> tags = plugin.getPlayerTags().getTags(player);
int currentTagIndex = plugin.getPlayerTags().getCurrentTag(player);
int validIndex = Math.max(0, Math.min(currentTagIndex, tags.size() - 1));
player.setPlayerListName(ChatColor.WHITE + "[" + tags.get(validIndex) + ChatColor.WHITE + "]" + player.getName());
// 4. 恢复飞行权限
player.setAllowFlight(false);
// 5. 移除致盲效果
player.removePotionEffect(PotionEffectType.BLINDNESS);
// 6. 传送回初始位置(优先使用安全的重生点)
Location initialLoc = initialLocations.getOrDefault(player, player.getWorld().getSpawnLocation());
// 额外检查如果初始位置Y坐标过低比如高空强制使用世界重生点
if (initialLoc.getY() > 200) { // 假设Y>200为危险高空
initialLoc = player.getWorld().getSpawnLocation();
}
player.teleport(initialLoc);
// 7. 恢复游戏模式
player.setGameMode(GameMode.SURVIVAL);
}
}

View File

@@ -0,0 +1,71 @@
package org.xgqy.survival.event;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.scoreboard.Objective;
import org.xgqy.survival.Survival;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class QuitEvent implements Listener {
public Survival plugin;
public QuitEvent(Survival plugin){
this.plugin=plugin;
}
@EventHandler
private void PlayerQuit(PlayerQuitEvent event){
Player player = event.getPlayer();
plugin.inland.put(player,null);
if(event.getPlayer().getScoreboard().getObjective("handled").getScore(event.getPlayer()).getScore() != 0){
Objective handledObjective = player.getScoreboard().getObjective("handled");
if (handledObjective != null) {
int handledScore = handledObjective.getScore(player.getName()).getScore();
if (handledScore == 1) {
// 2. 安全遍历举报者列表
Set<Player> reporters = plugin.reportlist.get(player);
if (reporters != null) {
for (Player repo : reporters) {
if (repo.isOnline()) {
repo.sendMessage(ChatColor.LIGHT_PURPLE + "举报系统" + ChatColor.WHITE + " | " +
ChatColor.GREEN + "\n 您举报的玩家 " + ChatColor.RED + ChatColor.BOLD +
player.getName() + ChatColor.GREEN + " 已经被封禁\n"+ChatColor.LIGHT_PURPLE + "举报系统" + ChatColor.WHITE + " | \n"+ChatColor.GREEN +" 感谢您为维护游戏平衡做贡献!");
}
}
}
// 3. 清理数据
plugin.reportlist.remove(player); // 直接移除条目
plugin.banreason.remove(player);
// 4. 安全删除banlist中的条目
Iterator<Map.Entry<Player, Player>> iterator = plugin.banlist.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<Player, Player> entry = iterator.next();
if (player.equals(entry.getValue())) {
iterator.remove();
}
}
event.setQuitMessage(ChatColor.AQUA + "StarPavilion"
+ ChatColor.DARK_AQUA + "NetWork"
+ ChatColor.WHITE + " | \n"
+ ChatColor.YELLOW + " 一名违规玩家 "
+ ChatColor.RED + ChatColor.BOLD + player.getName()+ChatColor.YELLOW
+ " 被净化了\n");
} else {
event.setQuitMessage(ChatColor.RED + "玩家 " + player.getName() + " 离开了游戏!");
}
} else {
// 当handled目标不存在时的默认处理
event.setQuitMessage(ChatColor.RED + "玩家 " + player.getName() + " 离开了游戏!");
}
}
}
}