update all
This commit is contained in:
31
src/main/java/org/xgqy/survival/FriendData.java
Normal file
31
src/main/java/org/xgqy/survival/FriendData.java
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
// 创建显示用的GUI(54格=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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
237
src/main/java/org/xgqy/survival/command/LandCommandExecutor.java
Normal file
237
src/main/java/org/xgqy/survival/command/LandCommandExecutor.java
Normal 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 + " - 开关超级保护(测试中)");
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
237
src/main/java/org/xgqy/survival/command/QdCommandExecutor.java
Normal file
237
src/main/java/org/xgqy/survival/command/QdCommandExecutor.java
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
88
src/main/java/org/xgqy/survival/event/AntiExploit.java
Normal file
88
src/main/java/org/xgqy/survival/event/AntiExploit.java
Normal 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; // 格式错误,返回无效值
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
128
src/main/java/org/xgqy/survival/event/CommandLimiter.java
Normal file
128
src/main/java/org/xgqy/survival/event/CommandLimiter.java
Normal 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=玩家UUID,value=命令发送时间戳列表
|
||||
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());
|
||||
}
|
||||
}
|
||||
266
src/main/java/org/xgqy/survival/event/LandEvent.java
Normal file
266
src/main/java/org/xgqy/survival/event/LandEvent.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
264
src/main/java/org/xgqy/survival/event/LoginEvent.java
Normal file
264
src/main/java/org/xgqy/survival/event/LoginEvent.java
Normal 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);
|
||||
}
|
||||
}
|
||||
71
src/main/java/org/xgqy/survival/event/QuitEvent.java
Normal file
71
src/main/java/org/xgqy/survival/event/QuitEvent.java
Normal 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() + " 离开了游戏!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user