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 {
|
public class PlayerTags {
|
||||||
private final Survival plugin;
|
private final Survival plugin;
|
||||||
private final File tagsFile;
|
private final File tagsFile;
|
||||||
// 原有:玩家-称号列表映射
|
|
||||||
private final Map<UUID, List<String>> playerTags = new HashMap<>();
|
private final Map<UUID, List<String>> playerTags = new HashMap<>();
|
||||||
// 新增:玩家-(称号-到期时间戳)映射(毫秒级,永久用Long.MAX_VALUE)
|
|
||||||
private final Map<UUID, Map<String, Long>> expireTimeMap = new HashMap<>();
|
private final Map<UUID, Map<String, Long>> expireTimeMap = new HashMap<>();
|
||||||
// 原有:玩家-选中称号索引映射
|
|
||||||
public Map<UUID, Integer> playerselectTag = new HashMap<>();
|
public Map<UUID, Integer> playerselectTag = new HashMap<>();
|
||||||
|
|
||||||
// 常量:7天的毫秒数(7*24*60*60*1000)
|
|
||||||
private static final long SEVEN_DAYS_MS = 604800000L;
|
private static final long SEVEN_DAYS_MS = 604800000L;
|
||||||
|
|
||||||
public PlayerTags(Survival plugin) {
|
public PlayerTags(Survival plugin) {
|
||||||
@@ -32,36 +28,28 @@ public class PlayerTags {
|
|||||||
this.tagsFile = new File(plugin.getDataFolder(), "playertags.data");
|
this.tagsFile = new File(plugin.getDataFolder(), "playertags.data");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 核心辅助:清理玩家的所有过期称号(所有方法调用前先执行)
|
|
||||||
*/
|
|
||||||
public void cleanExpiredTags(Player player) {
|
public void cleanExpiredTags(Player player) {
|
||||||
UUID uuid = player.getUniqueId();
|
UUID uuid = player.getUniqueId();
|
||||||
List<String> validTags = new ArrayList<>();
|
List<String> validTags = new ArrayList<>();
|
||||||
List<String> allTags = playerTags.getOrDefault(uuid, new ArrayList<>());
|
List<String> allTags = playerTags.getOrDefault(uuid, new ArrayList<>());
|
||||||
Map<String, Long> tagExpires = expireTimeMap.getOrDefault(uuid, new HashMap<>());
|
Map<String, Long> tagExpires = expireTimeMap.getOrDefault(uuid, new HashMap<>());
|
||||||
|
|
||||||
// 遍历所有称号,保留未过期的
|
|
||||||
for (String tag : allTags) {
|
for (String tag : allTags) {
|
||||||
long expireTime = tagExpires.getOrDefault(tag, Long.MAX_VALUE);
|
long expireTime = tagExpires.getOrDefault(tag, Long.MAX_VALUE);
|
||||||
// 未过期判断:永久(MAX)或当前时间 < 到期时间
|
|
||||||
if (expireTime == Long.MAX_VALUE || System.currentTimeMillis() < expireTime) {
|
if (expireTime == Long.MAX_VALUE || System.currentTimeMillis() < expireTime) {
|
||||||
validTags.add(tag);
|
validTags.add(tag);
|
||||||
} else {
|
} else {
|
||||||
// 过期:移除过期时间记录
|
|
||||||
tagExpires.remove(tag);
|
tagExpires.remove(tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新有效称号列表和过期时间映射
|
|
||||||
if (validTags.isEmpty()) {
|
if (validTags.isEmpty()) {
|
||||||
playerTags.remove(uuid);
|
playerTags.remove(uuid);
|
||||||
expireTimeMap.remove(uuid);
|
expireTimeMap.remove(uuid);
|
||||||
playerselectTag.put(uuid, -1); // 无有效称号,重置选中索引
|
playerselectTag.put(uuid, -1);
|
||||||
} else {
|
} else {
|
||||||
playerTags.put(uuid, validTags);
|
playerTags.put(uuid, validTags);
|
||||||
expireTimeMap.put(uuid, tagExpires);
|
expireTimeMap.put(uuid, tagExpires);
|
||||||
// 检查选中的称号是否已过期:若过期/索引无效,重置索引
|
|
||||||
int currentIndex = playerselectTag.getOrDefault(uuid, -1);
|
int currentIndex = playerselectTag.getOrDefault(uuid, -1);
|
||||||
if (currentIndex == -1 || currentIndex >= validTags.size()) {
|
if (currentIndex == -1 || currentIndex >= validTags.size()) {
|
||||||
playerselectTag.put(uuid, -1);
|
playerselectTag.put(uuid, -1);
|
||||||
@@ -69,11 +57,8 @@ public class PlayerTags {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取当前选中的称号索引(自动过滤过期)
|
|
||||||
*/
|
|
||||||
public int getCurrentTag(Player player) {
|
public int getCurrentTag(Player player) {
|
||||||
cleanExpiredTags(player); // 先清理过期
|
cleanExpiredTags(player);
|
||||||
UUID uuid = player.getUniqueId();
|
UUID uuid = player.getUniqueId();
|
||||||
Integer index = playerselectTag.get(uuid);
|
Integer index = playerselectTag.get(uuid);
|
||||||
if (index == null) {
|
if (index == null) {
|
||||||
@@ -82,34 +67,29 @@ public class PlayerTags {
|
|||||||
|
|
||||||
List<String> validTags = playerTags.getOrDefault(uuid, new ArrayList<>());
|
List<String> validTags = playerTags.getOrDefault(uuid, new ArrayList<>());
|
||||||
if (validTags.isEmpty() || index < 0 || index >= validTags.size()) {
|
if (validTags.isEmpty() || index < 0 || index >= validTags.size()) {
|
||||||
playerselectTag.put(uuid, -1); // 索引无效,重置
|
playerselectTag.put(uuid, -1);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置玩家选择的标签索引(自动过滤过期)
|
|
||||||
*/
|
|
||||||
public boolean setSelectedTag(Player player, int index) {
|
public boolean setSelectedTag(Player player, int index) {
|
||||||
cleanExpiredTags(player); // 先清理过期
|
cleanExpiredTags(player);
|
||||||
UUID uuid = player.getUniqueId();
|
UUID uuid = player.getUniqueId();
|
||||||
List<String> validTags = playerTags.getOrDefault(uuid, new ArrayList<>());
|
List<String> validTags = playerTags.getOrDefault(uuid, new ArrayList<>());
|
||||||
|
|
||||||
// 无有效称号或索引无效,返回失败
|
|
||||||
if (validTags.isEmpty() || index < 0 || index >= validTags.size()) {
|
if (validTags.isEmpty() || index < 0 || index >= validTags.size()) {
|
||||||
playerselectTag.put(uuid, -1);
|
playerselectTag.put(uuid, -1);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置选中索引,返回成功
|
|
||||||
playerselectTag.put(uuid, index);
|
playerselectTag.put(uuid, index);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加载称号数据:核心逻辑——识别旧数据并转为7天过期
|
* 修复核心:正确读取新版本数据中的expireTimeMap,避免误判为旧数据
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void loadTags() {
|
public void loadTags() {
|
||||||
@@ -118,17 +98,27 @@ public class PlayerTags {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(tagsFile))) {
|
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(tagsFile))) {
|
||||||
// 读取第一个对象:判断是旧版本(List<String>)还是新版本(含过期时间)
|
// 读取三个核心对象(新版本数据结构)
|
||||||
Object firstObj = ois.readObject();
|
Object firstObj = ois.readObject();
|
||||||
Object secondObj = ois.readObject();
|
Object secondObj = ois.readObject();
|
||||||
Object thirdObj = null; // 新版本的第三个对象:expireTimeMap
|
Object thirdObj = ois.readObject(); // 关键修复:读取第三个对象(expireTimeMap)
|
||||||
|
|
||||||
// 1. 识别旧版本数据(仅2个对象:playerTags + playerselectTag)
|
// 1. 优先判断新版本数据(3个对象完整)
|
||||||
if (firstObj instanceof Map<?, ?> && secondObj instanceof Map<?, ?> && ois.available() == 0) {
|
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, List<String>> oldPlayerTags = (Map<UUID, List<String>>) firstObj;
|
||||||
Map<UUID, Integer> oldSelectTag = (Map<UUID, Integer>) secondObj;
|
Map<UUID, Integer> oldSelectTag = (Map<UUID, Integer>) secondObj;
|
||||||
|
|
||||||
// 旧数据转换:为每个称号设置“当前时间+7天”的到期时间
|
// 旧数据转换为7天过期
|
||||||
for (Map.Entry<UUID, List<String>> entry : oldPlayerTags.entrySet()) {
|
for (Map.Entry<UUID, List<String>> entry : oldPlayerTags.entrySet()) {
|
||||||
UUID uuid = entry.getKey();
|
UUID uuid = entry.getKey();
|
||||||
List<String> oldTags = entry.getValue();
|
List<String> oldTags = entry.getValue();
|
||||||
@@ -136,34 +126,27 @@ public class PlayerTags {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 为当前玩家创建“称号-到期时间”映射
|
|
||||||
Map<String, Long> tagExpires = new HashMap<>();
|
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) {
|
for (String tag : oldTags) {
|
||||||
tagExpires.put(tag, sevenDaysLater); // 每个旧称号都设为7天过期
|
tagExpires.put(tag, sevenDaysLater);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 存入新结构
|
playerTags.put(uuid, new ArrayList<>(oldTags));
|
||||||
playerTags.put(uuid, new ArrayList<>(oldTags)); // 复制旧称号列表
|
expireTimeMap.put(uuid, tagExpires);
|
||||||
expireTimeMap.put(uuid, tagExpires); // 存入7天过期时间
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 恢复选中索引
|
|
||||||
playerselectTag.putAll(oldSelectTag);
|
playerselectTag.putAll(oldSelectTag);
|
||||||
plugin.getLogger().info("旧版本称号数据加载完成,已自动转为7日后到期!");
|
plugin.getLogger().info("旧版本称号数据加载完成,已自动转为7日后到期!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
// 3. 数据格式错误
|
||||||
// 2. 识别新版本数据(3个对象:playerTags + playerselectTag + expireTimeMap)
|
plugin.getLogger().warning("称号数据格式错误,无法识别!");
|
||||||
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("新版本称号数据加载完成!");
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (IOException | ClassNotFoundException e) {
|
} catch (IOException | ClassNotFoundException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
// 回退逻辑:仅加载称号列表,所有称号设为7天过期
|
// 回退逻辑:仅加载称号列表,设为7天过期
|
||||||
try (ObjectInputStream fallback = new ObjectInputStream(new FileInputStream(tagsFile))) {
|
try (ObjectInputStream fallback = new ObjectInputStream(new FileInputStream(tagsFile))) {
|
||||||
Object obj = fallback.readObject();
|
Object obj = fallback.readObject();
|
||||||
if (obj instanceof Map<?, ?> oldPlayerTags) {
|
if (obj instanceof Map<?, ?> oldPlayerTags) {
|
||||||
@@ -176,7 +159,7 @@ public class PlayerTags {
|
|||||||
for (Object tagObj : (List<?>) entry.getValue()) {
|
for (Object tagObj : (List<?>) entry.getValue()) {
|
||||||
if (tagObj instanceof String tag) {
|
if (tagObj instanceof String tag) {
|
||||||
tags.add(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() {
|
public void saveTags() {
|
||||||
if (!tagsFile.getParentFile().exists()) {
|
if (!tagsFile.getParentFile().exists()) {
|
||||||
tagsFile.getParentFile().mkdirs();
|
tagsFile.getParentFile().mkdirs();
|
||||||
}
|
}
|
||||||
|
|
||||||
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(tagsFile))) {
|
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(tagsFile))) {
|
||||||
// 按顺序写入3个核心结构(新版本)
|
// 按顺序写入三个核心结构(与加载时对应)
|
||||||
oos.writeObject(playerTags);
|
oos.writeObject(playerTags);
|
||||||
oos.writeObject(playerselectTag);
|
oos.writeObject(playerselectTag);
|
||||||
oos.writeObject(expireTimeMap);
|
oos.writeObject(expireTimeMap);
|
||||||
@@ -211,71 +191,50 @@ public class PlayerTags {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取玩家的有效称号列表(自动过滤过期)
|
|
||||||
*/
|
|
||||||
public List<String> getTags(Player player) {
|
public List<String> getTags(Player player) {
|
||||||
cleanExpiredTags(player);
|
cleanExpiredTags(player);
|
||||||
return playerTags.getOrDefault(player.getUniqueId(), new ArrayList<>());
|
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) {
|
public void addTag(Player player, String tag, boolean isPermanent) {
|
||||||
cleanExpiredTags(player); // 先清理过期
|
cleanExpiredTags(player);
|
||||||
UUID uuid = player.getUniqueId();
|
UUID uuid = player.getUniqueId();
|
||||||
List<String> tags = playerTags.computeIfAbsent(uuid, k -> new ArrayList<>());
|
List<String> tags = playerTags.computeIfAbsent(uuid, k -> new ArrayList<>());
|
||||||
Map<String, Long> tagExpires = expireTimeMap.computeIfAbsent(uuid, k -> new HashMap<>());
|
Map<String, Long> tagExpires = expireTimeMap.computeIfAbsent(uuid, k -> new HashMap<>());
|
||||||
|
|
||||||
// 避免重复添加相同称号
|
|
||||||
if (!tags.contains(tag)) {
|
if (!tags.contains(tag)) {
|
||||||
tags.add(tag);
|
tags.add(tag);
|
||||||
// 设置过期时间:永久=Long.MAX_VALUE,临时=当前时间+7天
|
|
||||||
long expireTime = isPermanent ? Long.MAX_VALUE : System.currentTimeMillis() + SEVEN_DAYS_MS;
|
long expireTime = isPermanent ? Long.MAX_VALUE : System.currentTimeMillis() + SEVEN_DAYS_MS;
|
||||||
tagExpires.put(tag, expireTime);
|
tagExpires.put(tag, expireTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 重载原有addTag方法:默认添加永久称号(保持向下兼容)
|
|
||||||
*/
|
|
||||||
public void addTag(Player player, String tag, int days) {
|
public void addTag(Player player, String tag, int days) {
|
||||||
cleanExpiredTags(player); // 先清理过期
|
cleanExpiredTags(player);
|
||||||
UUID uuid = player.getUniqueId();
|
UUID uuid = player.getUniqueId();
|
||||||
List<String> tags = playerTags.computeIfAbsent(uuid, k -> new ArrayList<>());
|
List<String> tags = playerTags.computeIfAbsent(uuid, k -> new ArrayList<>());
|
||||||
Map<String, Long> tagExpires = expireTimeMap.computeIfAbsent(uuid, k -> new HashMap<>());
|
Map<String, Long> tagExpires = expireTimeMap.computeIfAbsent(uuid, k -> new HashMap<>());
|
||||||
|
|
||||||
// 避免重复添加相同称号
|
|
||||||
if (!tags.contains(tag)) {
|
if (!tags.contains(tag)) {
|
||||||
tags.add(tag);
|
tags.add(tag);
|
||||||
long expireTime;
|
long expireTime;
|
||||||
if (days == 999) {
|
if (days == 999) {
|
||||||
expireTime = Long.MAX_VALUE; // 999天视为永久
|
expireTime = Long.MAX_VALUE;
|
||||||
} else if (days <= 0) {
|
} else if (days <= 0) {
|
||||||
expireTime = System.currentTimeMillis() - 1; // 天数≤0:立即过期(触发清理)
|
expireTime = System.currentTimeMillis() - 1;
|
||||||
} else {
|
} else {
|
||||||
// 计算有效期:当前时间 + 天数×24×60×60×1000(转换为毫秒)
|
|
||||||
expireTime = System.currentTimeMillis() + (long) days * 24 * 60 * 60 * 1000;
|
expireTime = System.currentTimeMillis() + (long) days * 24 * 60 * 60 * 1000;
|
||||||
}
|
}
|
||||||
tagExpires.put(tag, expireTime);
|
tagExpires.put(tag, expireTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 移除称号:同时删除过期时间记录
|
|
||||||
*/
|
|
||||||
public void removeTag(Player player, String tag) {
|
public void removeTag(Player player, String tag) {
|
||||||
cleanExpiredTags(player);
|
cleanExpiredTags(player);
|
||||||
UUID uuid = player.getUniqueId();
|
UUID uuid = player.getUniqueId();
|
||||||
List<String> tags = playerTags.get(uuid);
|
List<String> tags = playerTags.get(uuid);
|
||||||
Map<String, Long> tagExpires = expireTimeMap.get(uuid);
|
Map<String, Long> tagExpires = expireTimeMap.get(uuid);
|
||||||
|
|
||||||
// 移除称号列表和过期时间映射中的对应条目
|
|
||||||
if (tags != null) {
|
if (tags != null) {
|
||||||
tags.remove(tag);
|
tags.remove(tag);
|
||||||
if (tags.isEmpty()) {
|
if (tags.isEmpty()) {
|
||||||
@@ -293,7 +252,6 @@ public class PlayerTags {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 若移除的是当前选中称号,重置索引
|
|
||||||
int currentIndex = playerselectTag.getOrDefault(uuid, -1);
|
int currentIndex = playerselectTag.getOrDefault(uuid, -1);
|
||||||
List<String> validTags = playerTags.getOrDefault(uuid, new ArrayList<>());
|
List<String> validTags = playerTags.getOrDefault(uuid, new ArrayList<>());
|
||||||
if (currentIndex != -1 && (currentIndex >= validTags.size() || !validTags.contains(tag))) {
|
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) {
|
public long getTagExpireTime(Player player, String tag) {
|
||||||
cleanExpiredTags(player);
|
cleanExpiredTags(player);
|
||||||
UUID uuid = player.getUniqueId();
|
UUID uuid = player.getUniqueId();
|
||||||
Map<String, Long> tagExpires = expireTimeMap.getOrDefault(uuid, new HashMap<>());
|
Map<String, Long> tagExpires = expireTimeMap.getOrDefault(uuid, new HashMap<>());
|
||||||
List<String> validTags = playerTags.getOrDefault(uuid, new ArrayList<>());
|
List<String> validTags = playerTags.getOrDefault(uuid, new ArrayList<>());
|
||||||
|
|
||||||
// 称号不存在或已过期,返回-2
|
|
||||||
if (!validTags.contains(tag)) {
|
if (!validTags.contains(tag)) {
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
long expireTime = tagExpires.getOrDefault(tag, Long.MAX_VALUE);
|
long expireTime = tagExpires.getOrDefault(tag, Long.MAX_VALUE);
|
||||||
// 永久称号返回-1,否则返回时间戳
|
|
||||||
return expireTime == Long.MAX_VALUE ? -1 : expireTime;
|
return expireTime == Long.MAX_VALUE ? -1 : expireTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 新增:获取称号的剩余时间(返回字符串,如“2天3小时”)
|
|
||||||
*/
|
|
||||||
public String getTagRemainingTime(Player player, String tag) {
|
public String getTagRemainingTime(Player player, String tag) {
|
||||||
long expireTime = getTagExpireTime(player, tag);
|
long expireTime = getTagExpireTime(player, tag);
|
||||||
if (expireTime == -1) {
|
if (expireTime == -1) {
|
||||||
@@ -334,18 +282,15 @@ public class PlayerTags {
|
|||||||
return "已过期/不存在";
|
return "已过期/不存在";
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算剩余毫秒数(可能为负,需处理)
|
|
||||||
long remainingMs = expireTime - System.currentTimeMillis();
|
long remainingMs = expireTime - System.currentTimeMillis();
|
||||||
if (remainingMs <= 0) {
|
if (remainingMs <= 0) {
|
||||||
return "已过期";
|
return "已过期";
|
||||||
}
|
}
|
||||||
|
|
||||||
// 转换为天、时、分
|
|
||||||
long days = remainingMs / (24 * 60 * 60 * 1000);
|
long days = remainingMs / (24 * 60 * 60 * 1000);
|
||||||
long hours = (remainingMs % (24 * 60 * 60 * 1000)) / (60 * 60 * 1000);
|
long hours = (remainingMs % (24 * 60 * 60 * 1000)) / (60 * 60 * 1000);
|
||||||
long minutes = (remainingMs % (60 * 60 * 1000)) / (60 * 1000);
|
long minutes = (remainingMs % (60 * 60 * 1000)) / (60 * 1000);
|
||||||
|
|
||||||
// 拼接剩余时间字符串
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
if (days > 0) sb.append(days).append("天");
|
if (days > 0) sb.append(days).append("天");
|
||||||
if (hours > 0) sb.append(hours).append("小时");
|
if (hours > 0) sb.append(hours).append("小时");
|
||||||
|
|||||||
@@ -1,70 +1,415 @@
|
|||||||
package org.xgqy.survival;
|
package org.xgqy.survival;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.configuration.file.FileConfiguration;
|
||||||
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
import org.bukkit.scheduler.BukkitRunnable;
|
||||||
import org.xgqy.survival.command.DqshopCommandExecutor;
|
import org.xgqy.survival.command.DqshopCommandExecutor;
|
||||||
|
import org.xgqy.survival.command.FriendCommandExecutor;
|
||||||
import org.xgqy.survival.command.HandleCommandExecutor;
|
import org.xgqy.survival.command.HandleCommandExecutor;
|
||||||
import org.xgqy.survival.command.HelpCommandExecutor;
|
import org.xgqy.survival.command.HelpCommandExecutor;
|
||||||
import org.xgqy.survival.command.HubCommandExecutor;
|
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.PvpCommandExecutor;
|
||||||
|
import org.xgqy.survival.command.RegCommandExecutor;
|
||||||
import org.xgqy.survival.command.ReportCommandExecutor;
|
import org.xgqy.survival.command.ReportCommandExecutor;
|
||||||
import org.xgqy.survival.command.SetTagCommandExecutor;
|
import org.xgqy.survival.command.SetTagCommandExecutor;
|
||||||
import org.xgqy.survival.command.TagCommandExecutor;
|
import org.xgqy.survival.command.TagCommandExecutor;
|
||||||
import org.xgqy.survival.command.TeleportCommandExecutor;
|
import org.xgqy.survival.command.TeleportCommandExecutor;
|
||||||
import org.xgqy.survival.command.TpAccCommandExecutor;
|
import org.xgqy.survival.command.TpAccCommandExecutor;
|
||||||
import org.xgqy.survival.command.TpFinCommandExecutor;
|
import org.xgqy.survival.command.TpFinCommandExecutor;
|
||||||
|
import org.xgqy.survival.command.msgCommandExecutor;
|
||||||
import org.xgqy.survival.event.ChatEvent;
|
import org.xgqy.survival.event.ChatEvent;
|
||||||
import org.xgqy.survival.event.ChooseTagEvent;
|
import org.xgqy.survival.event.ChooseTagEvent;
|
||||||
import org.xgqy.survival.event.JoinEvent;
|
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.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
public final class Survival extends JavaPlugin {
|
public final class Survival extends JavaPlugin {
|
||||||
private PlayerTags playerTags;
|
private PlayerTags playerTags;
|
||||||
|
private AccountManager accountManager;
|
||||||
public Map<Player, Boolean> krt = new HashMap<>();
|
public Map<Player, Boolean> krt = new HashMap<>();
|
||||||
public Map<Player, Player> banlist = new HashMap<>();
|
public Map<Player, Player> banlist = new HashMap<>();
|
||||||
public Map<Player, String> banreason = new HashMap<>();
|
public Map<Player, String> banreason = new HashMap<>();
|
||||||
public Map<Player, HashSet<Player>> reportlist = new HashMap<>();
|
public Map<Player, HashSet<Player>> reportlist = new HashMap<>();
|
||||||
|
// Teleport
|
||||||
public Map<Player, Player> teleport = new HashMap<>();
|
public Map<Player, Player> teleport = new HashMap<>();
|
||||||
public Map<Player, Player> Ateleport = new HashMap<>();
|
public Map<Player, Player> Ateleport = new HashMap<>();
|
||||||
public Map<Player, Location> teleportp = new HashMap<>();
|
public Map<Player, Location> teleportp = new HashMap<>();
|
||||||
public Map<Player, Integer> isteleport = new HashMap<>();
|
public Map<Player, Integer> isteleport = new HashMap<>();
|
||||||
public Map<Player, Long> lasttp = 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
|
@Override
|
||||||
public void onEnable() {
|
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 = new PlayerTags(this);
|
||||||
playerTags.loadTags();
|
playerTags.loadTags();
|
||||||
|
|
||||||
|
// 注册事件监听器
|
||||||
Bukkit.getPluginManager().registerEvents(new JoinEvent(this), this);
|
Bukkit.getPluginManager().registerEvents(new JoinEvent(this), this);
|
||||||
Bukkit.getPluginManager().registerEvents(new ChatEvent(this), this);
|
Bukkit.getPluginManager().registerEvents(new ChatEvent(this), this);
|
||||||
Bukkit.getPluginManager().registerEvents(new ChooseTagEvent(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("report").setExecutor(new ReportCommandExecutor(this));
|
||||||
getCommand("handle").setExecutor(new HandleCommandExecutor(this));
|
getCommand("handle").setExecutor(new HandleCommandExecutor(this));
|
||||||
getCommand("pvp").setExecutor(new PvpCommandExecutor(this));
|
getCommand("pvp").setExecutor(new PvpCommandExecutor(this));
|
||||||
getCommand("settag").setExecutor(new SetTagCommandExecutor(this));
|
getCommand("settag").setExecutor(new SetTagCommandExecutor(this));
|
||||||
getCommand("help").setExecutor(new HelpCommandExecutor(this));
|
getCommand("help").setExecutor(new HelpCommandExecutor(this));
|
||||||
//getCommand("fly").setExecutor(new FlyCommandExecutor(this));
|
|
||||||
getCommand("tag").setExecutor(new TagCommandExecutor(this));
|
getCommand("tag").setExecutor(new TagCommandExecutor(this));
|
||||||
getCommand("hub").setExecutor(new HubCommandExecutor(this));
|
getCommand("hub").setExecutor(new HubCommandExecutor(this));
|
||||||
getCommand("teleport").setExecutor(new TeleportCommandExecutor(this));
|
getCommand("teleport").setExecutor(new TeleportCommandExecutor(this));
|
||||||
getCommand("tpacc").setExecutor(new TpAccCommandExecutor(this));
|
getCommand("tpacc").setExecutor(new TpAccCommandExecutor(this));
|
||||||
getCommand("tpfin").setExecutor(new TpFinCommandExecutor(this));
|
getCommand("tpfin").setExecutor(new TpFinCommandExecutor(this));
|
||||||
getCommand("shop").setExecutor(new DqshopCommandExecutor(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
|
@Override
|
||||||
public void onDisable() {
|
public void onDisable() {
|
||||||
|
// 保存玩家标签数据
|
||||||
playerTags.saveTags();
|
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() {
|
public PlayerTags getPlayerTags() {
|
||||||
return playerTags;
|
return playerTags;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// 获取账号管理器
|
||||||
|
public AccountManager getAccountManager() {
|
||||||
|
return accountManager;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,21 +1,12 @@
|
|||||||
package org.xgqy.survival.command;
|
package org.xgqy.survival.command;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.ChatColor;
|
import org.bukkit.ChatColor;
|
||||||
import org.bukkit.Material;
|
|
||||||
import org.bukkit.command.Command;
|
import org.bukkit.command.Command;
|
||||||
import org.bukkit.command.CommandExecutor;
|
import org.bukkit.command.CommandExecutor;
|
||||||
import org.bukkit.command.CommandSender;
|
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.jetbrains.annotations.NotNull;
|
||||||
import org.xgqy.survival.Survival;
|
import org.xgqy.survival.Survival;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class DqshopCommandExecutor implements CommandExecutor {
|
public class DqshopCommandExecutor implements CommandExecutor {
|
||||||
|
|
||||||
private Survival plugin;
|
private Survival plugin;
|
||||||
@@ -26,8 +17,10 @@ public class DqshopCommandExecutor implements CommandExecutor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||||
if (sender instanceof Player) {
|
//if (sender instanceof Player) {
|
||||||
Inventory shopGui = Bukkit.createInventory(null, 54, ChatColor.GOLD + "商城");
|
sender.sendMessage(ChatColor.RED+"本功能已废弃!请前往官网 https://starpavilion.xyz");
|
||||||
|
return true;
|
||||||
|
/*Inventory shopGui = Bukkit.createInventory(null, 54, ChatColor.GOLD + "商城");
|
||||||
ItemStack head = new ItemStack(Material.PLAYER_HEAD);
|
ItemStack head = new ItemStack(Material.PLAYER_HEAD);
|
||||||
ItemMeta headmeta = head.getItemMeta();
|
ItemMeta headmeta = head.getItemMeta();
|
||||||
List<String> meta1 = new ArrayList<>();
|
List<String> meta1 = new ArrayList<>();
|
||||||
@@ -181,6 +174,6 @@ public class DqshopCommandExecutor implements CommandExecutor {
|
|||||||
sender.sendMessage(ChatColor.RED + "仅玩家能使用该命令!");
|
sender.sendMessage(ChatColor.RED + "仅玩家能使用该命令!");
|
||||||
return true;
|
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 + " 感谢您为维护游戏平衡做贡献!");
|
args[0] + ChatColor.GREEN + " 已经被封禁\n" + ChatColor.LIGHT_PURPLE + "举报系统" + ChatColor.WHITE + " | \n" + ChatColor.GREEN + " 感谢您为维护游戏平衡做贡献!");
|
||||||
}
|
}
|
||||||
plugin.reportlist.put(Bukkit.getPlayer(args[0]), null);
|
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封禁原因:" + "管理员处理");
|
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 {
|
} else {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -21,9 +21,14 @@ public class HelpCommandExecutor implements CommandExecutor {
|
|||||||
sender.sendMessage(ChatColor.GREEN + "/pvp - 开启/关闭 玩家伤害");
|
sender.sendMessage(ChatColor.GREEN + "/pvp - 开启/关闭 玩家伤害");
|
||||||
sender.sendMessage(ChatColor.GREEN + "/tag - 选择称号");
|
sender.sendMessage(ChatColor.GREEN + "/tag - 选择称号");
|
||||||
sender.sendMessage(ChatColor.GREEN + "/teleport <玩家名> - 玩家传送");
|
sender.sendMessage(ChatColor.GREEN + "/teleport <玩家名> - 玩家传送");
|
||||||
|
sender.sendMessage(ChatColor.GREEN + "/teleport <玩家名> me - 玩家传送");
|
||||||
sender.sendMessage(ChatColor.GREEN + "/report - 举报作弊玩家");
|
sender.sendMessage(ChatColor.GREEN + "/report - 举报作弊玩家");
|
||||||
sender.sendMessage(ChatColor.GREEN + "/tpacc <accept|deny> - 同意/拒绝玩家传送");
|
sender.sendMessage(ChatColor.GREEN+"/login <密码> -登录");
|
||||||
sender.sendMessage(ChatColor.GREEN + "/tpfin - 结束传送");
|
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 + "-----------------------------");
|
sender.sendMessage(ChatColor.YELLOW + "-----------------------------");
|
||||||
return true;
|
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) {
|
if (plugin.krt.getOrDefault(sender, false) == true) {
|
||||||
plugin.krt.put((Player) sender, false);
|
plugin.krt.put((Player) sender, false);
|
||||||
sender.sendMessage(ChatColor.GREEN + "已切换PVP模式到: 关");
|
sender.sendMessage(ChatColor.GREEN + "已切换PVP模式到: 关");
|
||||||
|
//
|
||||||
} else {
|
} else {
|
||||||
sender.sendMessage(ChatColor.GREEN + "已切换PVP模式到: 开");
|
sender.sendMessage(ChatColor.GREEN + "已切换PVP模式到: 开");
|
||||||
plugin.krt.put((Player) sender, true);
|
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) + " 秒 ");
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
if (args.length != 1) {
|
if (args.length != 1 && args.length != 2) {
|
||||||
sender.sendMessage(ChatColor.RED + "参数错误!用法: /teleport <玩家名>");
|
sender.sendMessage(ChatColor.RED + "参数错误!用法: /teleport <玩家名>");
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
@@ -37,6 +37,41 @@ public class TeleportCommandExecutor implements CommandExecutor {
|
|||||||
sender.sendMessage(ChatColor.RED + "该玩家不在线!");
|
sender.sendMessage(ChatColor.RED + "该玩家不在线!");
|
||||||
return true;
|
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]);
|
playerto = Bukkit.getPlayer(args[0]);
|
||||||
if (plugin.teleport.getOrDefault((Player) sender, null) != null) {
|
if (plugin.teleport.getOrDefault((Player) sender, null) != null) {
|
||||||
playerto.sendMessage(ChatColor.RED + "您已经向 " + plugin.teleport.get((Player) sender).getName() + ChatColor.RED + " 发送了一份请求");
|
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 {
|
public class TpAccCommandExecutor implements CommandExecutor {
|
||||||
|
|
||||||
private Survival plugin;
|
private final Survival plugin;
|
||||||
|
|
||||||
public TpAccCommandExecutor(Survival plugin) {
|
public TpAccCommandExecutor(Survival plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
@@ -19,48 +19,159 @@ public class TpAccCommandExecutor implements CommandExecutor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
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 (!(sender instanceof Player player)) {
|
||||||
if (args[0].contains("accept")) {
|
sender.sendMessage(ChatColor.RED + "无法对非玩家使用此命令");
|
||||||
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 + "无法对非玩家类使用");
|
|
||||||
return true;
|
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;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
if (event.getView().getTitle().contains("称号")) {
|
if(event.getView().getTitle().contains("签到")){
|
||||||
|
|
||||||
|
} else if (event.getView().getTitle().contains("称号")) {
|
||||||
ItemStack clickedItem = event.getCurrentItem();
|
ItemStack clickedItem = event.getCurrentItem();
|
||||||
if (clickedItem == null || clickedItem.getType() != Material.NAME_TAG) {
|
if (clickedItem == null || clickedItem.getType() != Material.NAME_TAG) {
|
||||||
return;
|
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;
|
package org.xgqy.survival.event;
|
||||||
|
|
||||||
import org.bukkit.ChatColor;
|
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.EventHandler;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.player.PlayerJoinEvent;
|
import org.bukkit.event.player.PlayerJoinEvent;
|
||||||
@@ -18,6 +21,12 @@ public class JoinEvent implements Listener {
|
|||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
private void join(PlayerJoinEvent e) {
|
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();
|
PlayerTags playertags = plugin.getPlayerTags();
|
||||||
List<String> tags = playertags.getTags(e.getPlayer());
|
List<String> tags = playertags.getTags(e.getPlayer());
|
||||||
if (!tags.isEmpty() && playertags.getCurrentTag(e.getPlayer()) != -1) {
|
if (!tags.isEmpty() && playertags.getCurrentTag(e.getPlayer()) != -1) {
|
||||||
@@ -27,6 +36,31 @@ public class JoinEvent implements Listener {
|
|||||||
if (!tags.isEmpty())
|
if (!tags.isEmpty())
|
||||||
e.getPlayer().sendMessage(ChatColor.RED + "你还没有选择任何称号!已自动设置为第一个称号。可输入 /tag 进行切换");
|
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.setJoinMessage(e.getPlayer().getPlayerListName() + " 加入了 生存1区");
|
||||||
e.getPlayer().sendMessage(ChatColor.YELLOW + "欢迎来到 星阁钱语 生存服!");
|
e.getPlayer().sendMessage(ChatColor.YELLOW + "欢迎来到 星阁钱语 生存服!");
|
||||||
e.getPlayer().sendMessage(ChatColor.YELLOW + "你可以输入 /help 来查看帮助");
|
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:
|
shop:
|
||||||
description: to open the shop
|
description: to open the shop
|
||||||
usage: /<command>
|
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:
|
permissions:
|
||||||
permission.settag:
|
permission.settag:
|
||||||
description: Allows setting player tags
|
description: Allows setting player tags
|
||||||
|
|||||||
Reference in New Issue
Block a user