Compare commits
10 Commits
2944f192db
...
a375365d29
| Author | SHA1 | Date | |
|---|---|---|---|
| a375365d29 | |||
| 3015ba5a7c | |||
| 4d09482fb1 | |||
| 57c24b1dce | |||
| 529802f6c5 | |||
| f8b8065c37 | |||
| c5a3b66e5f | |||
| da974f2238 | |||
| 42ed9e8bc9 | |||
| 4712ed8723 |
137
src/main/java/org/xgqy/survival/AccountManager.java
Normal file
137
src/main/java/org/xgqy/survival/AccountManager.java
Normal file
@@ -0,0 +1,137 @@
|
||||
package org.xgqy.survival;
|
||||
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class AccountManager {
|
||||
private final Survival plugin;
|
||||
private final File accountsFile;
|
||||
private final FileConfiguration accountsConfig;
|
||||
private final Map<UUID, PlayerAccount> accounts = new HashMap<>();
|
||||
private final Map<UUID, Boolean> loggedInPlayers = new HashMap<>();
|
||||
|
||||
public AccountManager(Survival plugin) {
|
||||
this.plugin = plugin;
|
||||
this.accountsFile = new File(plugin.getDataFolder(), "accounts.yml");
|
||||
|
||||
if (!accountsFile.exists()) {
|
||||
plugin.getDataFolder().mkdirs();
|
||||
try {
|
||||
accountsFile.createNewFile();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
this.accountsConfig = YamlConfiguration.loadConfiguration(accountsFile);
|
||||
loadAccounts();
|
||||
}
|
||||
|
||||
private void loadAccounts() {
|
||||
for (String key : accountsConfig.getKeys(false)) {
|
||||
try {
|
||||
UUID uuid = UUID.fromString(key);
|
||||
|
||||
// 手动读取字段并构建PlayerAccount对象
|
||||
String name = accountsConfig.getString(key + ".username");
|
||||
String passwordHash = accountsConfig.getString(key + ".passwordHash");
|
||||
long lastLoginTime = accountsConfig.getLong(key + ".lastLoginTime", 0);
|
||||
|
||||
if (name != null && passwordHash != null) {
|
||||
PlayerAccount account = new PlayerAccount(name, passwordHash);
|
||||
account.setLastLoginTime(lastLoginTime);
|
||||
accounts.put(uuid, account);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
plugin.getLogger().warning("Invalid UUID format: " + key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void saveAccounts() {
|
||||
for (Map.Entry<UUID, PlayerAccount> entry : accounts.entrySet()) {
|
||||
String uuidStr = entry.getKey().toString();
|
||||
PlayerAccount account = entry.getValue();
|
||||
|
||||
// 手动存储每个字段
|
||||
accountsConfig.set(uuidStr + ".username", account.getUsername());
|
||||
accountsConfig.set(uuidStr + ".passwordHash", account.getPasswordHash());
|
||||
accountsConfig.set(uuidStr + ".lastLoginTime", account.getLastLoginTime());
|
||||
}
|
||||
try {
|
||||
accountsConfig.save(accountsFile);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRegistered(UUID uuid) {
|
||||
return accounts.containsKey(uuid);
|
||||
}
|
||||
|
||||
public boolean registerAccount(Player player, String password) {
|
||||
UUID uuid = player.getUniqueId();
|
||||
|
||||
if (isRegistered(uuid)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String hashedPassword = hashPassword(password);
|
||||
PlayerAccount account = new PlayerAccount(player.getName(), hashedPassword);
|
||||
accounts.put(uuid, account);
|
||||
saveAccounts();
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean login(Player player, String password) {
|
||||
UUID uuid = player.getUniqueId();
|
||||
|
||||
if (!isRegistered(uuid)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PlayerAccount account = accounts.get(uuid);
|
||||
String hashedPassword = hashPassword(password);
|
||||
|
||||
if (account.getPasswordHash().equals(hashedPassword)) {
|
||||
account.setLastLoginTime(System.currentTimeMillis());
|
||||
loggedInPlayers.put(uuid, true);
|
||||
saveAccounts();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void logout(Player player) {
|
||||
loggedInPlayers.remove(player.getUniqueId());
|
||||
}
|
||||
|
||||
public boolean isLoggedIn(Player player) {
|
||||
return loggedInPlayers.getOrDefault(player.getUniqueId(), false);
|
||||
}
|
||||
|
||||
private String hashPassword(String password) {
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
byte[] hashedBytes = md.digest(password.getBytes());
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte b : hashedBytes) {
|
||||
sb.append(String.format("%02x", b));
|
||||
}
|
||||
return sb.toString();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException("SHA-256 algorithm not found", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
57
src/main/java/org/xgqy/survival/PlayerAccount.java
Normal file
57
src/main/java/org/xgqy/survival/PlayerAccount.java
Normal file
@@ -0,0 +1,57 @@
|
||||
package org.xgqy.survival;
|
||||
|
||||
import org.bukkit.configuration.serialization.ConfigurationSerializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class PlayerAccount implements ConfigurationSerializable {
|
||||
private String username;
|
||||
private String passwordHash;
|
||||
private long registerTime;
|
||||
private long lastLoginTime;
|
||||
|
||||
public PlayerAccount(String username, String passwordHash) {
|
||||
this.username = username;
|
||||
this.passwordHash = passwordHash;
|
||||
this.registerTime = System.currentTimeMillis();
|
||||
this.lastLoginTime = 0;
|
||||
}
|
||||
|
||||
public PlayerAccount(Map<String, Object> map) {
|
||||
this.username = (String) map.get("username");
|
||||
this.passwordHash = (String) map.get("passwordHash");
|
||||
this.registerTime = (long) map.get("registerTime");
|
||||
this.lastLoginTime = (long) map.get("lastLoginTime");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> serialize() {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("username", username);
|
||||
map.put("passwordHash", passwordHash);
|
||||
map.put("registerTime", registerTime);
|
||||
map.put("lastLoginTime", lastLoginTime);
|
||||
return map;
|
||||
}
|
||||
|
||||
// Getters and Setters
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getPasswordHash() {
|
||||
return passwordHash;
|
||||
}
|
||||
|
||||
public long getRegisterTime() {
|
||||
return registerTime;
|
||||
}
|
||||
|
||||
public long getLastLoginTime() {
|
||||
return lastLoginTime;
|
||||
}
|
||||
|
||||
public void setLastLoginTime(long lastLoginTime) {
|
||||
this.lastLoginTime = lastLoginTime;
|
||||
}
|
||||
}
|
||||
@@ -17,14 +17,10 @@ import java.util.UUID;
|
||||
public class PlayerTags {
|
||||
private final Survival plugin;
|
||||
private final File tagsFile;
|
||||
// 原有:玩家-称号列表映射
|
||||
private final Map<UUID, List<String>> playerTags = new HashMap<>();
|
||||
// 新增:玩家-(称号-到期时间戳)映射(毫秒级,永久用Long.MAX_VALUE)
|
||||
private final Map<UUID, Map<String, Long>> expireTimeMap = new HashMap<>();
|
||||
// 原有:玩家-选中称号索引映射
|
||||
public Map<UUID, Integer> playerselectTag = new HashMap<>();
|
||||
|
||||
// 常量:7天的毫秒数(7*24*60*60*1000)
|
||||
private static final long SEVEN_DAYS_MS = 604800000L;
|
||||
|
||||
public PlayerTags(Survival plugin) {
|
||||
@@ -32,36 +28,28 @@ public class PlayerTags {
|
||||
this.tagsFile = new File(plugin.getDataFolder(), "playertags.data");
|
||||
}
|
||||
|
||||
/**
|
||||
* 核心辅助:清理玩家的所有过期称号(所有方法调用前先执行)
|
||||
*/
|
||||
public void cleanExpiredTags(Player player) {
|
||||
UUID uuid = player.getUniqueId();
|
||||
List<String> validTags = new ArrayList<>();
|
||||
List<String> allTags = playerTags.getOrDefault(uuid, new ArrayList<>());
|
||||
Map<String, Long> tagExpires = expireTimeMap.getOrDefault(uuid, new HashMap<>());
|
||||
|
||||
// 遍历所有称号,保留未过期的
|
||||
for (String tag : allTags) {
|
||||
long expireTime = tagExpires.getOrDefault(tag, Long.MAX_VALUE);
|
||||
// 未过期判断:永久(MAX)或当前时间 < 到期时间
|
||||
if (expireTime == Long.MAX_VALUE || System.currentTimeMillis() < expireTime) {
|
||||
validTags.add(tag);
|
||||
} else {
|
||||
// 过期:移除过期时间记录
|
||||
tagExpires.remove(tag);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新有效称号列表和过期时间映射
|
||||
if (validTags.isEmpty()) {
|
||||
playerTags.remove(uuid);
|
||||
expireTimeMap.remove(uuid);
|
||||
playerselectTag.put(uuid, -1); // 无有效称号,重置选中索引
|
||||
playerselectTag.put(uuid, -1);
|
||||
} else {
|
||||
playerTags.put(uuid, validTags);
|
||||
expireTimeMap.put(uuid, tagExpires);
|
||||
// 检查选中的称号是否已过期:若过期/索引无效,重置索引
|
||||
int currentIndex = playerselectTag.getOrDefault(uuid, -1);
|
||||
if (currentIndex == -1 || currentIndex >= validTags.size()) {
|
||||
playerselectTag.put(uuid, -1);
|
||||
@@ -69,11 +57,8 @@ public class PlayerTags {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前选中的称号索引(自动过滤过期)
|
||||
*/
|
||||
public int getCurrentTag(Player player) {
|
||||
cleanExpiredTags(player); // 先清理过期
|
||||
cleanExpiredTags(player);
|
||||
UUID uuid = player.getUniqueId();
|
||||
Integer index = playerselectTag.get(uuid);
|
||||
if (index == null) {
|
||||
@@ -82,34 +67,29 @@ public class PlayerTags {
|
||||
|
||||
List<String> validTags = playerTags.getOrDefault(uuid, new ArrayList<>());
|
||||
if (validTags.isEmpty() || index < 0 || index >= validTags.size()) {
|
||||
playerselectTag.put(uuid, -1); // 索引无效,重置
|
||||
playerselectTag.put(uuid, -1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置玩家选择的标签索引(自动过滤过期)
|
||||
*/
|
||||
public boolean setSelectedTag(Player player, int index) {
|
||||
cleanExpiredTags(player); // 先清理过期
|
||||
cleanExpiredTags(player);
|
||||
UUID uuid = player.getUniqueId();
|
||||
List<String> validTags = playerTags.getOrDefault(uuid, new ArrayList<>());
|
||||
|
||||
// 无有效称号或索引无效,返回失败
|
||||
if (validTags.isEmpty() || index < 0 || index >= validTags.size()) {
|
||||
playerselectTag.put(uuid, -1);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 设置选中索引,返回成功
|
||||
playerselectTag.put(uuid, index);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载称号数据:核心逻辑——识别旧数据并转为7天过期
|
||||
* 修复核心:正确读取新版本数据中的expireTimeMap,避免误判为旧数据
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void loadTags() {
|
||||
@@ -118,17 +98,27 @@ public class PlayerTags {
|
||||
}
|
||||
|
||||
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(tagsFile))) {
|
||||
// 读取第一个对象:判断是旧版本(List<String>)还是新版本(含过期时间)
|
||||
// 读取三个核心对象(新版本数据结构)
|
||||
Object firstObj = ois.readObject();
|
||||
Object secondObj = ois.readObject();
|
||||
Object thirdObj = null; // 新版本的第三个对象:expireTimeMap
|
||||
Object thirdObj = ois.readObject(); // 关键修复:读取第三个对象(expireTimeMap)
|
||||
|
||||
// 1. 识别旧版本数据(仅2个对象:playerTags + playerselectTag)
|
||||
if (firstObj instanceof Map<?, ?> && secondObj instanceof Map<?, ?> && ois.available() == 0) {
|
||||
// 1. 优先判断新版本数据(3个对象完整)
|
||||
if (firstObj instanceof Map<?, ?> && secondObj instanceof Map<?, ?> && thirdObj instanceof Map<?, ?>) {
|
||||
// 强制转换并加载数据
|
||||
playerTags.putAll((Map<UUID, List<String>>) firstObj);
|
||||
playerselectTag.putAll((Map<UUID, Integer>) secondObj);
|
||||
expireTimeMap.putAll((Map<UUID, Map<String, Long>>) thirdObj);
|
||||
plugin.getLogger().info("新版本称号数据加载完成!共加载 " + playerTags.size() + " 个玩家的称号");
|
||||
return; // 加载成功直接返回,避免进入旧数据处理
|
||||
}
|
||||
|
||||
// 2. 处理旧版本数据(仅2个对象:无expireTimeMap)
|
||||
if (firstObj instanceof Map<?, ?> && secondObj instanceof Map<?, ?>) {
|
||||
Map<UUID, List<String>> oldPlayerTags = (Map<UUID, List<String>>) firstObj;
|
||||
Map<UUID, Integer> oldSelectTag = (Map<UUID, Integer>) secondObj;
|
||||
|
||||
// 旧数据转换:为每个称号设置“当前时间+7天”的到期时间
|
||||
// 旧数据转换为7天过期
|
||||
for (Map.Entry<UUID, List<String>> entry : oldPlayerTags.entrySet()) {
|
||||
UUID uuid = entry.getKey();
|
||||
List<String> oldTags = entry.getValue();
|
||||
@@ -136,34 +126,27 @@ public class PlayerTags {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 为当前玩家创建“称号-到期时间”映射
|
||||
Map<String, Long> tagExpires = new HashMap<>();
|
||||
long sevenDaysLater = System.currentTimeMillis() + SEVEN_DAYS_MS; // 7天后到期
|
||||
long sevenDaysLater = System.currentTimeMillis() + SEVEN_DAYS_MS;
|
||||
for (String tag : oldTags) {
|
||||
tagExpires.put(tag, sevenDaysLater); // 每个旧称号都设为7天过期
|
||||
tagExpires.put(tag, sevenDaysLater);
|
||||
}
|
||||
|
||||
// 存入新结构
|
||||
playerTags.put(uuid, new ArrayList<>(oldTags)); // 复制旧称号列表
|
||||
expireTimeMap.put(uuid, tagExpires); // 存入7天过期时间
|
||||
playerTags.put(uuid, new ArrayList<>(oldTags));
|
||||
expireTimeMap.put(uuid, tagExpires);
|
||||
}
|
||||
|
||||
// 恢复选中索引
|
||||
playerselectTag.putAll(oldSelectTag);
|
||||
plugin.getLogger().info("旧版本称号数据加载完成,已自动转为7日后到期!");
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
// 2. 识别新版本数据(3个对象:playerTags + playerselectTag + expireTimeMap)
|
||||
else if (firstObj instanceof Map<?, ?> && secondObj instanceof Map<?, ?> && thirdObj instanceof Map<?, ?>) {
|
||||
playerTags.putAll((Map<UUID, List<String>>) firstObj);
|
||||
playerselectTag.putAll((Map<UUID, Integer>) secondObj);
|
||||
expireTimeMap.putAll((Map<UUID, Map<String, Long>>) thirdObj);
|
||||
plugin.getLogger().info("新版本称号数据加载完成!");
|
||||
}
|
||||
// 3. 数据格式错误
|
||||
plugin.getLogger().warning("称号数据格式错误,无法识别!");
|
||||
|
||||
} catch (IOException | ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
// 回退逻辑:仅加载称号列表,所有称号设为7天过期
|
||||
// 回退逻辑:仅加载称号列表,设为7天过期
|
||||
try (ObjectInputStream fallback = new ObjectInputStream(new FileInputStream(tagsFile))) {
|
||||
Object obj = fallback.readObject();
|
||||
if (obj instanceof Map<?, ?> oldPlayerTags) {
|
||||
@@ -176,7 +159,7 @@ public class PlayerTags {
|
||||
for (Object tagObj : (List<?>) entry.getValue()) {
|
||||
if (tagObj instanceof String tag) {
|
||||
tags.add(tag);
|
||||
tagExpires.put(tag, sevenDaysLater); // 回退时也设为7天过期
|
||||
tagExpires.put(tag, sevenDaysLater);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,16 +176,13 @@ public class PlayerTags {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存称号数据:同步保存playerTags、playerselectTag、expireTimeMap
|
||||
*/
|
||||
public void saveTags() {
|
||||
if (!tagsFile.getParentFile().exists()) {
|
||||
tagsFile.getParentFile().mkdirs();
|
||||
}
|
||||
|
||||
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(tagsFile))) {
|
||||
// 按顺序写入3个核心结构(新版本)
|
||||
// 按顺序写入三个核心结构(与加载时对应)
|
||||
oos.writeObject(playerTags);
|
||||
oos.writeObject(playerselectTag);
|
||||
oos.writeObject(expireTimeMap);
|
||||
@@ -211,71 +191,50 @@ public class PlayerTags {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取玩家的有效称号列表(自动过滤过期)
|
||||
*/
|
||||
public List<String> getTags(Player player) {
|
||||
cleanExpiredTags(player);
|
||||
return playerTags.getOrDefault(player.getUniqueId(), new ArrayList<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增称号:默认永久(可手动指定过期时间)
|
||||
*
|
||||
* @param player 目标玩家
|
||||
* @param tag 称号名称
|
||||
* @param isPermanent 是否永久(true=永久,false=7天过期)
|
||||
*/
|
||||
public void addTag(Player player, String tag, boolean isPermanent) {
|
||||
cleanExpiredTags(player); // 先清理过期
|
||||
cleanExpiredTags(player);
|
||||
UUID uuid = player.getUniqueId();
|
||||
List<String> tags = playerTags.computeIfAbsent(uuid, k -> new ArrayList<>());
|
||||
Map<String, Long> tagExpires = expireTimeMap.computeIfAbsent(uuid, k -> new HashMap<>());
|
||||
|
||||
// 避免重复添加相同称号
|
||||
if (!tags.contains(tag)) {
|
||||
tags.add(tag);
|
||||
// 设置过期时间:永久=Long.MAX_VALUE,临时=当前时间+7天
|
||||
long expireTime = isPermanent ? Long.MAX_VALUE : System.currentTimeMillis() + SEVEN_DAYS_MS;
|
||||
tagExpires.put(tag, expireTime);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重载原有addTag方法:默认添加永久称号(保持向下兼容)
|
||||
*/
|
||||
public void addTag(Player player, String tag, int days) {
|
||||
cleanExpiredTags(player); // 先清理过期
|
||||
cleanExpiredTags(player);
|
||||
UUID uuid = player.getUniqueId();
|
||||
List<String> tags = playerTags.computeIfAbsent(uuid, k -> new ArrayList<>());
|
||||
Map<String, Long> tagExpires = expireTimeMap.computeIfAbsent(uuid, k -> new HashMap<>());
|
||||
|
||||
// 避免重复添加相同称号
|
||||
if (!tags.contains(tag)) {
|
||||
tags.add(tag);
|
||||
long expireTime;
|
||||
if (days == 999) {
|
||||
expireTime = Long.MAX_VALUE; // 999天视为永久
|
||||
expireTime = Long.MAX_VALUE;
|
||||
} else if (days <= 0) {
|
||||
expireTime = System.currentTimeMillis() - 1; // 天数≤0:立即过期(触发清理)
|
||||
expireTime = System.currentTimeMillis() - 1;
|
||||
} else {
|
||||
// 计算有效期:当前时间 + 天数×24×60×60×1000(转换为毫秒)
|
||||
expireTime = System.currentTimeMillis() + (long) days * 24 * 60 * 60 * 1000;
|
||||
}
|
||||
tagExpires.put(tag, expireTime);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除称号:同时删除过期时间记录
|
||||
*/
|
||||
public void removeTag(Player player, String tag) {
|
||||
cleanExpiredTags(player);
|
||||
UUID uuid = player.getUniqueId();
|
||||
List<String> tags = playerTags.get(uuid);
|
||||
Map<String, Long> tagExpires = expireTimeMap.get(uuid);
|
||||
|
||||
// 移除称号列表和过期时间映射中的对应条目
|
||||
if (tags != null) {
|
||||
tags.remove(tag);
|
||||
if (tags.isEmpty()) {
|
||||
@@ -293,7 +252,6 @@ public class PlayerTags {
|
||||
}
|
||||
}
|
||||
|
||||
// 若移除的是当前选中称号,重置索引
|
||||
int currentIndex = playerselectTag.getOrDefault(uuid, -1);
|
||||
List<String> validTags = playerTags.getOrDefault(uuid, new ArrayList<>());
|
||||
if (currentIndex != -1 && (currentIndex >= validTags.size() || !validTags.contains(tag))) {
|
||||
@@ -301,30 +259,20 @@ public class PlayerTags {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增:获取称号的到期时间(返回时间戳,单位毫秒)
|
||||
*
|
||||
* @return 永久返回-1,过期/不存在返回-2,有效返回到期时间戳
|
||||
*/
|
||||
public long getTagExpireTime(Player player, String tag) {
|
||||
cleanExpiredTags(player);
|
||||
UUID uuid = player.getUniqueId();
|
||||
Map<String, Long> tagExpires = expireTimeMap.getOrDefault(uuid, new HashMap<>());
|
||||
List<String> validTags = playerTags.getOrDefault(uuid, new ArrayList<>());
|
||||
|
||||
// 称号不存在或已过期,返回-2
|
||||
if (!validTags.contains(tag)) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
long expireTime = tagExpires.getOrDefault(tag, Long.MAX_VALUE);
|
||||
// 永久称号返回-1,否则返回时间戳
|
||||
return expireTime == Long.MAX_VALUE ? -1 : expireTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增:获取称号的剩余时间(返回字符串,如“2天3小时”)
|
||||
*/
|
||||
public String getTagRemainingTime(Player player, String tag) {
|
||||
long expireTime = getTagExpireTime(player, tag);
|
||||
if (expireTime == -1) {
|
||||
@@ -334,18 +282,15 @@ public class PlayerTags {
|
||||
return "已过期/不存在";
|
||||
}
|
||||
|
||||
// 计算剩余毫秒数(可能为负,需处理)
|
||||
long remainingMs = expireTime - System.currentTimeMillis();
|
||||
if (remainingMs <= 0) {
|
||||
return "已过期";
|
||||
}
|
||||
|
||||
// 转换为天、时、分
|
||||
long days = remainingMs / (24 * 60 * 60 * 1000);
|
||||
long hours = (remainingMs % (24 * 60 * 60 * 1000)) / (60 * 60 * 1000);
|
||||
long minutes = (remainingMs % (60 * 60 * 1000)) / (60 * 1000);
|
||||
|
||||
// 拼接剩余时间字符串
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (days > 0) sb.append(days).append("天");
|
||||
if (hours > 0) sb.append(hours).append("小时");
|
||||
|
||||
@@ -1,70 +1,415 @@
|
||||
package org.xgqy.survival;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.xgqy.survival.command.DqshopCommandExecutor;
|
||||
import org.xgqy.survival.command.FriendCommandExecutor;
|
||||
import org.xgqy.survival.command.HandleCommandExecutor;
|
||||
import org.xgqy.survival.command.HelpCommandExecutor;
|
||||
import org.xgqy.survival.command.HubCommandExecutor;
|
||||
import org.xgqy.survival.command.InventoryCommandExecutor;
|
||||
import org.xgqy.survival.command.KillCommandExecutor;
|
||||
import org.xgqy.survival.command.LandCommandExecutor;
|
||||
import org.xgqy.survival.command.LoginCommandExecutor;
|
||||
import org.xgqy.survival.command.PartyCommandExecutor;
|
||||
import org.xgqy.survival.command.PcCommandExecutor;
|
||||
import org.xgqy.survival.command.PointCommandExecutor;
|
||||
import org.xgqy.survival.command.PvpCommandExecutor;
|
||||
import org.xgqy.survival.command.RegCommandExecutor;
|
||||
import org.xgqy.survival.command.ReportCommandExecutor;
|
||||
import org.xgqy.survival.command.SetTagCommandExecutor;
|
||||
import org.xgqy.survival.command.TagCommandExecutor;
|
||||
import org.xgqy.survival.command.TeleportCommandExecutor;
|
||||
import org.xgqy.survival.command.TpAccCommandExecutor;
|
||||
import org.xgqy.survival.command.TpFinCommandExecutor;
|
||||
import org.xgqy.survival.command.msgCommandExecutor;
|
||||
import org.xgqy.survival.event.ChatEvent;
|
||||
import org.xgqy.survival.event.ChooseTagEvent;
|
||||
import org.xgqy.survival.event.JoinEvent;
|
||||
import org.xgqy.survival.event.LandEvent;
|
||||
import org.xgqy.survival.event.LoginEvent;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public final class Survival extends JavaPlugin {
|
||||
private PlayerTags playerTags;
|
||||
private AccountManager accountManager;
|
||||
public Map<Player, Boolean> krt = new HashMap<>();
|
||||
public Map<Player, Player> banlist = new HashMap<>();
|
||||
public Map<Player, String> banreason = new HashMap<>();
|
||||
public Map<Player, HashSet<Player>> reportlist = new HashMap<>();
|
||||
// Teleport
|
||||
public Map<Player, Player> teleport = new HashMap<>();
|
||||
public Map<Player, Player> Ateleport = new HashMap<>();
|
||||
public Map<Player, Location> teleportp = new HashMap<>();
|
||||
public Map<Player, Integer> isteleport = new HashMap<>();
|
||||
public Map<Player, Long> lasttp = new HashMap<>();
|
||||
public Map<Player, Location> area = new HashMap<>();
|
||||
/*
|
||||
----------------------------------------
|
||||
party
|
||||
----------------------------------------
|
||||
*/
|
||||
public Map<Player, Integer> party = new HashMap<>();
|
||||
public Map<Integer, List<Player>> partyp = new HashMap<>();
|
||||
public Map<Integer, Player> owner = new HashMap<>();
|
||||
public Map<Integer, List<Player>> ban = new HashMap<>();
|
||||
/*
|
||||
----------------------------------------
|
||||
friend
|
||||
----------------------------------------
|
||||
*/
|
||||
public Map<Player, List<Player>> friends = new ConcurrentHashMap<>();
|
||||
public Map<Player, List<Player>> waiting = new ConcurrentHashMap<>();
|
||||
/*
|
||||
----------------------------------------
|
||||
信用分
|
||||
----------------------------------------
|
||||
*/
|
||||
public Map<Player, Integer> ppoint = new HashMap<>();
|
||||
private List<String> msg = new ArrayList<>();
|
||||
public Map<Player, String> breason = new HashMap<>();
|
||||
/*
|
||||
----------------------------------------
|
||||
领地
|
||||
----------------------------------------
|
||||
*/
|
||||
public static class Land{
|
||||
public Location start,end;
|
||||
public List<Boolean> defaultperm;
|
||||
public String owner;
|
||||
public Map<String,List<Boolean>> perm;
|
||||
public boolean boomb;
|
||||
public Land(Location start,Location end,List<Boolean> defaultperm,String owner,Map<String,List<Boolean>> perm){
|
||||
this.start=start;
|
||||
this.end=end;
|
||||
this.defaultperm = defaultperm;
|
||||
this.owner=owner;
|
||||
this.perm=perm;
|
||||
}
|
||||
public Boolean getBoomb(){
|
||||
return boomb;
|
||||
}
|
||||
public void setBoomb(Boolean boomb){
|
||||
this.boomb=boomb;
|
||||
}
|
||||
public Location getStart(){
|
||||
return start;
|
||||
}
|
||||
public Location getEnd(){
|
||||
return end;
|
||||
}
|
||||
public List<Boolean> getDefaultperm(){
|
||||
return defaultperm;
|
||||
}
|
||||
public String getOwner(){
|
||||
return owner;
|
||||
}
|
||||
public List<Boolean> getPerm(Player player){
|
||||
return perm.getOrDefault(player,null);
|
||||
}
|
||||
public void setStart(Location start){
|
||||
this.start=start;
|
||||
}
|
||||
public void setEnd(Location end) {
|
||||
this.end = end;
|
||||
}
|
||||
public void setDefaultperm(List<Boolean> defaultperm){
|
||||
this.defaultperm=defaultperm;
|
||||
}
|
||||
public void setOwner(String owner){
|
||||
this.owner=owner;
|
||||
}
|
||||
public void setPerm(Player player,List<Boolean> perm){
|
||||
this.perm.put(player.getName(),perm);
|
||||
}
|
||||
}
|
||||
public List<Land> land =new ArrayList<>();
|
||||
|
||||
public Map<Player,String> inland=new HashMap<>();
|
||||
// 信用分数据文件相关
|
||||
private File ppointFile;
|
||||
private FileConfiguration ppointConfig;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
// Plugin startup logic
|
||||
// 初始化账号管理器
|
||||
accountManager = new AccountManager(this);
|
||||
|
||||
// 初始化信用分数据文件
|
||||
initPpointFile();
|
||||
// 加载已在线玩家的信用分数据
|
||||
loadPpoint();
|
||||
|
||||
msg.add(ChatColor.GREEN + "可以用 /help 命令来查看命令列表");
|
||||
msg.add(ChatColor.GREEN + "如果遇到作弊玩家可以用 /report <玩家名> <原因> 来举报!管理员会很快处理的");
|
||||
msg.add(ChatColor.GREEN + "想传送到玩家怎么办? 用 /teleport <玩家名> 命令!");
|
||||
msg.add(ChatColor.GREEN + "更换IP但没有更换账号导致的封禁不能找管理员解决");
|
||||
msg.add(ChatColor.GREEN + "星阁钱语的名称其实叫星阁浅语");
|
||||
msg.add(ChatColor.RED + "作弊会导致账号封禁!请不要作弊");
|
||||
msg.add(ChatColor.RED + "在服务器内说不文明用语会导致账号被封禁,请不要这样做");
|
||||
msg.add(ChatColor.GREEN + "今天签到了吗?快输入 /qd 签到吧!");
|
||||
msg.add(ChatColor.GREEN + "其实可以输入 /pvp 来开启/关闭自己的pvp模式");
|
||||
msg.add(ChatColor.RED + "你不可以把别人的家毁掉!这样你将会受到处罚");
|
||||
msg.add(ChatColor.GREEN + "每日坚持签到可以获得信用分");
|
||||
msg.add(ChatColor.GREEN + "服务器官网: https://starpavilion.xyz");
|
||||
msg.add(ChatColor.GREEN + "赞助我们? https://starpavilion.xyz/sponsor.html");
|
||||
msg.add(ChatColor.RED+"封禁者会在此处公示: https://starpavilion.xyz/bans.html");
|
||||
msg.add(ChatColor.RED + "信用分过低将会导致你的账号受限甚至永久封禁!");
|
||||
msg.add(ChatColor.GREEN + "信用分被扣除会提醒,如果您的信用分被莫名扣除,请及时到QQ群 717903781 申诉。");
|
||||
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (Player plr : Bukkit.getOnlinePlayers()) {
|
||||
// 只向已登录玩家发送提示
|
||||
if (accountManager.isLoggedIn(plr)) {
|
||||
plr.sendMessage(ChatColor.LIGHT_PURPLE + "小提示 " + ChatColor.WHITE + " | " + msg.get((int) (Math.random() * 10000) % msg.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}.runTaskLater(this, 20 * 60);
|
||||
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (Player plr : Bukkit.getOnlinePlayers()) {
|
||||
// 检查玩家是否有违规扣分记录
|
||||
if (plr.getScoreboard().getObjective("handled") != null &&
|
||||
plr.getScoreboard().getObjective("handled").getScore(plr).getScore() != 0) {
|
||||
|
||||
ZonedDateTime beijingTime = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
String formattedTime = beijingTime.format(formatter);
|
||||
|
||||
// 确保玩家信用分已初始化
|
||||
int currentPoint = ppoint.getOrDefault(plr, 100);
|
||||
int deductPoint = plr.getScoreboard().getObjective("handled").getScore(plr).getScore();
|
||||
int newPoint = currentPoint - deductPoint;
|
||||
//ppoint.put(plr, newPoint);
|
||||
|
||||
// 发送扣分提示
|
||||
plr.sendMessage(ChatColor.RED + "警告" + ChatColor.WHITE + " | " + ChatColor.RED + "您的账号 " + ChatColor.YELLOW + plr.getName() + ChatColor.RED + " 在 " + ChatColor.WHITE + formattedTime + ChatColor.RED + " 有疑似违规行为,扣取信用分 " + ChatColor.YELLOW + deductPoint + ChatColor.RED + " 分。\n");
|
||||
plr.sendMessage(ChatColor.RED + "-----------警告单-----------");
|
||||
plr.sendMessage(ChatColor.RED + "|账号名: " + ChatColor.YELLOW + plr.getName());
|
||||
plr.sendMessage(ChatColor.RED + "|违规时间: " + ChatColor.YELLOW + formattedTime);
|
||||
plr.sendMessage(ChatColor.RED + "|违规行为: " + ChatColor.YELLOW + breason.getOrDefault(plr, "管理员扣分"));
|
||||
plr.sendMessage(ChatColor.RED + "|违规扣分: " + ChatColor.YELLOW + deductPoint + " 分");
|
||||
plr.sendMessage(ChatColor.RED + "|剩余信用分: " + ChatColor.YELLOW + newPoint);
|
||||
|
||||
// 根据剩余信用分执行处罚
|
||||
if (newPoint > 80) {
|
||||
plr.sendMessage(ChatColor.RED + "|采取处罚: " + ChatColor.YELLOW + "无");
|
||||
} else if (newPoint <= 0) {
|
||||
plr.kickPlayer(ChatColor.RED + "您好! 您由于在 " + formattedTime + " 信用分小于 0 ,我们决定对你的账号 " + plr.getName() + " 采取\n" + ChatColor.RED + ChatColor.BOLD + "永久封禁\n" + ChatColor.RED + "措施,如果您想解封您的账号,请到QQ群 717903781 申诉\n\n" + ChatColor.RED + "如果您对本次处罚不满,请采取以下措施:\n" + ChatColor.YELLOW + "1.如果您对自己的行为" + ChatColor.BOLD + " 问心无愧 " + ChatColor.YELLOW + "请立即向QQ 2213866559 发送解封申请\n" + ChatColor.YELLOW + "2.如果您属实有违规行为,请" + ChatColor.BOLD + "手写" + ChatColor.YELLOW + "一篇 " + ChatColor.BOLD + "100字以上且AIGC合格" + ChatColor.YELLOW + " 的检讨发送到申诉QQ群,态度诚恳我们将会对你进行解封");
|
||||
} else if (newPoint <= 20) {
|
||||
plr.sendMessage(ChatColor.RED + "|采取处罚: " + ChatColor.YELLOW + "限制行为及发言,列入监管名单,限制功能使用");
|
||||
} else if (newPoint <= 50) {
|
||||
plr.sendMessage(ChatColor.RED + "|采取处罚: " + ChatColor.YELLOW + "限制行为及发言,限制功能使用");
|
||||
} else if (newPoint <= 80) {
|
||||
plr.sendMessage(ChatColor.RED + "|采取处罚: " + ChatColor.YELLOW + "监管行为及发言,签到将奖励转换为信用分");
|
||||
}
|
||||
plr.sendMessage(ChatColor.RED + "|为了维护游戏环境,请不要违规!");
|
||||
plr.sendMessage(ChatColor.RED + "----------------------------");
|
||||
|
||||
// 清理临时数据
|
||||
breason.remove(plr);
|
||||
plr.getScoreboard().getObjective("handled").getScore(plr).setScore(0);
|
||||
|
||||
// 实时保存扣分后的信用分数据
|
||||
savePpoint();
|
||||
}
|
||||
}
|
||||
}
|
||||
}.runTaskTimer(this, 20 * 5, 20 * 5);
|
||||
|
||||
// 初始化玩家标签
|
||||
playerTags = new PlayerTags(this);
|
||||
playerTags.loadTags();
|
||||
|
||||
// 注册事件监听器
|
||||
Bukkit.getPluginManager().registerEvents(new JoinEvent(this), this);
|
||||
Bukkit.getPluginManager().registerEvents(new ChatEvent(this), this);
|
||||
Bukkit.getPluginManager().registerEvents(new ChooseTagEvent(this), this);
|
||||
//Bukkit.getPluginManager().registerEvents(new AntiXray(this),this);
|
||||
Bukkit.getPluginManager().registerEvents(new LoginEvent(this), this);
|
||||
Bukkit.getPluginManager().registerEvents(new LandEvent(this),this);
|
||||
|
||||
// 注册命令执行器
|
||||
getCommand("report").setExecutor(new ReportCommandExecutor(this));
|
||||
getCommand("handle").setExecutor(new HandleCommandExecutor(this));
|
||||
getCommand("pvp").setExecutor(new PvpCommandExecutor(this));
|
||||
getCommand("settag").setExecutor(new SetTagCommandExecutor(this));
|
||||
getCommand("help").setExecutor(new HelpCommandExecutor(this));
|
||||
//getCommand("fly").setExecutor(new FlyCommandExecutor(this));
|
||||
getCommand("tag").setExecutor(new TagCommandExecutor(this));
|
||||
getCommand("hub").setExecutor(new HubCommandExecutor(this));
|
||||
getCommand("teleport").setExecutor(new TeleportCommandExecutor(this));
|
||||
getCommand("tpacc").setExecutor(new TpAccCommandExecutor(this));
|
||||
getCommand("tpfin").setExecutor(new TpFinCommandExecutor(this));
|
||||
getCommand("shop").setExecutor(new DqshopCommandExecutor(this));
|
||||
getCommand("reg").setExecutor(new RegCommandExecutor(this));
|
||||
getCommand("login").setExecutor(new LoginCommandExecutor(this));
|
||||
getCommand("party").setExecutor(new PartyCommandExecutor(this));
|
||||
getCommand("inventory").setExecutor(new InventoryCommandExecutor(this));
|
||||
getCommand("pc").setExecutor(new PcCommandExecutor(this));
|
||||
getCommand("msg").setExecutor(new msgCommandExecutor(this));
|
||||
getCommand("friend").setExecutor(new FriendCommandExecutor(this));
|
||||
getCommand("point").setExecutor(new PointCommandExecutor(this));
|
||||
getCommand("selfkill").setExecutor(new KillCommandExecutor(this));
|
||||
getCommand("land").setExecutor(new LandCommandExecutor(this));
|
||||
getCommand("qd").setExecutor(new LandCommandExecutor(this));
|
||||
getLogger().info("Survival插件已启用,信用分持久化功能加载完成!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
// 保存玩家标签数据
|
||||
playerTags.saveTags();
|
||||
// Plugin shutdown logic
|
||||
|
||||
// 保存信用分数据
|
||||
savePpoint();
|
||||
|
||||
// 保存好友数据
|
||||
try {
|
||||
FriendCommandExecutor executor = (FriendCommandExecutor) this.getCommand("friend").getExecutor();
|
||||
if (executor != null) {
|
||||
executor.saveFriendData();
|
||||
getLogger().info("好友数据已保存!");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
getLogger().severe("插件卸载时保存好友数据失败:" + e.getMessage());
|
||||
}
|
||||
|
||||
// 保存账号信息
|
||||
accountManager.saveAccounts();
|
||||
|
||||
getLogger().info("Survival插件已禁用,所有数据已保存!");
|
||||
}
|
||||
|
||||
// 初始化信用分数据文件
|
||||
private void initPpointFile() {
|
||||
// 创建插件数据文件夹(若不存在)
|
||||
File dataFolder = getDataFolder();
|
||||
if (!dataFolder.exists()) {
|
||||
if (dataFolder.mkdirs()) {
|
||||
getLogger().info("插件数据文件夹创建成功!");
|
||||
} else {
|
||||
getLogger().severe("插件数据文件夹创建失败!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 创建信用分文件(路径:plugins/Survival/ppoint.yml)
|
||||
ppointFile = new File(dataFolder, "ppoint.yml");
|
||||
if (!ppointFile.exists()) {
|
||||
try {
|
||||
if (ppointFile.createNewFile()) {
|
||||
getLogger().info("信用分数据文件创建成功!");
|
||||
} else {
|
||||
getLogger().severe("信用分数据文件创建失败!");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
getLogger().severe("创建信用分文件时发生异常:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 加载配置文件
|
||||
ppointConfig = YamlConfiguration.loadConfiguration(ppointFile);
|
||||
}
|
||||
|
||||
// 保存信用分数据到文件(关键修正处)
|
||||
public void savePpoint() {
|
||||
if (ppointConfig == null || ppointFile == null) {
|
||||
getLogger().severe("信用分配置未初始化,保存失败!");
|
||||
return;
|
||||
}
|
||||
|
||||
// 修正:清空原有配置的正确方式(将所有键设为null或移除)
|
||||
for (String key : ppointConfig.getKeys(false)) {
|
||||
ppointConfig.set(key, null); // 用null覆盖原有值,或用 ppointConfig.remove(key) 直接移除键
|
||||
}
|
||||
|
||||
// 将在线玩家的信用分存入配置(用UUID作为键,参数完整)
|
||||
for (Map.Entry<Player, Integer> entry : ppoint.entrySet()) {
|
||||
Player player = entry.getKey();
|
||||
if (player.isOnline()) {
|
||||
UUID playerUuid = player.getUniqueId();
|
||||
ppointConfig.set(playerUuid.toString(), entry.getValue()); // 完整参数:路径(UUID字符串)+ 值(信用分)
|
||||
}
|
||||
}
|
||||
|
||||
// 写入文件
|
||||
try {
|
||||
ppointConfig.save(ppointFile);
|
||||
getLogger().info("信用分数据保存成功!");
|
||||
} catch (IOException e) {
|
||||
getLogger().severe("保存信用分数据失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 加载信用分数据(仅加载在线玩家)
|
||||
private void loadPpoint() {
|
||||
if (ppointConfig == null || ppointFile == null) {
|
||||
getLogger().severe("信用分配置未初始化,加载失败!");
|
||||
return;
|
||||
}
|
||||
|
||||
// 遍历配置中的所有UUID,为在线玩家分配信用分
|
||||
for (String uuidStr : ppointConfig.getKeys(false)) {
|
||||
try {
|
||||
UUID playerUuid = UUID.fromString(uuidStr);
|
||||
Player player = Bukkit.getPlayer(playerUuid);
|
||||
if (player != null && player.isOnline()) {
|
||||
// 读取信用分(默认100分)
|
||||
int point = ppointConfig.getInt(uuidStr, 100);
|
||||
ppoint.put(player, point);
|
||||
getLogger().info("加载玩家 " + player.getName() + " 的信用分:" + point);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
getLogger().warning("无效的UUID格式:" + uuidStr + ",跳过该数据");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 玩家上线时加载其信用分(供JoinEvent/LoginEvent调用)
|
||||
public void loadPlayerPpoint(Player player) {
|
||||
if (ppointConfig == null) {
|
||||
getLogger().severe("信用分配置未初始化,无法加载玩家 " + player.getName() + " 的信用分!");
|
||||
return;
|
||||
}
|
||||
|
||||
UUID playerUuid = player.getUniqueId();
|
||||
String uuidStr = playerUuid.toString();
|
||||
|
||||
// 读取已有信用分,无数据则设为默认100分
|
||||
int point = ppointConfig.getInt(uuidStr, 100);
|
||||
ppoint.put(player, point);
|
||||
getLogger().info("玩家 " + player.getName() + " 上线,加载信用分:" + point);
|
||||
}
|
||||
|
||||
// 获取玩家标签管理器
|
||||
public PlayerTags getPlayerTags() {
|
||||
return playerTags;
|
||||
}
|
||||
}
|
||||
|
||||
// 获取账号管理器
|
||||
public AccountManager getAccountManager() {
|
||||
return accountManager;
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,12 @@
|
||||
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.meta.ItemMeta;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.xgqy.survival.Survival;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class DqshopCommandExecutor implements CommandExecutor {
|
||||
|
||||
private Survival plugin;
|
||||
@@ -26,8 +17,10 @@ public class DqshopCommandExecutor implements CommandExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (sender instanceof Player) {
|
||||
Inventory shopGui = Bukkit.createInventory(null, 54, ChatColor.GOLD + "商城");
|
||||
//if (sender instanceof Player) {
|
||||
sender.sendMessage(ChatColor.RED+"本功能已废弃!请前往官网 https://starpavilion.xyz");
|
||||
return true;
|
||||
/*Inventory shopGui = Bukkit.createInventory(null, 54, ChatColor.GOLD + "商城");
|
||||
ItemStack head = new ItemStack(Material.PLAYER_HEAD);
|
||||
ItemMeta headmeta = head.getItemMeta();
|
||||
List<String> meta1 = new ArrayList<>();
|
||||
@@ -181,6 +174,6 @@ public class DqshopCommandExecutor implements CommandExecutor {
|
||||
sender.sendMessage(ChatColor.RED + "仅玩家能使用该命令!");
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
return true;*/
|
||||
}
|
||||
}
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,7 @@ public class HandleCommandExecutor implements CommandExecutor {
|
||||
args[0] + ChatColor.GREEN + " 已经被封禁\n" + ChatColor.LIGHT_PURPLE + "举报系统" + ChatColor.WHITE + " | \n" + ChatColor.GREEN + " 感谢您为维护游戏平衡做贡献!");
|
||||
}
|
||||
plugin.reportlist.put(Bukkit.getPlayer(args[0]), null);
|
||||
banlis.addBan(args[0], ChatColor.AQUA + "星阁钱语\n" + ChatColor.RED + "您的账号 " + args[0] + " 已被封禁\n原因: 管理员处理作弊行为", expdat, null);
|
||||
banlis.addBan(args[0], ChatColor.AQUA + "\n\n星阁钱语\n" + ChatColor.RED + "您的账号 " + args[0] + " 已被封禁\n原因: 管理员处理作弊行为", expdat, null);
|
||||
Bukkit.getPlayer(args[0]).kickPlayer(ChatColor.AQUA + "星阁钱语\n" + ChatColor.RED + "你被封禁了" + ChatColor.BOLD + ChatColor.YELLOW + " 1 " + ChatColor.RED + "天\n被封禁的账号: " + ChatColor.RED + ChatColor.BOLD + args[0] + ChatColor.RED + "\n封禁原因:" + "管理员处理");
|
||||
} else {
|
||||
return true;
|
||||
|
||||
@@ -21,9 +21,14 @@ public class HelpCommandExecutor implements CommandExecutor {
|
||||
sender.sendMessage(ChatColor.GREEN + "/pvp - 开启/关闭 玩家伤害");
|
||||
sender.sendMessage(ChatColor.GREEN + "/tag - 选择称号");
|
||||
sender.sendMessage(ChatColor.GREEN + "/teleport <玩家名> - 玩家传送");
|
||||
sender.sendMessage(ChatColor.GREEN + "/teleport <玩家名> me - 玩家传送");
|
||||
sender.sendMessage(ChatColor.GREEN + "/report - 举报作弊玩家");
|
||||
sender.sendMessage(ChatColor.GREEN + "/tpacc <accept|deny> - 同意/拒绝玩家传送");
|
||||
sender.sendMessage(ChatColor.GREEN + "/tpfin - 结束传送");
|
||||
sender.sendMessage(ChatColor.GREEN+"/login <密码> -登录");
|
||||
sender.sendMessage(ChatColor.GREEN+"/reg <密码> <密码> - 注册");
|
||||
sender.sendMessage(ChatColor.GREEN+"/party <参数> <参数> - 队伍");
|
||||
sender.sendMessage(ChatColor.GREEN+"/friend <参数> <参数> - 好友");
|
||||
sender.sendMessage(ChatColor.GREEN+"/msg <内容> - 私聊");
|
||||
sender.sendMessage(ChatColor.GREEN+"/selfkill - 自杀");
|
||||
sender.sendMessage(ChatColor.YELLOW + "-----------------------------");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ public class PvpCommandExecutor implements CommandExecutor {
|
||||
if (plugin.krt.getOrDefault(sender, false) == true) {
|
||||
plugin.krt.put((Player) sender, false);
|
||||
sender.sendMessage(ChatColor.GREEN + "已切换PVP模式到: 关");
|
||||
//
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.GREEN + "已切换PVP模式到: 开");
|
||||
plugin.krt.put((Player) sender, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ public class TeleportCommandExecutor implements CommandExecutor {
|
||||
sender.sendMessage(ChatColor.RED + "传送正在冷却,剩余: " + (4 - (System.currentTimeMillis() - plugin.lasttp.getOrDefault(sender, 0L)) / 1000 / 60) + " 分 " + (60 - ((System.currentTimeMillis() - plugin.lasttp.getOrDefault(sender, 0L)) / 1000) % 60) + " 秒 ");
|
||||
return true;
|
||||
}
|
||||
if (args.length != 1) {
|
||||
if (args.length != 1 && args.length != 2) {
|
||||
sender.sendMessage(ChatColor.RED + "参数错误!用法: /teleport <玩家名>");
|
||||
return true;
|
||||
} else {
|
||||
@@ -37,6 +37,41 @@ public class TeleportCommandExecutor implements CommandExecutor {
|
||||
sender.sendMessage(ChatColor.RED + "该玩家不在线!");
|
||||
return true;
|
||||
}
|
||||
if (args.length == 2) {
|
||||
if (args[1].contains("me")) {
|
||||
playerto = Bukkit.getPlayer(args[0]);
|
||||
if (plugin.teleport.getOrDefault(playerto, null) != null) {
|
||||
sender.sendMessage(ChatColor.RED + "对方已经发送了一个请求,请等待");
|
||||
return true;
|
||||
}
|
||||
if (plugin.Ateleport.getOrDefault(sender, null) != null) {
|
||||
sender.sendMessage(ChatColor.RED + "你还有一个未处理的请求!");
|
||||
return true;
|
||||
}
|
||||
sender.sendMessage(ChatColor.GREEN + "请求已发送");
|
||||
playerto.sendMessage(ChatColor.GREEN + "玩家 " + sender.getName() + ChatColor.GREEN + " 向你发来传送请求,输入 /tpacc tome 同意这个请求或 /tpacc detome 来拒绝这个请求,有效期2分钟。");
|
||||
BaseComponent[] message = new ComponentBuilder(ChatColor.YELLOW + "玩家 " + sender.getName() + ChatColor.YELLOW + " 向你发来传送请求\n")
|
||||
.append(new ComponentBuilder(ChatColor.GREEN + "[同意] ").event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/tpacc tome")).create())
|
||||
.append(new ComponentBuilder(ChatColor.RED + "[拒绝]").event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/tpacc detome")).create())
|
||||
.create();
|
||||
playerto.spigot().sendMessage(message);
|
||||
plugin.Ateleport.put((Player) sender, playerto);
|
||||
plugin.teleport.put(playerto, (Player) sender);
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (plugin.teleportp.getOrDefault(playerto, null) == null)
|
||||
playerto.sendMessage(ChatColor.RED + "玩家 " + sender.getName() + ChatColor.RED + " 的传送请求已过期");
|
||||
if (plugin.teleportp.getOrDefault(playerto, null) == null)
|
||||
sender.sendMessage(ChatColor.RED + "你向 " + playerto.getName() + ChatColor.RED + " 发送的传送请求已过期");
|
||||
plugin.Ateleport.remove((Player) sender);
|
||||
plugin.teleport.remove(playerto);
|
||||
plugin.isteleport.remove(playerto);
|
||||
}
|
||||
}.runTaskLater(plugin, 40 * 60);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
playerto = Bukkit.getPlayer(args[0]);
|
||||
if (plugin.teleport.getOrDefault((Player) sender, null) != null) {
|
||||
playerto.sendMessage(ChatColor.RED + "您已经向 " + plugin.teleport.get((Player) sender).getName() + ChatColor.RED + " 发送了一份请求");
|
||||
|
||||
@@ -11,7 +11,7 @@ import org.xgqy.survival.Survival;
|
||||
|
||||
public class TpAccCommandExecutor implements CommandExecutor {
|
||||
|
||||
private Survival plugin;
|
||||
private final Survival plugin;
|
||||
|
||||
public TpAccCommandExecutor(Survival plugin) {
|
||||
this.plugin = plugin;
|
||||
@@ -19,48 +19,159 @@ public class TpAccCommandExecutor implements CommandExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (sender instanceof Player) {
|
||||
if (plugin.Ateleport.getOrDefault(sender, null) != null) {
|
||||
if (args[0].contains("accept")) {
|
||||
sender.sendMessage(ChatColor.GREEN + "传送成功!");
|
||||
plugin.Ateleport.get(sender).sendMessage(ChatColor.GREEN + "传送成功!输入 /tpfin 回到原处,该命令将会在5分钟后过期。");
|
||||
plugin.teleportp.put(plugin.Ateleport.get(sender), plugin.Ateleport.get(sender).getLocation());
|
||||
plugin.Ateleport.get(sender).teleport(((Player) sender).getLocation());
|
||||
plugin.isteleport.put(plugin.Ateleport.get(sender), 1);
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (plugin.isteleport.get(sender) != 1)
|
||||
plugin.Ateleport.get(sender).sendMessage(ChatColor.RED + "返回命令已过期,你将无法返回原处!");
|
||||
plugin.teleportp.remove(plugin.Ateleport.get(sender));
|
||||
plugin.teleport.remove(plugin.Ateleport.get(sender));
|
||||
plugin.isteleport.remove(plugin.Ateleport.get(sender));
|
||||
plugin.Ateleport.remove(sender);
|
||||
}
|
||||
}.runTaskLater(plugin, 5 * 20 * 60);
|
||||
return true;
|
||||
} else if (args[0].contains("deny")) {
|
||||
sender.sendMessage(ChatColor.GREEN + "拒绝成功");
|
||||
plugin.Ateleport.get(sender).sendMessage(ChatColor.GREEN + "您的传送请求被拒绝");
|
||||
//plugin.teleportp.put(plugin.Ateleport.get(sender),plugin.Ateleport.get(sender).getLocation());
|
||||
plugin.teleportp.remove(plugin.Ateleport.get(sender));
|
||||
plugin.teleport.remove(plugin.Ateleport.get(sender));
|
||||
plugin.isteleport.remove(plugin.Ateleport.get(sender));
|
||||
plugin.Ateleport.remove(sender);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.RED + "参数错误,请输入 /tpacc accept 或 /tpacc deny");
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.RED + "现在没有传送申请或申请已过期");
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.RED + "无法对非玩家类使用");
|
||||
// 仅允许玩家执行命令
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage(ChatColor.RED + "无法对非玩家使用此命令");
|
||||
return true;
|
||||
}
|
||||
//return true;
|
||||
|
||||
// 检查玩家是否已登录
|
||||
if (!plugin.getAccountManager().isLoggedIn(player)) {
|
||||
player.sendMessage(ChatColor.RED + "请先登录或注册!");
|
||||
return true;
|
||||
}
|
||||
|
||||
// 参数长度校验(必须为1个参数)
|
||||
if (args.length != 1) {
|
||||
player.sendMessage(ChatColor.RED + "请输入正确参数!用法: /tpacc <accept|deny|tome|detome>");
|
||||
return true;
|
||||
}
|
||||
|
||||
// 处理不同参数逻辑
|
||||
String action = args[0];
|
||||
switch (action) {
|
||||
case "accept":
|
||||
handleAccept(player);
|
||||
break;
|
||||
case "deny":
|
||||
handleDeny(player);
|
||||
break;
|
||||
case "tome":
|
||||
handleTome(player);
|
||||
break;
|
||||
case "detome":
|
||||
handleDetome(player);
|
||||
break;
|
||||
default:
|
||||
player.sendMessage(ChatColor.RED + "参数错误!请使用 /tpacc <accept|deny|tome|detome>");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理接受对方传送至自己的请求
|
||||
*/
|
||||
private void handleAccept(Player receiver) {
|
||||
// 获取请求传送的玩家(Ateleport: 接收者 -> 请求者)
|
||||
Player requester = plugin.Ateleport.get(receiver);
|
||||
if (requester == null || !requester.isOnline()) {
|
||||
receiver.sendMessage(ChatColor.RED + "传送请求已过期或不存在");
|
||||
return;
|
||||
}
|
||||
|
||||
// 执行传送逻辑
|
||||
receiver.sendMessage(ChatColor.GREEN + "已同意传送请求!");
|
||||
requester.sendMessage(ChatColor.GREEN + "对方已同意传送!输入 /tpfin 可在5分钟内返回原处");
|
||||
|
||||
// 保存请求者原始位置
|
||||
plugin.teleportp.put(requester, requester.getLocation());
|
||||
// 传送请求者到接收者位置
|
||||
requester.teleport(receiver.getLocation());
|
||||
plugin.isteleport.put(requester, 1);
|
||||
|
||||
// 5分钟后清理传送状态
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (plugin.isteleport.getOrDefault(requester, 0) != 1) {
|
||||
requester.sendMessage(ChatColor.RED + "返回命令已过期,无法再返回原处");
|
||||
}
|
||||
// 清理相关映射
|
||||
plugin.teleportp.remove(requester);
|
||||
plugin.teleport.remove(requester);
|
||||
plugin.isteleport.remove(requester);
|
||||
plugin.Ateleport.remove(receiver);
|
||||
}
|
||||
}.runTaskLater(plugin, 5 * 60 * 20); // 5分钟(20tick=1秒)
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理拒绝对方传送至自己的请求
|
||||
*/
|
||||
private void handleDeny(Player receiver) {
|
||||
Player requester = plugin.Ateleport.get(receiver);
|
||||
if (requester == null || !requester.isOnline()) {
|
||||
receiver.sendMessage(ChatColor.RED + "传送请求已过期或不存在");
|
||||
return;
|
||||
}
|
||||
|
||||
// 发送拒绝消息
|
||||
receiver.sendMessage(ChatColor.GREEN + "已拒绝传送请求");
|
||||
requester.sendMessage(ChatColor.RED + "对方已拒绝你的传送请求");
|
||||
|
||||
// 清理相关映射
|
||||
plugin.teleportp.remove(requester);
|
||||
plugin.teleport.remove(requester);
|
||||
plugin.isteleport.remove(requester);
|
||||
plugin.Ateleport.remove(receiver);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理同意自己传送至对方的请求
|
||||
*/
|
||||
private void handleTome(Player requester) {
|
||||
// 获取请求接收者(teleport: 请求者 -> 接收者)
|
||||
Player receiver = plugin.teleport.get(requester);
|
||||
if (receiver == null || !receiver.isOnline()) {
|
||||
requester.sendMessage(ChatColor.RED + "传送请求已过期或不存在");
|
||||
return;
|
||||
}
|
||||
|
||||
// 执行传送逻辑
|
||||
requester.sendMessage(ChatColor.GREEN + "已同意传送至对方!");
|
||||
receiver.sendMessage(ChatColor.GREEN + "对方已同意传送至你处");
|
||||
|
||||
// 保存请求者原始位置
|
||||
plugin.teleportp.put(requester, requester.getLocation());
|
||||
// 传送请求者到接收者位置
|
||||
requester.teleport(receiver.getLocation());
|
||||
plugin.isteleport.put(requester, 1);
|
||||
|
||||
// 5分钟后清理传送状态
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (plugin.isteleport.getOrDefault(requester, 0) != 1) {
|
||||
requester.sendMessage(ChatColor.RED + "返回命令已过期,无法再返回原处");
|
||||
}
|
||||
// 清理相关映射
|
||||
plugin.teleportp.remove(requester);
|
||||
plugin.Ateleport.remove(receiver);
|
||||
plugin.isteleport.remove(requester);
|
||||
plugin.teleport.remove(requester);
|
||||
}
|
||||
}.runTaskLater(plugin, 5 * 60 * 20);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理拒绝自己传送至对方的请求
|
||||
*/
|
||||
private void handleDetome(Player requester) {
|
||||
Player receiver = plugin.teleport.get(requester);
|
||||
if (receiver == null || !receiver.isOnline()) {
|
||||
requester.sendMessage(ChatColor.RED + "传送请求已过期或不存在");
|
||||
return;
|
||||
}
|
||||
|
||||
// 发送拒绝消息
|
||||
requester.sendMessage(ChatColor.GREEN + "已拒绝传送至对方");
|
||||
receiver.sendMessage(ChatColor.RED + "对方已拒绝传送至你处");
|
||||
|
||||
// 清理相关映射
|
||||
plugin.teleportp.remove(requester);
|
||||
plugin.Ateleport.remove(receiver);
|
||||
plugin.isteleport.remove(requester);
|
||||
plugin.teleport.remove(requester);
|
||||
}
|
||||
}
|
||||
@@ -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; // 格式错误,返回无效值
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,12 +45,14 @@ public class ChooseTagEvent implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!event.getView().getTitle().contains("称号") && !event.getView().getTitle().contains("商城")) {
|
||||
if (!event.getView().getTitle().contains("称号") && !event.getView().getTitle().contains("商城")&& !event.getView().getTitle().contains("背包")&& !event.getView().getTitle().contains("签到")) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.setCancelled(true);
|
||||
if (event.getView().getTitle().contains("称号")) {
|
||||
if(event.getView().getTitle().contains("签到")){
|
||||
|
||||
} else if (event.getView().getTitle().contains("称号")) {
|
||||
ItemStack clickedItem = event.getCurrentItem();
|
||||
if (clickedItem == null || clickedItem.getType() != Material.NAME_TAG) {
|
||||
return;
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
package org.xgqy.survival.event;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
@@ -18,6 +21,12 @@ public class JoinEvent implements Listener {
|
||||
|
||||
@EventHandler
|
||||
private void join(PlayerJoinEvent e) {
|
||||
if(plugin.ppoint.getOrDefault(e.getPlayer(),100) <= 0){
|
||||
e.getPlayer().kickPlayer(ChatColor.RED+"您好! 您由于 信用分小于 0 ,我们决定对你的账号 "+ e.getPlayer().getName() +" 采取\n"+ChatColor.RED+ChatColor.BOLD+"永久封禁\n"+ChatColor.RED+"措施,如果您想解封您的账号,请到QQ群 717903781 申诉\n\n"+ChatColor.RED+"如果您对本次处罚不满,请采取以下措施:\n"+ChatColor.YELLOW+"1.如果您对自己的行为"+ChatColor.BOLD+" 问心无愧 "+ChatColor.YELLOW+"请立即向QQ 2213866559 发送解封申请\n"+ChatColor.YELLOW+"2.如果您属实有违规行为,请"+ChatColor.BOLD+"手写"+ChatColor.YELLOW+"一篇 "+ChatColor.BOLD+"100字以上且AIGC合格"+ChatColor.YELLOW+" 的检讨发送到申诉QQ群,态度诚恳我们将会对你进行解封");
|
||||
}
|
||||
if(plugin.ppoint.getOrDefault(e.getPlayer(),100) == 100){
|
||||
plugin.ppoint.put(e.getPlayer(),100);
|
||||
}
|
||||
PlayerTags playertags = plugin.getPlayerTags();
|
||||
List<String> tags = playertags.getTags(e.getPlayer());
|
||||
if (!tags.isEmpty() && playertags.getCurrentTag(e.getPlayer()) != -1) {
|
||||
@@ -27,6 +36,31 @@ public class JoinEvent implements Listener {
|
||||
if (!tags.isEmpty())
|
||||
e.getPlayer().sendMessage(ChatColor.RED + "你还没有选择任何称号!已自动设置为第一个称号。可输入 /tag 进行切换");
|
||||
}
|
||||
Player player = e.getPlayer();
|
||||
if(player.getScoreboard().getObjective("logged").getScore(player).getScore() == 0){
|
||||
double x = Math.random() * 10000;
|
||||
double z = Math.random() * 10000;
|
||||
int bx = (int) x, bz = (int) z;
|
||||
while(true) {
|
||||
if((x <= 150 && x >= -100) || (z <= 150 && z >= -180)){
|
||||
x = Math.random() * 10000;
|
||||
z = Math.random() * 10000;
|
||||
bx = (int) x;
|
||||
bz = (int) z;
|
||||
}else if(player.getWorld().getHighestBlockAt(bx,bz).getType() != Material.WATER &&
|
||||
player.getWorld().getHighestBlockAt(bx,bz).getType() != Material.LAVA){
|
||||
x = Math.random() * 10000;
|
||||
z = Math.random() * 10000;
|
||||
bx = (int) x;
|
||||
bz = (int) z;
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
}
|
||||
player.teleport(new Location(player.getWorld(),x,player.getWorld().getHighestBlockYAt(bx,bz),z));
|
||||
//player.sendMessage(ChatColor.GREEN+"你好,欢迎!");
|
||||
player.getScoreboard().getObjective("logged").getScore(player).setScore(1);
|
||||
}
|
||||
e.setJoinMessage(e.getPlayer().getPlayerListName() + " 加入了 生存1区");
|
||||
e.getPlayer().sendMessage(ChatColor.YELLOW + "欢迎来到 星阁钱语 生存服!");
|
||||
e.getPlayer().sendMessage(ChatColor.YELLOW + "你可以输入 /help 来查看帮助");
|
||||
|
||||
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() + " 离开了游戏!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,47 @@ commands:
|
||||
shop:
|
||||
description: to open the shop
|
||||
usage: /<command>
|
||||
reg:
|
||||
description: to register an account
|
||||
usage: /<command> <password> <password>
|
||||
pc:
|
||||
description: chat in a party
|
||||
usage: /<command> <message>
|
||||
login:
|
||||
description: to login
|
||||
usage: /<command> <password>
|
||||
party:
|
||||
description: to create a party
|
||||
usage: /<command> <create|quit|join|list|kick|ban|disband|help> <args>
|
||||
inventory:
|
||||
description: check inventory of a player
|
||||
usage: /<command> <player>
|
||||
permission: permission.settag
|
||||
permission-message: 无权执行命令
|
||||
msg:
|
||||
description: private chat with friend
|
||||
usage: /<command> <player> <message>
|
||||
qd:
|
||||
description: qian dao
|
||||
usage: /<command>
|
||||
friend:
|
||||
description: add friend
|
||||
usage: /<command> <add|allow|deny|remove> <player>
|
||||
gift:
|
||||
description: gift
|
||||
usage: /<command>
|
||||
aboutme:
|
||||
description: about you
|
||||
usage: /<command>
|
||||
point:
|
||||
description: change point
|
||||
usage: /<command> <player> <set|add|remove> <number>
|
||||
land:
|
||||
description: change point
|
||||
usage: /<command> <player> <set|add|remove> <number>
|
||||
selfkill:
|
||||
description: self kill
|
||||
usage: /<command>
|
||||
permissions:
|
||||
permission.settag:
|
||||
description: Allows setting player tags
|
||||
|
||||
Reference in New Issue
Block a user