Compare commits

...

10 Commits

Author SHA1 Message Date
a375365d29 update all 2025-11-02 19:29:09 +08:00
3015ba5a7c update 2025-11-02 19:21:58 +08:00
4d09482fb1 update 8 2025-10-25 18:27:24 +08:00
57c24b1dce update 8 2025-10-18 13:25:23 +08:00
529802f6c5 update 2025/10/15 upd
update:0
2025-10-16 20:14:25 +08:00
f8b8065c37 update 2025/10/15 upd
update:0
2025-10-15 20:23:22 +08:00
c5a3b66e5f update 2025/10/15 upd
update:0
2025-10-15 19:49:28 +08:00
da974f2238 update 2025/10/12 upd2
update:
- teleport fix
- teleport new va (teleport <player> me)
2025-10-12 19:12:22 +08:00
42ed9e8bc9 update 2025/10/12 upd1
update:
- random player spawn area
2025-10-12 18:49:57 +08:00
4712ed8723 update 2025/10/12 upd1
update:
- random player spawn area
2025-10-12 18:48:57 +08:00
30 changed files with 3526 additions and 155 deletions

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

View File

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

View File

@@ -0,0 +1,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;
}
}

View File

@@ -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("小时");

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 + " 发送了一份请求");

View File

@@ -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 + "传送成功!"); return true;
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); 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() { new BukkitRunnable() {
@Override @Override
public void run() { public void run() {
if (plugin.isteleport.get(sender) != 1) if (plugin.isteleport.getOrDefault(requester, 0) != 1) {
plugin.Ateleport.get(sender).sendMessage(ChatColor.RED + "返回命令已过期,你将无法返回原处"); requester.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.teleportp.remove(requester);
plugin.Ateleport.remove(sender); plugin.teleport.remove(requester);
plugin.isteleport.remove(requester);
plugin.Ateleport.remove(receiver);
}
}.runTaskLater(plugin, 5 * 60 * 20); // 5分钟20tick=1秒
} }
}.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; private void handleDeny(Player receiver) {
Player requester = plugin.Ateleport.get(receiver);
if (requester == null || !requester.isOnline()) {
receiver.sendMessage(ChatColor.RED + "传送请求已过期或不存在");
return;
} }
} else {
sender.sendMessage(ChatColor.RED + "现在没有传送申请或申请已过期"); // 发送拒绝消息
return true; receiver.sendMessage(ChatColor.GREEN + "已拒绝传送请求");
requester.sendMessage(ChatColor.RED + "对方已拒绝你的传送请求");
// 清理相关映射
plugin.teleportp.remove(requester);
plugin.teleport.remove(requester);
plugin.isteleport.remove(requester);
plugin.Ateleport.remove(receiver);
} }
} else {
sender.sendMessage(ChatColor.RED + "无法对非玩家类使用"); /**
return true; * 处理同意自己传送至对方的请求
*/
private void handleTome(Player requester) {
// 获取请求接收者teleport: 请求者 -> 接收者)
Player receiver = plugin.teleport.get(requester);
if (receiver == null || !receiver.isOnline()) {
requester.sendMessage(ChatColor.RED + "传送请求已过期或不存在");
return;
} }
//return true;
// 执行传送逻辑
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);
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 来查看帮助");

View File

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

View File

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

View File

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

View File

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