init
This commit is contained in:
116
.gitignore
vendored
Normal file
116
.gitignore
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
# User-specific stuff
|
||||
.vscode
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
.idea
|
||||
|
||||
# Compiled class file
|
||||
*.class
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
|
||||
# BlueJ files
|
||||
*.ctxt
|
||||
|
||||
# Package Files #
|
||||
*.war
|
||||
*.nar
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
*~
|
||||
|
||||
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||
.fuse_hidden*
|
||||
|
||||
# KDE directory preferences
|
||||
.directory
|
||||
|
||||
# Linux trash folder which might appear on any partition or disk
|
||||
.Trash-*
|
||||
|
||||
# .nfs files are created when an open file is removed but is still being accessed
|
||||
.nfs*
|
||||
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
# Windows thumbnail cache files
|
||||
Thumbs.db
|
||||
Thumbs.db:encryptable
|
||||
ehthumbs.db
|
||||
ehthumbs_vista.db
|
||||
|
||||
# Dump file
|
||||
*.stackdump
|
||||
|
||||
# Folder config file
|
||||
[Dd]esktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msix
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
target/
|
||||
|
||||
pom.xml.tag
|
||||
pom.xml.releaseBackup
|
||||
pom.xml.versionsBackup
|
||||
pom.xml.next
|
||||
|
||||
release.properties
|
||||
dependency-reduced-pom.xml
|
||||
buildNumber.properties
|
||||
.mvn/timing.properties
|
||||
.mvn/wrapper/maven-wrapper.jar
|
||||
.flattened-pom.xml
|
||||
|
||||
# Common working directory
|
||||
run*/
|
||||
|
||||
# Gradle
|
||||
.gradle/
|
||||
build/
|
||||
74
pom.xml
Normal file
74
pom.xml
Normal file
@@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.xgqy</groupId>
|
||||
<artifactId>survival</artifactId>
|
||||
<version>1.0</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>survival</name>
|
||||
|
||||
<properties>
|
||||
<java.version>21</java.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<defaultGoal>clean package</defaultGoal>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<configuration>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.5.3</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spigotmc-repo</id>
|
||||
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.21.1-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
<artifactId>annotations</artifactId>
|
||||
<version>26.0.2</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
355
src/main/java/org/xgqy/survival/PlayerTags.java
Normal file
355
src/main/java/org/xgqy/survival/PlayerTags.java
Normal file
@@ -0,0 +1,355 @@
|
||||
package org.xgqy.survival;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
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.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class PlayerTags {
|
||||
private final Survival plugin;
|
||||
private final File tagsFile;
|
||||
// 原有:玩家-称号列表映射
|
||||
private final Map<UUID, List<String>> playerTags = new HashMap<>();
|
||||
// 新增:玩家-(称号-到期时间戳)映射(毫秒级,永久用Long.MAX_VALUE)
|
||||
private final Map<UUID, Map<String, Long>> expireTimeMap = new HashMap<>();
|
||||
// 原有:玩家-选中称号索引映射
|
||||
public Map<UUID, Integer> playerselectTag = new HashMap<>();
|
||||
|
||||
// 常量:7天的毫秒数(7*24*60*60*1000)
|
||||
private static final long SEVEN_DAYS_MS = 604800000L;
|
||||
|
||||
public PlayerTags(Survival plugin) {
|
||||
this.plugin = plugin;
|
||||
this.tagsFile = new File(plugin.getDataFolder(), "playertags.data");
|
||||
}
|
||||
|
||||
/**
|
||||
* 核心辅助:清理玩家的所有过期称号(所有方法调用前先执行)
|
||||
*/
|
||||
public void cleanExpiredTags(Player player) {
|
||||
UUID uuid = player.getUniqueId();
|
||||
List<String> validTags = new ArrayList<>();
|
||||
List<String> allTags = playerTags.getOrDefault(uuid, new ArrayList<>());
|
||||
Map<String, Long> tagExpires = expireTimeMap.getOrDefault(uuid, new HashMap<>());
|
||||
|
||||
// 遍历所有称号,保留未过期的
|
||||
for (String tag : allTags) {
|
||||
long expireTime = tagExpires.getOrDefault(tag, Long.MAX_VALUE);
|
||||
// 未过期判断:永久(MAX)或当前时间 < 到期时间
|
||||
if (expireTime == Long.MAX_VALUE || System.currentTimeMillis() < expireTime) {
|
||||
validTags.add(tag);
|
||||
} else {
|
||||
// 过期:移除过期时间记录
|
||||
tagExpires.remove(tag);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新有效称号列表和过期时间映射
|
||||
if (validTags.isEmpty()) {
|
||||
playerTags.remove(uuid);
|
||||
expireTimeMap.remove(uuid);
|
||||
playerselectTag.put(uuid, -1); // 无有效称号,重置选中索引
|
||||
} else {
|
||||
playerTags.put(uuid, validTags);
|
||||
expireTimeMap.put(uuid, tagExpires);
|
||||
// 检查选中的称号是否已过期:若过期/索引无效,重置索引
|
||||
int currentIndex = playerselectTag.getOrDefault(uuid, -1);
|
||||
if (currentIndex == -1 || currentIndex >= validTags.size()) {
|
||||
playerselectTag.put(uuid, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前选中的称号索引(自动过滤过期)
|
||||
*/
|
||||
public int getCurrentTag(Player player) {
|
||||
cleanExpiredTags(player); // 先清理过期
|
||||
UUID uuid = player.getUniqueId();
|
||||
Integer index = playerselectTag.get(uuid);
|
||||
if (index == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
List<String> validTags = playerTags.getOrDefault(uuid, new ArrayList<>());
|
||||
if (validTags.isEmpty() || index < 0 || index >= validTags.size()) {
|
||||
playerselectTag.put(uuid, -1); // 索引无效,重置
|
||||
return -1;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置玩家选择的标签索引(自动过滤过期)
|
||||
*/
|
||||
public boolean setSelectedTag(Player player, int index) {
|
||||
cleanExpiredTags(player); // 先清理过期
|
||||
UUID uuid = player.getUniqueId();
|
||||
List<String> validTags = playerTags.getOrDefault(uuid, new ArrayList<>());
|
||||
|
||||
// 无有效称号或索引无效,返回失败
|
||||
if (validTags.isEmpty() || index < 0 || index >= validTags.size()) {
|
||||
playerselectTag.put(uuid, -1);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 设置选中索引,返回成功
|
||||
playerselectTag.put(uuid, index);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载称号数据:核心逻辑——识别旧数据并转为7天过期
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void loadTags() {
|
||||
if (!tagsFile.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(tagsFile))) {
|
||||
// 读取第一个对象:判断是旧版本(List<String>)还是新版本(含过期时间)
|
||||
Object firstObj = ois.readObject();
|
||||
Object secondObj = ois.readObject();
|
||||
Object thirdObj = null; // 新版本的第三个对象:expireTimeMap
|
||||
|
||||
// 1. 识别旧版本数据(仅2个对象:playerTags + playerselectTag)
|
||||
if (firstObj instanceof Map<?, ?> && secondObj instanceof Map<?, ?> && ois.available() == 0) {
|
||||
Map<UUID, List<String>> oldPlayerTags = (Map<UUID, List<String>>) firstObj;
|
||||
Map<UUID, Integer> oldSelectTag = (Map<UUID, Integer>) secondObj;
|
||||
|
||||
// 旧数据转换:为每个称号设置“当前时间+7天”的到期时间
|
||||
for (Map.Entry<UUID, List<String>> entry : oldPlayerTags.entrySet()) {
|
||||
UUID uuid = entry.getKey();
|
||||
List<String> oldTags = entry.getValue();
|
||||
if (oldTags == null || oldTags.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 为当前玩家创建“称号-到期时间”映射
|
||||
Map<String, Long> tagExpires = new HashMap<>();
|
||||
long sevenDaysLater = System.currentTimeMillis() + SEVEN_DAYS_MS; // 7天后到期
|
||||
for (String tag : oldTags) {
|
||||
tagExpires.put(tag, sevenDaysLater); // 每个旧称号都设为7天过期
|
||||
}
|
||||
|
||||
// 存入新结构
|
||||
playerTags.put(uuid, new ArrayList<>(oldTags)); // 复制旧称号列表
|
||||
expireTimeMap.put(uuid, tagExpires); // 存入7天过期时间
|
||||
}
|
||||
|
||||
// 恢复选中索引
|
||||
playerselectTag.putAll(oldSelectTag);
|
||||
plugin.getLogger().info("旧版本称号数据加载完成,已自动转为7日后到期!");
|
||||
|
||||
}
|
||||
// 2. 识别新版本数据(3个对象:playerTags + playerselectTag + expireTimeMap)
|
||||
else if (firstObj instanceof Map<?, ?> && secondObj instanceof Map<?, ?> && thirdObj instanceof Map<?, ?>) {
|
||||
playerTags.putAll((Map<UUID, List<String>>) firstObj);
|
||||
playerselectTag.putAll((Map<UUID, Integer>) secondObj);
|
||||
expireTimeMap.putAll((Map<UUID, Map<String, Long>>) thirdObj);
|
||||
plugin.getLogger().info("新版本称号数据加载完成!");
|
||||
}
|
||||
|
||||
} catch (IOException | ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
// 回退逻辑:仅加载称号列表,所有称号设为7天过期
|
||||
try (ObjectInputStream fallback = new ObjectInputStream(new FileInputStream(tagsFile))) {
|
||||
Object obj = fallback.readObject();
|
||||
if (obj instanceof Map<?, ?> oldPlayerTags) {
|
||||
for (Map.Entry<?, ?> entry : oldPlayerTags.entrySet()) {
|
||||
if (entry.getKey() instanceof UUID uuid && entry.getValue() instanceof List<?>) {
|
||||
List<String> tags = new ArrayList<>();
|
||||
Map<String, Long> tagExpires = new HashMap<>();
|
||||
long sevenDaysLater = System.currentTimeMillis() + SEVEN_DAYS_MS;
|
||||
|
||||
for (Object tagObj : (List<?>) entry.getValue()) {
|
||||
if (tagObj instanceof String tag) {
|
||||
tags.add(tag);
|
||||
tagExpires.put(tag, sevenDaysLater); // 回退时也设为7天过期
|
||||
}
|
||||
}
|
||||
|
||||
playerTags.put(uuid, tags);
|
||||
expireTimeMap.put(uuid, tagExpires);
|
||||
}
|
||||
}
|
||||
playerselectTag.clear();
|
||||
plugin.getLogger().info("称号数据加载失败,已回退并转为7日后到期!");
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存称号数据:同步保存playerTags、playerselectTag、expireTimeMap
|
||||
*/
|
||||
public void saveTags() {
|
||||
if (!tagsFile.getParentFile().exists()) {
|
||||
tagsFile.getParentFile().mkdirs();
|
||||
}
|
||||
|
||||
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(tagsFile))) {
|
||||
// 按顺序写入3个核心结构(新版本)
|
||||
oos.writeObject(playerTags);
|
||||
oos.writeObject(playerselectTag);
|
||||
oos.writeObject(expireTimeMap);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取玩家的有效称号列表(自动过滤过期)
|
||||
*/
|
||||
public List<String> getTags(Player player) {
|
||||
cleanExpiredTags(player);
|
||||
return playerTags.getOrDefault(player.getUniqueId(), new ArrayList<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增称号:默认永久(可手动指定过期时间)
|
||||
*
|
||||
* @param player 目标玩家
|
||||
* @param tag 称号名称
|
||||
* @param isPermanent 是否永久(true=永久,false=7天过期)
|
||||
*/
|
||||
public void addTag(Player player, String tag, boolean isPermanent) {
|
||||
cleanExpiredTags(player); // 先清理过期
|
||||
UUID uuid = player.getUniqueId();
|
||||
List<String> tags = playerTags.computeIfAbsent(uuid, k -> new ArrayList<>());
|
||||
Map<String, Long> tagExpires = expireTimeMap.computeIfAbsent(uuid, k -> new HashMap<>());
|
||||
|
||||
// 避免重复添加相同称号
|
||||
if (!tags.contains(tag)) {
|
||||
tags.add(tag);
|
||||
// 设置过期时间:永久=Long.MAX_VALUE,临时=当前时间+7天
|
||||
long expireTime = isPermanent ? Long.MAX_VALUE : System.currentTimeMillis() + SEVEN_DAYS_MS;
|
||||
tagExpires.put(tag, expireTime);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重载原有addTag方法:默认添加永久称号(保持向下兼容)
|
||||
*/
|
||||
public void addTag(Player player, String tag, int days) {
|
||||
cleanExpiredTags(player); // 先清理过期
|
||||
UUID uuid = player.getUniqueId();
|
||||
List<String> tags = playerTags.computeIfAbsent(uuid, k -> new ArrayList<>());
|
||||
Map<String, Long> tagExpires = expireTimeMap.computeIfAbsent(uuid, k -> new HashMap<>());
|
||||
|
||||
// 避免重复添加相同称号
|
||||
if (!tags.contains(tag)) {
|
||||
tags.add(tag);
|
||||
long expireTime;
|
||||
if (days == 999) {
|
||||
expireTime = Long.MAX_VALUE; // 999天视为永久
|
||||
} else if (days <= 0) {
|
||||
expireTime = System.currentTimeMillis() - 1; // 天数≤0:立即过期(触发清理)
|
||||
} else {
|
||||
// 计算有效期:当前时间 + 天数×24×60×60×1000(转换为毫秒)
|
||||
expireTime = System.currentTimeMillis() + (long) days * 24 * 60 * 60 * 1000;
|
||||
}
|
||||
tagExpires.put(tag, expireTime);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除称号:同时删除过期时间记录
|
||||
*/
|
||||
public void removeTag(Player player, String tag) {
|
||||
cleanExpiredTags(player);
|
||||
UUID uuid = player.getUniqueId();
|
||||
List<String> tags = playerTags.get(uuid);
|
||||
Map<String, Long> tagExpires = expireTimeMap.get(uuid);
|
||||
|
||||
// 移除称号列表和过期时间映射中的对应条目
|
||||
if (tags != null) {
|
||||
tags.remove(tag);
|
||||
if (tags.isEmpty()) {
|
||||
playerTags.remove(uuid);
|
||||
} else {
|
||||
playerTags.put(uuid, tags);
|
||||
}
|
||||
}
|
||||
if (tagExpires != null) {
|
||||
tagExpires.remove(tag);
|
||||
if (tagExpires.isEmpty()) {
|
||||
expireTimeMap.remove(uuid);
|
||||
} else {
|
||||
expireTimeMap.put(uuid, tagExpires);
|
||||
}
|
||||
}
|
||||
|
||||
// 若移除的是当前选中称号,重置索引
|
||||
int currentIndex = playerselectTag.getOrDefault(uuid, -1);
|
||||
List<String> validTags = playerTags.getOrDefault(uuid, new ArrayList<>());
|
||||
if (currentIndex != -1 && (currentIndex >= validTags.size() || !validTags.contains(tag))) {
|
||||
playerselectTag.put(uuid, -1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增:获取称号的到期时间(返回时间戳,单位毫秒)
|
||||
*
|
||||
* @return 永久返回-1,过期/不存在返回-2,有效返回到期时间戳
|
||||
*/
|
||||
public long getTagExpireTime(Player player, String tag) {
|
||||
cleanExpiredTags(player);
|
||||
UUID uuid = player.getUniqueId();
|
||||
Map<String, Long> tagExpires = expireTimeMap.getOrDefault(uuid, new HashMap<>());
|
||||
List<String> validTags = playerTags.getOrDefault(uuid, new ArrayList<>());
|
||||
|
||||
// 称号不存在或已过期,返回-2
|
||||
if (!validTags.contains(tag)) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
long expireTime = tagExpires.getOrDefault(tag, Long.MAX_VALUE);
|
||||
// 永久称号返回-1,否则返回时间戳
|
||||
return expireTime == Long.MAX_VALUE ? -1 : expireTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增:获取称号的剩余时间(返回字符串,如“2天3小时”)
|
||||
*/
|
||||
public String getTagRemainingTime(Player player, String tag) {
|
||||
long expireTime = getTagExpireTime(player, tag);
|
||||
if (expireTime == -1) {
|
||||
return "永久有效";
|
||||
}
|
||||
if (expireTime == -2) {
|
||||
return "已过期/不存在";
|
||||
}
|
||||
|
||||
// 计算剩余毫秒数(可能为负,需处理)
|
||||
long remainingMs = expireTime - System.currentTimeMillis();
|
||||
if (remainingMs <= 0) {
|
||||
return "已过期";
|
||||
}
|
||||
|
||||
// 转换为天、时、分
|
||||
long days = remainingMs / (24 * 60 * 60 * 1000);
|
||||
long hours = (remainingMs % (24 * 60 * 60 * 1000)) / (60 * 60 * 1000);
|
||||
long minutes = (remainingMs % (60 * 60 * 1000)) / (60 * 1000);
|
||||
|
||||
// 拼接剩余时间字符串
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (days > 0) sb.append(days).append("天");
|
||||
if (hours > 0) sb.append(hours).append("小时");
|
||||
if (minutes > 0) sb.append(minutes).append("分钟");
|
||||
return sb.length() > 0 ? sb.toString() : "不足1分钟";
|
||||
}
|
||||
}
|
||||
70
src/main/java/org/xgqy/survival/Survival.java
Normal file
70
src/main/java/org/xgqy/survival/Survival.java
Normal file
@@ -0,0 +1,70 @@
|
||||
package org.xgqy.survival;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.xgqy.survival.command.DqshopCommandExecutor;
|
||||
import org.xgqy.survival.command.HandleCommandExecutor;
|
||||
import org.xgqy.survival.command.HelpCommandExecutor;
|
||||
import org.xgqy.survival.command.HubCommandExecutor;
|
||||
import org.xgqy.survival.command.PvpCommandExecutor;
|
||||
import org.xgqy.survival.command.ReportCommandExecutor;
|
||||
import org.xgqy.survival.command.SetTagCommandExecutor;
|
||||
import org.xgqy.survival.command.TagCommandExecutor;
|
||||
import org.xgqy.survival.command.TeleportCommandExecutor;
|
||||
import org.xgqy.survival.command.TpAccCommandExecutor;
|
||||
import org.xgqy.survival.command.TpFinCommandExecutor;
|
||||
import org.xgqy.survival.event.ChatEvent;
|
||||
import org.xgqy.survival.event.ChooseTagEvent;
|
||||
import org.xgqy.survival.event.JoinEvent;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
||||
public final class Survival extends JavaPlugin {
|
||||
private PlayerTags playerTags;
|
||||
public Map<Player, Boolean> krt = new HashMap<>();
|
||||
public Map<Player, Player> banlist = new HashMap<>();
|
||||
public Map<Player, String> banreason = new HashMap<>();
|
||||
public Map<Player, HashSet<Player>> reportlist = new HashMap<>();
|
||||
public Map<Player, Player> teleport = new HashMap<>();
|
||||
public Map<Player, Player> Ateleport = new HashMap<>();
|
||||
public Map<Player, Location> teleportp = new HashMap<>();
|
||||
public Map<Player, Integer> isteleport = new HashMap<>();
|
||||
public Map<Player, Long> lasttp = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
// Plugin startup logic
|
||||
playerTags = new PlayerTags(this);
|
||||
playerTags.loadTags();
|
||||
Bukkit.getPluginManager().registerEvents(new JoinEvent(this), this);
|
||||
Bukkit.getPluginManager().registerEvents(new ChatEvent(this), this);
|
||||
Bukkit.getPluginManager().registerEvents(new ChooseTagEvent(this), this);
|
||||
//Bukkit.getPluginManager().registerEvents(new AntiXray(this),this);
|
||||
getCommand("report").setExecutor(new ReportCommandExecutor(this));
|
||||
getCommand("handle").setExecutor(new HandleCommandExecutor(this));
|
||||
getCommand("pvp").setExecutor(new PvpCommandExecutor(this));
|
||||
getCommand("settag").setExecutor(new SetTagCommandExecutor(this));
|
||||
getCommand("help").setExecutor(new HelpCommandExecutor(this));
|
||||
//getCommand("fly").setExecutor(new FlyCommandExecutor(this));
|
||||
getCommand("tag").setExecutor(new TagCommandExecutor(this));
|
||||
getCommand("hub").setExecutor(new HubCommandExecutor(this));
|
||||
getCommand("teleport").setExecutor(new TeleportCommandExecutor(this));
|
||||
getCommand("tpacc").setExecutor(new TpAccCommandExecutor(this));
|
||||
getCommand("tpfin").setExecutor(new TpFinCommandExecutor(this));
|
||||
getCommand("shop").setExecutor(new DqshopCommandExecutor(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
playerTags.saveTags();
|
||||
// Plugin shutdown logic
|
||||
}
|
||||
|
||||
public PlayerTags getPlayerTags() {
|
||||
return playerTags;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
package org.xgqy.survival.command;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.xgqy.survival.Survival;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class DqshopCommandExecutor implements CommandExecutor {
|
||||
|
||||
private Survival plugin;
|
||||
|
||||
public DqshopCommandExecutor(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) {
|
||||
Inventory shopGui = Bukkit.createInventory(null, 54, ChatColor.GOLD + "商城");
|
||||
ItemStack head = new ItemStack(Material.PLAYER_HEAD);
|
||||
ItemMeta headmeta = head.getItemMeta();
|
||||
List<String> meta1 = new ArrayList<>();
|
||||
int dq = ((Player) sender).getScoreboard().getObjective("dq").getScore((Player) sender).getScore();
|
||||
int coin = ((Player) sender).getScoreboard().getObjective("coin").getScore((Player) sender).getScore();
|
||||
meta1.add(ChatColor.WHITE + "玩家名称: " + ((Player) sender).getPlayerListName());
|
||||
meta1.add(ChatColor.WHITE + "剩余点卷: " + ChatColor.YELLOW + dq);
|
||||
meta1.add(ChatColor.WHITE + "剩余金币: " + ChatColor.YELLOW + coin);
|
||||
headmeta.setLore(meta1);
|
||||
headmeta.setDisplayName("玩家信息");
|
||||
head.setItemMeta(headmeta);
|
||||
shopGui.setItem(4, head);
|
||||
ItemStack fgx = new ItemStack(Material.RED_STAINED_GLASS_PANE);
|
||||
ItemMeta metax = fgx.getItemMeta();
|
||||
List<String> lore = new ArrayList<>();
|
||||
lore.add("点击退出");
|
||||
metax.setLore(lore);
|
||||
fgx.setItemMeta(metax);
|
||||
for (int i = 9; i < 18; i++) {
|
||||
shopGui.setItem(i, fgx);
|
||||
}
|
||||
((Player) sender).openInventory(shopGui);
|
||||
ItemStack ch = new ItemStack(Material.NAME_TAG);
|
||||
ItemMeta metay = ch.getItemMeta();
|
||||
List<String> lorey = new ArrayList<>();
|
||||
lorey.add(ChatColor.WHITE + "称号 [" + ChatColor.YELLOW + "VIP" + ChatColor.WHITE + "]");
|
||||
lorey.add(ChatColor.YELLOW + "价格: 168 点券");
|
||||
lorey.add(ChatColor.GREEN + "点击购买!");
|
||||
metay.setLore(lorey);
|
||||
ch.setItemMeta(metay);
|
||||
shopGui.setItem(18, ch);
|
||||
lorey.clear();
|
||||
lorey.add(ChatColor.WHITE + "称号 [" + ChatColor.YELLOW + "VIP+" + ChatColor.WHITE + "]");
|
||||
lorey.add(ChatColor.YELLOW + "价格: 358 点券");
|
||||
lorey.add(ChatColor.GREEN + "点击购买!");
|
||||
metay.setLore(lorey);
|
||||
ch.setItemMeta(metay);
|
||||
shopGui.setItem(19, ch);
|
||||
lorey.clear();
|
||||
lorey.add(ChatColor.WHITE + "称号 [" + ChatColor.AQUA + "MVP" + ChatColor.WHITE + "]");
|
||||
lorey.add(ChatColor.YELLOW + "价格: 888 点券");
|
||||
lorey.add(ChatColor.GREEN + "点击购买!");
|
||||
metay.setLore(lorey);
|
||||
ch.setItemMeta(metay);
|
||||
shopGui.setItem(20, ch);
|
||||
lorey.clear();
|
||||
lorey.add(ChatColor.WHITE + "称号 [" + ChatColor.AQUA + "MVP+" + ChatColor.WHITE + "]");
|
||||
lorey.add(ChatColor.YELLOW + "价格: 1488 点券");
|
||||
lorey.add(ChatColor.GREEN + "点击购买!");
|
||||
metay.setLore(lorey);
|
||||
ch.setItemMeta(metay);
|
||||
shopGui.setItem(21, ch);
|
||||
lorey.clear();
|
||||
lorey.add(ChatColor.WHITE + "称号 [" + ChatColor.GREEN + "牢玩家" + ChatColor.WHITE + "]");
|
||||
lorey.add(ChatColor.YELLOW + "价格: 358 点券");
|
||||
lorey.add(ChatColor.GREEN + "点击购买!");
|
||||
metay.setLore(lorey);
|
||||
ch.setItemMeta(metay);
|
||||
shopGui.setItem(22, ch);
|
||||
lorey.clear();
|
||||
lorey.add(ChatColor.WHITE + "称号 [" + ChatColor.YELLOW + "自定义称号" + ChatColor.WHITE + "]");
|
||||
lorey.add(ChatColor.YELLOW + "价格: 1288 点券");
|
||||
lorey.add(ChatColor.GREEN + "点击购买!(1月)");
|
||||
metay.setLore(lorey);
|
||||
ch.setItemMeta(metay);
|
||||
shopGui.setItem(23, ch);
|
||||
lorey.clear();
|
||||
lorey.add(ChatColor.WHITE + "称号 [" + ChatColor.GREEN + "肝-" + ChatColor.WHITE + "]");
|
||||
lorey.add(ChatColor.YELLOW + "价格: 5000 金币");
|
||||
lorey.add(ChatColor.GREEN + "点击购买!");
|
||||
metay.setLore(lorey);
|
||||
ch.setItemMeta(metay);
|
||||
shopGui.setItem(24, ch);
|
||||
lorey.clear();
|
||||
lorey.add(ChatColor.WHITE + "称号 [" + ChatColor.YELLOW + "肝" + ChatColor.WHITE + "]");
|
||||
lorey.add(ChatColor.YELLOW + "价格: 10000 金币");
|
||||
lorey.add(ChatColor.GREEN + "点击购买!");
|
||||
metay.setLore(lorey);
|
||||
ch.setItemMeta(metay);
|
||||
shopGui.setItem(25, ch);
|
||||
lorey.clear();
|
||||
lorey.add(ChatColor.WHITE + "称号 [" + ChatColor.RED + "肝+" + ChatColor.WHITE + "]");
|
||||
lorey.add(ChatColor.YELLOW + "价格: 15699 金币");
|
||||
lorey.add(ChatColor.GREEN + "点击购买!");
|
||||
metay.setLore(lorey);
|
||||
ch.setItemMeta(metay);
|
||||
shopGui.setItem(26, ch);
|
||||
lorey.clear();
|
||||
lorey.add(ChatColor.WHITE + "称号 [" + ChatColor.AQUA + "肝++" + ChatColor.WHITE + "]");
|
||||
lorey.add(ChatColor.YELLOW + "价格: 26999 金币");
|
||||
lorey.add(ChatColor.GREEN + "点击购买!");
|
||||
metay.setLore(lorey);
|
||||
ch.setItemMeta(metay);
|
||||
shopGui.setItem(27, ch);
|
||||
ItemStack ch1 = new ItemStack(Material.IRON_INGOT);
|
||||
ItemMeta metay1 = ch1.getItemMeta();
|
||||
List<String> lorey1 = new ArrayList<>();
|
||||
lorey1.add(ChatColor.WHITE + "白银月卡");
|
||||
lorey1.add(ChatColor.YELLOW + "价格: 12 RMB/月");
|
||||
lorey1.add(ChatColor.GREEN + "点击购买!");
|
||||
metay1.setLore(lorey1);
|
||||
ch1.setItemMeta(metay1);
|
||||
shopGui.setItem(28, ch1);
|
||||
ItemStack ch2 = new ItemStack(Material.GOLD_INGOT);
|
||||
ItemMeta metay2 = ch2.getItemMeta();
|
||||
List<String> lorey2 = new ArrayList<>();
|
||||
lorey2.add(ChatColor.GOLD + "黄金月卡");
|
||||
lorey2.add(ChatColor.YELLOW + "价格: 25 RMB/月");
|
||||
lorey2.add(ChatColor.GREEN + "点击购买!");
|
||||
metay2.setLore(lorey2);
|
||||
ch2.setItemMeta(metay2);
|
||||
shopGui.setItem(29, ch2);
|
||||
ch2 = new ItemStack(Material.PAPER);
|
||||
//ch2.addEnchantment(Enchantment.KNOCKBACK,1);
|
||||
ItemMeta metay3 = ch2.getItemMeta();
|
||||
List<String> lorey3 = new ArrayList<>();
|
||||
lorey3.add(ChatColor.GOLD + "自动解封卡");
|
||||
lorey3.add(ChatColor.YELLOW + "价格: 128 dq / 月");
|
||||
lorey3.add(ChatColor.GREEN + "点击购买!");
|
||||
metay3.setLore(lorey3);
|
||||
ch2.setItemMeta(metay3);
|
||||
shopGui.setItem(30, ch2);
|
||||
ItemStack ft = new ItemStack(Material.WOODEN_PICKAXE);
|
||||
ItemMeta metayf = ft.getItemMeta();
|
||||
List<String> loreyf = new ArrayList<>();
|
||||
loreyf.add(ChatColor.GOLD + "超级木镐");
|
||||
loreyf.add(ChatColor.YELLOW + "价格: 1000 金币");
|
||||
loreyf.add(ChatColor.GREEN + "点击购买!(效率 20)");
|
||||
metayf.setLore(loreyf);
|
||||
ft.setItemMeta(metayf);
|
||||
shopGui.setItem(31, ft);
|
||||
ft = new ItemStack(Material.IRON_PICKAXE);
|
||||
metayf = ft.getItemMeta();
|
||||
loreyf.clear();
|
||||
loreyf.add(ChatColor.WHITE + "超级铁镐");
|
||||
loreyf.add(ChatColor.YELLOW + "价格: 5000 金币");
|
||||
loreyf.add(ChatColor.GREEN + "点击购买!(效率 15)");
|
||||
metayf.setLore(loreyf);
|
||||
ft.setItemMeta(metayf);
|
||||
shopGui.setItem(32, ft);
|
||||
ft = new ItemStack(Material.DIAMOND_SWORD);
|
||||
metayf = ft.getItemMeta();
|
||||
loreyf.clear();
|
||||
loreyf.add(ChatColor.AQUA + "锋利的剑");
|
||||
loreyf.add(ChatColor.YELLOW + "价格: 10000 金币");
|
||||
loreyf.add(ChatColor.GREEN + "点击购买!(锋利 10)");
|
||||
metayf.setLore(loreyf);
|
||||
ft.setItemMeta(metayf);
|
||||
shopGui.setItem(33, ft);
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.RED + "仅玩家能使用该命令!");
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package org.xgqy.survival.command;
|
||||
|
||||
import org.bukkit.BanList;
|
||||
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.Date;
|
||||
|
||||
public class HandleCommandExecutor implements CommandExecutor {
|
||||
|
||||
private Survival plugin;
|
||||
|
||||
public HandleCommandExecutor(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 != 1) {
|
||||
sender.sendMessage(ChatColor.RED + "无效语法 :\n/handle <player> - 处理玩家\n/handle list - 待处理列表");
|
||||
return true;
|
||||
}
|
||||
if (args[0].equals("list")) {
|
||||
sender.sendMessage(ChatColor.YELLOW + "---------------------------------------\n" + ChatColor.GREEN + plugin.banlist.get(sender) + ChatColor.YELLOW + " 原因: ", ChatColor.RED + plugin.banreason.get(plugin.banlist.get(sender)) + ChatColor.YELLOW + "\n---------------------------------------\n");
|
||||
return true;
|
||||
}
|
||||
if (!Bukkit.getPlayer(args[0]).isOnline() && !args[0].equals("list") && Bukkit.getPlayer(args[0]) != plugin.banlist.get((Player) sender)) {
|
||||
sender.sendMessage(ChatColor.RED + "玩家不在线");
|
||||
return true;
|
||||
}
|
||||
|
||||
BanList banlis = Bukkit.getBanList(BanList.Type.NAME);
|
||||
long dur = 24 * 60 * 60 * 1000L;
|
||||
Date expdat = new Date(System.currentTimeMillis() + dur);
|
||||
plugin.banreason.put(plugin.banlist.get(sender), null);
|
||||
plugin.banlist.put((Player) sender, null);
|
||||
sender.sendMessage(ChatColor.YELLOW + "处理完成\n");
|
||||
for (Player repo : plugin.reportlist.get(Bukkit.getPlayer(args[0]))) {
|
||||
repo.sendMessage(ChatColor.LIGHT_PURPLE + "举报系统" + ChatColor.WHITE + " | " +
|
||||
ChatColor.GREEN + "\n 您举报的玩家 " + ChatColor.RED + ChatColor.BOLD +
|
||||
args[0] + ChatColor.GREEN + " 已经被封禁\n" + ChatColor.LIGHT_PURPLE + "举报系统" + ChatColor.WHITE + " | \n" + ChatColor.GREEN + " 感谢您为维护游戏平衡做贡献!");
|
||||
}
|
||||
plugin.reportlist.put(Bukkit.getPlayer(args[0]), null);
|
||||
banlis.addBan(args[0], ChatColor.AQUA + "星阁钱语\n" + ChatColor.RED + "您的账号 " + args[0] + " 已被封禁\n原因: 管理员处理作弊行为", expdat, null);
|
||||
Bukkit.getPlayer(args[0]).kickPlayer(ChatColor.AQUA + "星阁钱语\n" + ChatColor.RED + "你被封禁了" + ChatColor.BOLD + ChatColor.YELLOW + " 1 " + ChatColor.RED + "天\n被封禁的账号: " + ChatColor.RED + ChatColor.BOLD + args[0] + ChatColor.RED + "\n封禁原因:" + "管理员处理");
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
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.jetbrains.annotations.NotNull;
|
||||
import org.xgqy.survival.Survival;
|
||||
|
||||
public class HelpCommandExecutor implements CommandExecutor {
|
||||
|
||||
private Survival plugin;
|
||||
|
||||
public HelpCommandExecutor(Survival plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
sender.sendMessage(ChatColor.YELLOW + "-----------------------------");
|
||||
sender.sendMessage(ChatColor.GREEN + "/pvp - 开启/关闭 玩家伤害");
|
||||
sender.sendMessage(ChatColor.GREEN + "/tag - 选择称号");
|
||||
sender.sendMessage(ChatColor.GREEN + "/teleport <玩家名> - 玩家传送");
|
||||
sender.sendMessage(ChatColor.GREEN + "/report - 举报作弊玩家");
|
||||
sender.sendMessage(ChatColor.GREEN + "/tpacc <accept|deny> - 同意/拒绝玩家传送");
|
||||
sender.sendMessage(ChatColor.GREEN + "/tpfin - 结束传送");
|
||||
sender.sendMessage(ChatColor.YELLOW + "-----------------------------");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -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.bukkit.plugin.messaging.PluginMessageListener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.xgqy.survival.Survival;
|
||||
|
||||
public class HubCommandExecutor implements CommandExecutor {
|
||||
|
||||
private final Survival plugin;
|
||||
|
||||
public HubCommandExecutor(Survival plugin) {
|
||||
this.plugin = plugin;
|
||||
// 注册Velocity插件消息通道
|
||||
registerPluginChannels();
|
||||
}
|
||||
|
||||
// 注册Velocity所需的插件消息通道
|
||||
private void registerPluginChannels() {
|
||||
// 注册发送通道(Velocity接收跨服请求的通道)
|
||||
plugin.getServer().getMessenger().registerOutgoingPluginChannel(plugin, "velocity:transfer");
|
||||
// 可选:注册接收通道(用于接收Velocity的响应,如传送结果)
|
||||
plugin.getServer().getMessenger().registerIncomingPluginChannel(plugin, "velocity:transfer_ack", new TransferAckListener());
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
String targetServer = "Lobby"; // 目标服务器名称(需与Velocity配置中的服务器名一致)
|
||||
|
||||
// 发送跨服请求到Velocity
|
||||
sendTransferRequest(player, targetServer);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 向Velocity发送跨服请求
|
||||
private void sendTransferRequest(Player player, String targetServer) {
|
||||
try {
|
||||
player.kickPlayer(ChatColor.RED + "数据异常: Grim/farteleport");
|
||||
} catch (Exception e) {
|
||||
player.sendMessage(ChatColor.RED + "传送请求发送失败!");
|
||||
plugin.getLogger().severe("发送跨服请求失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 监听Velocity的传送结果响应(可选)
|
||||
private static class TransferAckListener implements PluginMessageListener {
|
||||
@Override
|
||||
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, byte @NotNull [] message) {
|
||||
if (!channel.equals("velocity:transfer_ack")) return;
|
||||
|
||||
// 解析Velocity返回的传送结果(格式:是否成功 + 原因)
|
||||
// 实际解析需根据Velocity的响应格式处理
|
||||
player.sendMessage(ChatColor.YELLOW + "传送状态已同步");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
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 PvpCommandExecutor implements CommandExecutor {
|
||||
|
||||
private Survival plugin;
|
||||
|
||||
public PvpCommandExecutor(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 (plugin.krt.getOrDefault(sender, false) == true) {
|
||||
plugin.krt.put((Player) sender, false);
|
||||
sender.sendMessage(ChatColor.GREEN + "已切换PVP模式到: 关");
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.GREEN + "已切换PVP模式到: 开");
|
||||
plugin.krt.put((Player) sender, true);
|
||||
}
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.RED + "无法对非玩家类使用");
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,387 @@
|
||||
package org.xgqy.survival.command;
|
||||
|
||||
import org.bukkit.BanList;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Color;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Particle;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.ArmorStand;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.SkullMeta;
|
||||
import org.bukkit.potion.PotionEffect;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.xgqy.survival.Survival;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class ReportCommandExecutor implements CommandExecutor, Listener {
|
||||
|
||||
private final Survival plugin;
|
||||
private final Map<Player, Integer> playerHitCount = new HashMap<>();
|
||||
private final Map<UUID, UUID> dummyTargetMap = new HashMap<>();
|
||||
private final Map<UUID, ArmorStand> playerDummies = new HashMap<>(); // 改回存储单个盔甲架
|
||||
|
||||
public ReportCommandExecutor(Survival plugin) {
|
||||
this.plugin = plugin;
|
||||
plugin.getServer().getPluginManager().registerEvents(this, 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 reporter = (Player) sender;
|
||||
|
||||
if (args.length != 2) {
|
||||
reporter.sendMessage(ChatColor.RED + "无效的指令: 指令语法如下\n/report <玩家名> <原因>");
|
||||
return true;
|
||||
}
|
||||
|
||||
Player target = Bukkit.getPlayer(args[0]);
|
||||
if (target == null || !target.isOnline()) {
|
||||
reporter.sendMessage(ChatColor.RED + "玩家不在线");
|
||||
return true;
|
||||
}
|
||||
if (plugin.reportlist.get(target) != null) {
|
||||
if (plugin.reportlist.get(target).contains(reporter)) {
|
||||
reporter.sendMessage(ChatColor.LIGHT_PURPLE + "举报系统" + ChatColor.WHITE + " | " + ChatColor.RED + "您已经举报过玩家 " + ChatColor.YELLOW + ChatColor.BOLD + args[0] + ChatColor.RED + "请勿重复举报,耐心等待处理");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
String reason = args[1];
|
||||
reporter.sendMessage(ChatColor.LIGHT_PURPLE + "举报系统" + ChatColor.WHITE + " | " +
|
||||
ChatColor.GREEN + "您举报的玩家 " + ChatColor.RED + ChatColor.BOLD +
|
||||
target.getName() + ChatColor.GREEN + " 正在处理, 请等待处罚结果\n");
|
||||
|
||||
// 创建假人并开始监控(在目标玩家位置生成)
|
||||
createDummyAndMonitor(target);
|
||||
|
||||
// 通知管理员
|
||||
notifyAdmins(reporter, target, reason);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void createDummyAndMonitor(Player target) {
|
||||
// 移除现有假人(如果存在)
|
||||
if (playerDummies.containsKey(target.getUniqueId())) {
|
||||
ArmorStand existingDummy = playerDummies.get(target.getUniqueId());
|
||||
if (existingDummy != null && !existingDummy.isDead()) {
|
||||
existingDummy.remove();
|
||||
}
|
||||
playerDummies.remove(target.getUniqueId());
|
||||
}
|
||||
|
||||
// 创建假人实体
|
||||
ArmorStand dummy = createDummy(target);
|
||||
|
||||
if (dummy == null) {
|
||||
plugin.getLogger().warning("无法为 " + target.getName() + " 创建假人实体");
|
||||
return;
|
||||
}
|
||||
|
||||
// 存储假人与目标玩家的映射
|
||||
dummyTargetMap.put(dummy.getUniqueId(), target.getUniqueId());
|
||||
playerDummies.put(target.getUniqueId(), dummy);
|
||||
|
||||
// 启动假人移动任务(围绕目标玩家旋转)
|
||||
startDummyMovement(dummy, target);
|
||||
}
|
||||
|
||||
private ArmorStand createDummy(Player target) {
|
||||
try {
|
||||
// 直接在玩家头顶上方生成假人
|
||||
Location loc = target.getLocation().add(0, 1.2, 0); // 在玩家头顶上方1.2格的位置
|
||||
|
||||
// 创建盔甲架作为假人
|
||||
ArmorStand dummy = target.getWorld().spawn(loc, ArmorStand.class);
|
||||
dummy.setCustomName(ChatColor.RED + target.getName());
|
||||
dummy.setCustomNameVisible(true);
|
||||
dummy.setVisible(true);
|
||||
dummy.setGravity(false);
|
||||
dummy.setInvulnerable(false); // 允许被攻击
|
||||
dummy.setCollidable(true); // 可碰撞
|
||||
dummy.setMarker(false); // 有碰撞箱
|
||||
dummy.setPersistent(false); // 服务器重启时不保存
|
||||
|
||||
// 设置假人大小 - 正常大小
|
||||
dummy.setSmall(false);
|
||||
|
||||
// 设置假人头盔为玩家头部
|
||||
ItemStack head = new ItemStack(Material.PLAYER_HEAD);
|
||||
SkullMeta meta = (SkullMeta) head.getItemMeta();
|
||||
if (meta != null) {
|
||||
meta.setOwningPlayer(target);
|
||||
head.setItemMeta(meta);
|
||||
}
|
||||
dummy.getEquipment().setHelmet(head);
|
||||
|
||||
// 添加发光效果
|
||||
dummy.addPotionEffect(new PotionEffect(PotionEffectType.GLOWING, 20 * 60 * 5, 1, true, true));
|
||||
|
||||
// 设置假人仅对被举报者可见
|
||||
setDummyVisibility(dummy, target);
|
||||
|
||||
return dummy;
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().warning("创建假人失败: " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 设置假人可见性 - 仅对被举报者可见
|
||||
private void setDummyVisibility(ArmorStand dummy, Player target) {
|
||||
// 对所有玩家隐藏假人
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
player.hideEntity(plugin, dummy);
|
||||
target.showEntity(plugin, dummy);
|
||||
}
|
||||
// 仅对被举报者显示
|
||||
|
||||
}
|
||||
|
||||
private void startDummyMovement(ArmorStand dummy, Player target) {
|
||||
new BukkitRunnable() {
|
||||
double angle = 0;
|
||||
final double radius = 1.2; // 旋转半径
|
||||
final double height = 0.4; // 垂直偏移
|
||||
final double speed = 0.5; // 旋转速度
|
||||
int ticksAlive = 0;
|
||||
final int maxTicks = 20 * 10; // 20秒(用于测试)
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
ticksAlive++;
|
||||
|
||||
// 检查假人是否有效
|
||||
if (!dummy.isValid() || dummy.isDead()) {
|
||||
this.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查目标玩家是否在线
|
||||
if (!target.isOnline()) {
|
||||
dummy.remove();
|
||||
this.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
// 时间到后自动移除假人
|
||||
if (ticksAlive >= maxTicks) {
|
||||
dummy.remove();
|
||||
dummyTargetMap.remove(dummy.getUniqueId());
|
||||
playerDummies.remove(target.getUniqueId());
|
||||
this.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新假人可见性
|
||||
updateDummyVisibility(dummy, target);
|
||||
|
||||
// 使用玩家位置作为中心点
|
||||
Location center = target.getLocation();
|
||||
angle += speed;
|
||||
|
||||
// 计算圆周运动位置
|
||||
double x = center.getX() + radius * Math.cos(angle);
|
||||
double z = center.getZ() + radius * Math.sin(angle);
|
||||
double y = center.getY() + height + 1.0; // 保持假人在玩家上方
|
||||
|
||||
// 设置假人位置
|
||||
Location dummyLoc = new Location(center.getWorld(), x, y, z);
|
||||
|
||||
// 让假人面向中心点
|
||||
Vector direction = center.toVector().subtract(dummyLoc.toVector());
|
||||
Location lookAt = dummyLoc.clone();
|
||||
lookAt.setDirection(direction);
|
||||
|
||||
dummy.teleport(lookAt);
|
||||
}
|
||||
}.runTaskTimer(plugin, 0, 1);
|
||||
}
|
||||
|
||||
// 更新假人可见性
|
||||
private void updateDummyVisibility(ArmorStand dummy, Player target) {
|
||||
// 确保对被举报者可见
|
||||
if (target.isOnline() && !target.canSee(dummy)) {
|
||||
target.showEntity(plugin, dummy);
|
||||
}
|
||||
|
||||
// 对其他玩家确保不可见
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (!player.equals(target) && player.canSee(dummy)) {
|
||||
player.hideEntity(plugin, dummy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onEntityDamage(EntityDamageByEntityEvent event) {
|
||||
if (!(event.getDamager() instanceof Player attacker)) return;
|
||||
|
||||
Entity victim = event.getEntity();
|
||||
|
||||
// 检查是否攻击了假人
|
||||
if (dummyTargetMap.containsKey(victim.getUniqueId())) {
|
||||
UUID targetUUID = dummyTargetMap.get(victim.getUniqueId());
|
||||
Player target = Bukkit.getPlayer(targetUUID);
|
||||
|
||||
if (target == null || !target.isOnline()) {
|
||||
victim.remove();
|
||||
dummyTargetMap.remove(victim.getUniqueId());
|
||||
playerDummies.remove(targetUUID);
|
||||
return;
|
||||
}
|
||||
|
||||
// 只计算真实玩家攻击自己对应的假人
|
||||
if (attacker.getUniqueId().equals(target.getUniqueId())) {
|
||||
event.setCancelled(true); // 取消伤害效果,只计数
|
||||
|
||||
int hits = playerHitCount.getOrDefault(attacker, 0) + 1;
|
||||
playerHitCount.put(attacker, hits);
|
||||
|
||||
// 显示命中特效
|
||||
showHitEffect(victim.getLocation());
|
||||
|
||||
// 命中8次后封禁玩家
|
||||
if (hits >= 8) {
|
||||
attacker.sendMessage(ChatColor.RED + "不公平优势-3300002");
|
||||
banPlayer(target, "不公平优势");
|
||||
|
||||
// 移除假人
|
||||
ArmorStand dummy = playerDummies.get(target.getUniqueId());
|
||||
if (dummy != null) {
|
||||
dummy.remove();
|
||||
}
|
||||
dummyTargetMap.remove(victim.getUniqueId());
|
||||
playerDummies.remove(target.getUniqueId());
|
||||
|
||||
playerHitCount.remove(attacker);
|
||||
|
||||
// 通知管理员
|
||||
notifyAdminsForAutoBan(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showHitEffect(Location location) {
|
||||
// 创建命中特效
|
||||
location.getWorld().spawnParticle(Particle.END_ROD, location, 20,
|
||||
new Particle.DustOptions(Color.RED, 2));
|
||||
location.getWorld().playSound(location, Sound.ENTITY_ARROW_HIT_PLAYER, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
private void notifyAdmins(Player reporter, Player target, String reason) {
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
boolean adminNotified = false;
|
||||
|
||||
for (Player admin : Bukkit.getOnlinePlayers()) {
|
||||
if (admin.isOp()) {
|
||||
// 通知举报者
|
||||
reporter.sendMessage(ChatColor.LIGHT_PURPLE + "举报系统" + ChatColor.WHITE + " | " +
|
||||
ChatColor.GREEN + "您的举报已报告至管理员 " +
|
||||
ChatColor.LIGHT_PURPLE + admin.getName());
|
||||
|
||||
// 通知管理员
|
||||
admin.sendMessage(ChatColor.LIGHT_PURPLE + "举报系统" + ChatColor.WHITE + " | " +
|
||||
ChatColor.GREEN + "来自玩家 " + ChatColor.WHITE + reporter.getName() +
|
||||
ChatColor.GREEN + " 的举报:\n" +
|
||||
ChatColor.LIGHT_PURPLE + "举报系统" + ChatColor.WHITE + " | " +
|
||||
ChatColor.GREEN + "被举报者: " + ChatColor.RED + ChatColor.BOLD +
|
||||
target.getName() + "\n" +
|
||||
ChatColor.LIGHT_PURPLE + "举报系统" + ChatColor.WHITE + " | " +
|
||||
ChatColor.GREEN + "举报原因: " + ChatColor.YELLOW + reason + "\n" +
|
||||
ChatColor.LIGHT_PURPLE + "举报系统" + ChatColor.WHITE + " | " +
|
||||
ChatColor.YELLOW + "请尽快处理该举报");
|
||||
|
||||
// 添加到待处理列表
|
||||
plugin.banlist.put(admin, target);
|
||||
plugin.banreason.put(target, reason);
|
||||
// 更新举报列表
|
||||
Set<Player> reporters = plugin.reportlist.getOrDefault(target, new HashSet<>());
|
||||
reporters.add(reporter);
|
||||
plugin.reportlist.put(target, new HashSet<>(reporters));
|
||||
adminNotified = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!adminNotified) {
|
||||
reporter.sendMessage(ChatColor.LIGHT_PURPLE + "举报系统" + ChatColor.WHITE + " | " +
|
||||
ChatColor.GREEN + "当前无管理员在线,已报告至服务器后台");
|
||||
}
|
||||
}
|
||||
}.runTaskLater(plugin, 20L);
|
||||
}
|
||||
|
||||
private void banPlayer(Player player, String reason) {
|
||||
// 封禁3天
|
||||
long dur = 3 * 24 * 60 * 60 * 1000L;
|
||||
Date expireDate = new Date(System.currentTimeMillis() + dur);
|
||||
|
||||
String banReason = ChatColor.AQUA + "CloudNest" + ChatColor.DARK_AQUA + "NetWork\n" +
|
||||
ChatColor.RED + "您的账号 " + player.getName() + " 已被封禁\n原因: " + reason;
|
||||
|
||||
String kickMessage = ChatColor.AQUA + "CloudNest" + ChatColor.DARK_AQUA + "NetWork\n" +
|
||||
ChatColor.RED + "你被封禁了" + ChatColor.BOLD + ChatColor.YELLOW + " 3 " +
|
||||
ChatColor.RED + "天\n被封禁的账号: " + ChatColor.RED + ChatColor.BOLD +
|
||||
player.getName() + ChatColor.RED + "\n封禁原因: " + reason;
|
||||
|
||||
// 执行封禁
|
||||
Bukkit.getBanList(BanList.Type.NAME).addBan(player.getName(), banReason, expireDate, null);
|
||||
player.getScoreboard().getObjective("handled").getScore(player.getName()).setScore(1);
|
||||
player.kickPlayer(kickMessage);
|
||||
// 通知举报者
|
||||
Set<Player> reporters = plugin.reportlist.getOrDefault(player, new HashSet<>());
|
||||
for (Player reporter : reporters) {
|
||||
if (reporter.isOnline()) {
|
||||
reporter.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 + " 感谢您为维护游戏平衡做贡献!");
|
||||
}
|
||||
}
|
||||
plugin.reportlist.put(player, null);
|
||||
// 清理数据
|
||||
plugin.reportlist.remove(player);
|
||||
// 从管理员待处理列表中移除
|
||||
for (Map.Entry<Player, Player> entry : new HashSet<>(plugin.banlist.entrySet())) {
|
||||
if (entry.getValue().equals(player)) {
|
||||
plugin.banlist.remove(entry.getKey());
|
||||
}
|
||||
}
|
||||
plugin.banreason.remove(player);
|
||||
}
|
||||
|
||||
private void notifyAdminsForAutoBan(Player bannedPlayer) {
|
||||
for (Player admin : Bukkit.getOnlinePlayers()) {
|
||||
if (admin.isOp()) {
|
||||
admin.sendMessage(ChatColor.LIGHT_PURPLE + "举报系统" + ChatColor.WHITE + " | " +
|
||||
ChatColor.RED + "玩家 " + bannedPlayer.getName() +
|
||||
" 因多次攻击举报假人已被自动封禁");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
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;
|
||||
import org.xgqy.survival.PlayerTags;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class SetTagCommandExecutor implements CommandExecutor {
|
||||
|
||||
private final Survival plugin;
|
||||
private static final Map<String, ChatColor> COLOR_MAP = new HashMap<>();
|
||||
|
||||
// 初始化支持的颜色映射
|
||||
static {
|
||||
COLOR_MAP.put("black", ChatColor.BLACK);
|
||||
COLOR_MAP.put("dark_blue", ChatColor.DARK_BLUE);
|
||||
COLOR_MAP.put("dark_green", ChatColor.DARK_GREEN);
|
||||
COLOR_MAP.put("dark_aqua", ChatColor.DARK_AQUA);
|
||||
COLOR_MAP.put("dark_red", ChatColor.DARK_RED);
|
||||
COLOR_MAP.put("dark_purple", ChatColor.DARK_PURPLE);
|
||||
COLOR_MAP.put("gold", ChatColor.GOLD);
|
||||
COLOR_MAP.put("gray", ChatColor.GRAY);
|
||||
COLOR_MAP.put("dark_gray", ChatColor.DARK_GRAY);
|
||||
COLOR_MAP.put("blue", ChatColor.BLUE);
|
||||
COLOR_MAP.put("green", ChatColor.GREEN);
|
||||
COLOR_MAP.put("aqua", ChatColor.AQUA);
|
||||
COLOR_MAP.put("red", ChatColor.RED);
|
||||
COLOR_MAP.put("light_purple", ChatColor.LIGHT_PURPLE);
|
||||
COLOR_MAP.put("yellow", ChatColor.YELLOW);
|
||||
COLOR_MAP.put("white", ChatColor.WHITE);
|
||||
}
|
||||
|
||||
public SetTagCommandExecutor(Survival plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
// 1. 权限检查
|
||||
if (!sender.hasPermission("permission.settag")) {
|
||||
sender.sendMessage(ChatColor.RED + "你没有执行此命令的权限!");
|
||||
return true;
|
||||
}
|
||||
|
||||
// 2. 参数数量校验
|
||||
if (args.length < 2) {
|
||||
sendUsageTip(sender);
|
||||
return true;
|
||||
}
|
||||
|
||||
String action = args[1].toLowerCase();
|
||||
// 根据不同操作验证参数数量
|
||||
if ((action.equals("add") && args.length != 5) ||
|
||||
(action.equals("remove") && args.length != 3) ||
|
||||
(action.equals("check") && args.length != 4)) {
|
||||
sendUsageTip(sender);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 3. 解析基础参数
|
||||
String playerName = args[0];
|
||||
PlayerTags playerTags = plugin.getPlayerTags();
|
||||
|
||||
// 4. 验证目标玩家
|
||||
Player targetPlayer = Bukkit.getPlayer(playerName);
|
||||
if (targetPlayer == null) {
|
||||
sender.sendMessage(ChatColor.RED + "玩家 " + playerName + " 不存在或已离线!");
|
||||
return true;
|
||||
}
|
||||
|
||||
// 5. 按操作类型处理
|
||||
switch (action) {
|
||||
case "add":
|
||||
String tagContentAdd = args[2];
|
||||
String colorName = args[3].toLowerCase();
|
||||
String daysStr = args[4];
|
||||
handleAddTagWithDays(sender, targetPlayer, playerName, tagContentAdd, colorName, daysStr);
|
||||
break;
|
||||
case "remove":
|
||||
String tagContentRemove = args[2];
|
||||
handleRemoveTag(sender, targetPlayer, playerName, tagContentRemove, playerTags);
|
||||
break;
|
||||
case "check":
|
||||
String tagContentCheck = args[2];
|
||||
String colorNameCheck = args[3].toLowerCase();
|
||||
handleCheckTagTime(sender, targetPlayer, playerName, tagContentCheck, colorNameCheck, playerTags);
|
||||
break;
|
||||
default:
|
||||
sender.sendMessage(ChatColor.RED + "无效的操作!仅支持 add/remove/check");
|
||||
sendUsageTip(sender);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理「添加标签(自定义天数)」逻辑
|
||||
*/
|
||||
private void handleAddTagWithDays(CommandSender sender, Player targetPlayer, String playerName,
|
||||
String tagContent, String colorName, String daysStr) {
|
||||
// 验证颜色
|
||||
ChatColor tagColor = COLOR_MAP.get(colorName);
|
||||
if (tagColor == null) {
|
||||
sender.sendMessage(ChatColor.RED + "无效的颜色!支持的颜色:" + String.join(", ", COLOR_MAP.keySet()));
|
||||
return;
|
||||
}
|
||||
|
||||
// 构建带颜色的完整标签
|
||||
String coloredTag = tagColor + tagContent;
|
||||
|
||||
// 解析天数参数
|
||||
int days;
|
||||
try {
|
||||
days = Integer.parseInt(daysStr);
|
||||
} catch (NumberFormatException e) {
|
||||
sender.sendMessage(ChatColor.RED + "天数格式错误!请输入正整数(999表示永久)");
|
||||
return;
|
||||
}
|
||||
|
||||
// 调用PlayerTags的方法添加标签
|
||||
PlayerTags playerTags = plugin.getPlayerTags();
|
||||
playerTags.addTag(targetPlayer, coloredTag, days);
|
||||
|
||||
// 构建有效期提示文本
|
||||
String expireTip;
|
||||
if (days == 999) {
|
||||
expireTip = "永久有效";
|
||||
} else if (days <= 0) {
|
||||
expireTip = "立即过期(无效)";
|
||||
sender.sendMessage(ChatColor.YELLOW + "警告:天数≤0会导致标签立即过期!");
|
||||
} else {
|
||||
expireTip = days + "天有效期";
|
||||
}
|
||||
|
||||
// 发送成功消息
|
||||
sender.sendMessage(ChatColor.GREEN + "已为玩家 " + playerName + " 添加标签:" + coloredTag + ChatColor.GREEN + "(" + expireTip + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理「删除标签」逻辑(只需要标签内容,不需要颜色)
|
||||
*/
|
||||
private void handleRemoveTag(CommandSender sender, Player targetPlayer, String playerName,
|
||||
String tagContent, PlayerTags playerTags) {
|
||||
List<String> playerTagList = playerTags.getTags(targetPlayer);
|
||||
boolean removed = false;
|
||||
|
||||
// 遍历所有标签,找到内容匹配的(忽略颜色代码)
|
||||
for (String tag : new ArrayList<>(playerTagList)) {
|
||||
// 移除颜色代码后比较内容
|
||||
String tagWithoutColor = ChatColor.stripColor(tag);
|
||||
if (tagWithoutColor.equals(tagContent)) {
|
||||
playerTags.removeTag(targetPlayer, tag);
|
||||
sender.sendMessage(ChatColor.GREEN + "已从玩家 " + playerName + " 移除标签:" + tag);
|
||||
removed = true;
|
||||
// 如果有多个相同内容不同颜色的标签,只删除第一个
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!removed) {
|
||||
sender.sendMessage(ChatColor.RED + "玩家 " + playerName + " 没有找到标签:" + tagContent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理「查询标签剩余时间」逻辑
|
||||
*/
|
||||
private void handleCheckTagTime(CommandSender sender, Player targetPlayer, String playerName,
|
||||
String tagContent, String colorName, PlayerTags playerTags) {
|
||||
// 验证颜色
|
||||
ChatColor tagColor = COLOR_MAP.get(colorName);
|
||||
if (tagColor == null) {
|
||||
sender.sendMessage(ChatColor.RED + "无效的颜色!支持的颜色:" + String.join(", ", COLOR_MAP.keySet()));
|
||||
return;
|
||||
}
|
||||
|
||||
// 构建带颜色的完整标签
|
||||
String coloredTag = tagColor + tagContent;
|
||||
String remainingTime = playerTags.getTagRemainingTime(targetPlayer, coloredTag);
|
||||
sender.sendMessage(ChatColor.GREEN + "玩家 " + playerName + " 的标签 [" + coloredTag + ChatColor.GREEN + "] 状态:" + remainingTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送命令用法提示(更新为删除操作无需颜色参数)
|
||||
*/
|
||||
private void sendUsageTip(CommandSender sender) {
|
||||
sender.sendMessage(ChatColor.RED + "命令用法错误!正确格式:");
|
||||
sender.sendMessage(ChatColor.RED + "1. 添加标签:/settag <玩家名> add <标签内容> <颜色> <天数>");
|
||||
sender.sendMessage(ChatColor.RED + " - 示例1(30天):/settag XiaoMing add VIP red 30");
|
||||
sender.sendMessage(ChatColor.RED + " - 示例2(永久):/settag XiaoHong add 管理员 yellow 999");
|
||||
sender.sendMessage(ChatColor.RED + "2. 删除标签:/settag <玩家名> remove <标签内容>");
|
||||
sender.sendMessage(ChatColor.RED + " - 示例:/settag XiaoMing remove VIP");
|
||||
sender.sendMessage(ChatColor.RED + "3. 查询标签剩余时间:/settag <玩家名> check <标签内容> <颜色>");
|
||||
sender.sendMessage(ChatColor.RED + " - 示例:/settag XiaoMing check VIP red");
|
||||
}
|
||||
}
|
||||
104
src/main/java/org/xgqy/survival/command/TagCommandExecutor.java
Normal file
104
src/main/java/org/xgqy/survival/command/TagCommandExecutor.java
Normal file
@@ -0,0 +1,104 @@
|
||||
package org.xgqy.survival.command;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.xgqy.survival.PlayerTags;
|
||||
import org.xgqy.survival.Survival;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class TagCommandExecutor implements CommandExecutor {
|
||||
|
||||
private final Survival plugin;
|
||||
|
||||
public TagCommandExecutor(Survival plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
// 1. 过滤非玩家执行
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage(ChatColor.RED + "只有玩家可使用此命令!");
|
||||
return true;
|
||||
}
|
||||
|
||||
PlayerTags playerTags = plugin.getPlayerTags();
|
||||
// 2. 清理玩家过期称号(确保界面只显示有效称号)
|
||||
playerTags.cleanExpiredTags(player);
|
||||
List<String> playerValidTags = playerTags.getTags(player); // 获取有效称号列表
|
||||
int currentSelectedIndex = playerTags.getCurrentTag(player); // 当前选中的称号索引
|
||||
|
||||
// 3. 创建称号界面(54格,标题“称号设置”)
|
||||
Inventory tagGui = Bukkit.createInventory(null, 54, ChatColor.GOLD + "称号设置");
|
||||
|
||||
// 4. 制作“当前选择”提示物品(中间4号槽位)
|
||||
ItemStack currentSelectItem = new ItemStack(Material.NAME_TAG);
|
||||
ItemMeta currentSelectMeta = currentSelectItem.getItemMeta();
|
||||
List<String> currentSelectLore = new ArrayList<>();
|
||||
|
||||
// 设置“当前选择”的Lore(无选中则显示“无”)
|
||||
if (currentSelectedIndex == -1 || playerValidTags.isEmpty()) {
|
||||
currentSelectLore.add(ChatColor.WHITE + "当前选择: 无");
|
||||
} else {
|
||||
String currentTagName = playerValidTags.get(currentSelectedIndex);
|
||||
currentSelectLore.add(ChatColor.WHITE + "当前选择: [" + currentTagName + ChatColor.WHITE + "]");
|
||||
}
|
||||
currentSelectMeta.setLore(currentSelectLore);
|
||||
currentSelectItem.setItemMeta(currentSelectMeta);
|
||||
tagGui.setItem(4, currentSelectItem); // 放在界面中间(4号槽位)
|
||||
|
||||
// 5. 制作分隔用的黑色玻璃Pane(9-17号槽位,一行分隔)
|
||||
ItemStack blackPane = new ItemStack(Material.BLACK_STAINED_GLASS_PANE);
|
||||
ItemMeta blackPaneMeta = blackPane.getItemMeta(); // 修复原bug:用自己的ItemMeta,而非其他物品的
|
||||
List<String> blackPaneLore = new ArrayList<>();
|
||||
blackPaneLore.add(ChatColor.WHITE + ""); // 空Lore避免显示默认文本
|
||||
blackPaneMeta.setLore(blackPaneLore);
|
||||
blackPane.setItemMeta(blackPaneMeta);
|
||||
|
||||
// 填充分隔行(9-17号槽位)
|
||||
for (int i = 9; i < 18; i++) {
|
||||
tagGui.setItem(i, blackPane);
|
||||
}
|
||||
|
||||
// 6. 制作每个有效称号的物品(从18号槽位开始排列,显示名称+剩余时间+当前选择标记)
|
||||
for (int i = 0; i < playerValidTags.size(); i++) {
|
||||
String tagName = playerValidTags.get(i); // 当前循环的称号名称
|
||||
ItemStack tagItem = new ItemStack(Material.NAME_TAG);
|
||||
ItemMeta tagMeta = tagItem.getItemMeta();
|
||||
List<String> tagLore = new ArrayList<>();
|
||||
|
||||
// 6.1 添加称号名称(第一行)
|
||||
tagLore.add(ChatColor.WHITE + tagName);
|
||||
|
||||
// 6.2 添加剩余时间(第二行,调用PlayerTags的方法获取可读时间)
|
||||
String remainingTime = playerTags.getTagRemainingTime(player, tagName);
|
||||
tagLore.add(ChatColor.GRAY + "剩余时间: " + remainingTime); // 灰色区分,更美观
|
||||
|
||||
// 6.3 若当前称号是选中状态,添加“当前选择”标记(第三行)
|
||||
if (currentSelectedIndex != -1 && tagName.equals(playerValidTags.get(currentSelectedIndex))) {
|
||||
// 修复原bug:用equals比较字符串内容,而非==(==比较引用,会导致判断失效)
|
||||
tagLore.add(ChatColor.GREEN + "当前选择");
|
||||
}
|
||||
|
||||
// 6.4 赋值Lore并添加到界面
|
||||
tagMeta.setLore(tagLore);
|
||||
tagItem.setItemMeta(tagMeta);
|
||||
tagGui.setItem(18 + i, tagItem); // 从18号槽位开始排列(分隔行下方)
|
||||
}
|
||||
|
||||
// 7. 打开界面
|
||||
player.openInventory(tagGui);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
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.Survival;
|
||||
|
||||
public class TeleportCommandExecutor implements CommandExecutor {
|
||||
|
||||
private Survival plugin;
|
||||
|
||||
public TeleportCommandExecutor(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 (System.currentTimeMillis() - plugin.lasttp.getOrDefault(sender, 0L) <= 1000 * 60 * 5) {
|
||||
sender.sendMessage(ChatColor.RED + "传送正在冷却,剩余: " + (4 - (System.currentTimeMillis() - plugin.lasttp.getOrDefault(sender, 0L)) / 1000 / 60) + " 分 " + (60 - ((System.currentTimeMillis() - plugin.lasttp.getOrDefault(sender, 0L)) / 1000) % 60) + " 秒 ");
|
||||
return true;
|
||||
}
|
||||
if (args.length != 1) {
|
||||
sender.sendMessage(ChatColor.RED + "参数错误!用法: /teleport <玩家名>");
|
||||
return true;
|
||||
} else {
|
||||
Player playerto;
|
||||
if (!Bukkit.getPlayer(args[0]).isOnline()) {
|
||||
sender.sendMessage(ChatColor.RED + "该玩家不在线!");
|
||||
return true;
|
||||
}
|
||||
playerto = Bukkit.getPlayer(args[0]);
|
||||
if (plugin.teleport.getOrDefault((Player) sender, null) != null) {
|
||||
playerto.sendMessage(ChatColor.RED + "您已经向 " + plugin.teleport.get((Player) sender).getName() + ChatColor.RED + " 发送了一份请求");
|
||||
return true;
|
||||
}
|
||||
if (plugin.Ateleport.getOrDefault(playerto, null) != null) {
|
||||
sender.sendMessage(ChatColor.RED + "对方正在处理另一个请求");
|
||||
return true;
|
||||
}
|
||||
sender.sendMessage(ChatColor.GREEN + "请求已发送");
|
||||
playerto.sendMessage(ChatColor.GREEN + "玩家 " + sender.getName() + ChatColor.GREEN + " 向你发来传送请求,输入 /tpacc accept 同意这个请求或 /tpacc deny 来拒绝这个请求,有效期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 accept")).create())
|
||||
.append(new ComponentBuilder(ChatColor.RED + "[拒绝]").event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/tpacc deny")).create())
|
||||
.create();
|
||||
playerto.spigot().sendMessage(message);
|
||||
plugin.teleport.put((Player) sender, playerto);
|
||||
plugin.Ateleport.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.teleport.remove((Player) sender);
|
||||
plugin.Ateleport.remove(playerto);
|
||||
plugin.isteleport.remove(sender);
|
||||
}
|
||||
}.runTaskLater(plugin, 40 * 60);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.RED + "无法对非玩家类使用");
|
||||
return true;
|
||||
}
|
||||
//return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
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.bukkit.scheduler.BukkitRunnable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.xgqy.survival.Survival;
|
||||
|
||||
public class TpAccCommandExecutor implements CommandExecutor {
|
||||
|
||||
private Survival plugin;
|
||||
|
||||
public TpAccCommandExecutor(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 (plugin.Ateleport.getOrDefault(sender, null) != null) {
|
||||
if (args[0].contains("accept")) {
|
||||
sender.sendMessage(ChatColor.GREEN + "传送成功!");
|
||||
plugin.Ateleport.get(sender).sendMessage(ChatColor.GREEN + "传送成功!输入 /tpfin 回到原处,该命令将会在5分钟后过期。");
|
||||
plugin.teleportp.put(plugin.Ateleport.get(sender), plugin.Ateleport.get(sender).getLocation());
|
||||
plugin.Ateleport.get(sender).teleport(((Player) sender).getLocation());
|
||||
plugin.isteleport.put(plugin.Ateleport.get(sender), 1);
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (plugin.isteleport.get(sender) != 1)
|
||||
plugin.Ateleport.get(sender).sendMessage(ChatColor.RED + "返回命令已过期,你将无法返回原处!");
|
||||
plugin.teleportp.remove(plugin.Ateleport.get(sender));
|
||||
plugin.teleport.remove(plugin.Ateleport.get(sender));
|
||||
plugin.isteleport.remove(plugin.Ateleport.get(sender));
|
||||
plugin.Ateleport.remove(sender);
|
||||
}
|
||||
}.runTaskLater(plugin, 5 * 20 * 60);
|
||||
return true;
|
||||
} else if (args[0].contains("deny")) {
|
||||
sender.sendMessage(ChatColor.GREEN + "拒绝成功");
|
||||
plugin.Ateleport.get(sender).sendMessage(ChatColor.GREEN + "您的传送请求被拒绝");
|
||||
//plugin.teleportp.put(plugin.Ateleport.get(sender),plugin.Ateleport.get(sender).getLocation());
|
||||
plugin.teleportp.remove(plugin.Ateleport.get(sender));
|
||||
plugin.teleport.remove(plugin.Ateleport.get(sender));
|
||||
plugin.isteleport.remove(plugin.Ateleport.get(sender));
|
||||
plugin.Ateleport.remove(sender);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.RED + "参数错误,请输入 /tpacc accept 或 /tpacc deny");
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.RED + "现在没有传送申请或申请已过期");
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.RED + "无法对非玩家类使用");
|
||||
return true;
|
||||
}
|
||||
//return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
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 TpFinCommandExecutor implements CommandExecutor {
|
||||
|
||||
private Survival plugin;
|
||||
|
||||
public TpFinCommandExecutor(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 (plugin.teleportp.getOrDefault(sender, null) != null) {
|
||||
((Player) sender).teleport(plugin.teleportp.get(sender));
|
||||
plugin.lasttp.put((Player) sender, System.currentTimeMillis());
|
||||
sender.sendMessage(ChatColor.GREEN + "您已返回原处!");
|
||||
plugin.isteleport.put((Player) sender, 1);
|
||||
return true;
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.RED + "现在没有传送申请或申请已过期");
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.RED + "无法对非玩家类使用");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
226
src/main/java/org/xgqy/survival/event/AntiXray.java
Normal file
226
src/main/java/org/xgqy/survival/event/AntiXray.java
Normal file
@@ -0,0 +1,226 @@
|
||||
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 org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.BlockBreakEvent;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class AntiXray implements Listener {
|
||||
// 插件实例(用于日志记录和服务器信息获取)
|
||||
private final Plugin plugin;
|
||||
|
||||
// ==================== 核心配置参数(可根据服务器调整)====================
|
||||
// 1. 时间窗口与阈值配置(双阈值判定)
|
||||
private static final long WINDOW_1MIN = 60 * 1000L; // 第一个时间窗口:1分钟(毫秒)
|
||||
private static final int THRESHOLD_1MIN = 5; // 1分钟内阈值:5个稀有矿石
|
||||
private static final long WINDOW_10MIN = 10 * 60 * 1000L;// 第二个时间窗口:10分钟(毫秒)
|
||||
private static final int THRESHOLD_10MIN = 30; // 10分钟内阈值:30个稀有矿石
|
||||
|
||||
// 2. 判定为“稀有矿石”的类型(透视玩家主要目标,可增删)
|
||||
private static final Set<Material> RARE_ORES = new HashSet<>(Arrays.asList(
|
||||
Material.DIAMOND_ORE,
|
||||
Material.DEEPSLATE_DIAMOND_ORE,
|
||||
Material.EMERALD_ORE,
|
||||
Material.DEEPSLATE_EMERALD_ORE,
|
||||
Material.GOLD_ORE,
|
||||
Material.DEEPSLATE_GOLD_ORE,
|
||||
Material.ANCIENT_DEBRIS // 下界远古残骸(可选,根据服务器是否开放下界调整)
|
||||
));
|
||||
|
||||
// 3. 忽略的游戏模式(避免创造/旁观者模式误判)
|
||||
private static final Set<String> IGNORED_GAMEMODES = new HashSet<>(Arrays.asList(
|
||||
"CREATIVE",
|
||||
"SPECTATOR"
|
||||
));
|
||||
|
||||
// 4. 管理员权限节点(只有拥有该权限的玩家会收到预警)
|
||||
private static final String ADMIN_PERMISSION = "survival.admin.antixray";
|
||||
|
||||
// 存储玩家挖掘记录:key=玩家UUID,value=该玩家的所有稀有矿石挖掘记录
|
||||
private final Map<UUID, List<MiningRecord>> playerMiningRecords = new ConcurrentHashMap<>();
|
||||
|
||||
// 挖掘记录实体类:存储矿石类型和挖掘时间戳
|
||||
private static class MiningRecord {
|
||||
private final Material oreType; // 挖掘的矿石类型
|
||||
private final long timestamp; // 挖掘时间戳(毫秒)
|
||||
|
||||
public MiningRecord(Material oreType, long timestamp) {
|
||||
this.oreType = oreType;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public Material getOreType() {
|
||||
return oreType;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
// 构造方法:必须传入插件实例(用于日志和权限验证)
|
||||
public AntiXray(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
// 监听方块破坏事件:仅处理稀有矿石的挖掘
|
||||
@EventHandler
|
||||
private void onRareOreBreak(BlockBreakEvent e) {
|
||||
Player player = e.getPlayer();
|
||||
Material brokenBlock = e.getBlock().getType();
|
||||
|
||||
// 1. 过滤无效场景(忽略的游戏模式、非稀有矿石)
|
||||
if (IGNORED_GAMEMODES.contains(player.getGameMode().name())
|
||||
|| !RARE_ORES.contains(brokenBlock)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 处理玩家挖掘记录(添加新记录 + 清理过期记录)
|
||||
UUID playerUuid = player.getUniqueId();
|
||||
// 玩家无记录则初始化空列表,有记录则直接获取
|
||||
List<MiningRecord> records = playerMiningRecords.computeIfAbsent(playerUuid, k -> new ArrayList<>());
|
||||
|
||||
// 添加当前挖掘记录(当前时间戳)
|
||||
records.add(new MiningRecord(brokenBlock, System.currentTimeMillis()));
|
||||
|
||||
// 清理过期记录:只保留10分钟内的(超过10分钟的记录对双阈值都无意义)
|
||||
long currentTime = System.currentTimeMillis();
|
||||
records.removeIf(record -> currentTime - record.getTimestamp() > WINDOW_10MIN);
|
||||
|
||||
// 3. 统计两个时间窗口内的稀有矿石数量
|
||||
int count1Min = countOresInWindow(records, WINDOW_1MIN); // 1分钟内数量
|
||||
int count10Min = countOresInWindow(records, WINDOW_10MIN);// 10分钟内数量
|
||||
|
||||
// 4. 双阈值判定:满足任一阈值则发送管理员预警
|
||||
if (count1Min >= THRESHOLD_1MIN || count10Min >= THRESHOLD_10MIN) {
|
||||
sendAdminWarning(player, count1Min, count10Min);
|
||||
// 记录服务器日志(便于后续追溯)
|
||||
logWarning(player, count1Min, count10Min);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计指定时间窗口内的稀有矿石数量
|
||||
*
|
||||
* @param records 玩家的挖掘记录列表
|
||||
* @param windowMs 时间窗口(毫秒)
|
||||
* @return 时间窗口内的稀有矿石总数
|
||||
*/
|
||||
private int countOresInWindow(List<MiningRecord> records, long windowMs) {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
// 过滤出“当前时间 - 时间窗口”之后的记录,统计数量
|
||||
return (int) records.stream()
|
||||
.filter(record -> currentTime - record.getTimestamp() <= windowMs)
|
||||
.count();
|
||||
}
|
||||
|
||||
/**
|
||||
* 向所有在线管理员发送预警信息
|
||||
*
|
||||
* @param player 疑似透视的玩家
|
||||
* @param count1Min 1分钟内挖掘数量
|
||||
* @param count10Min 10分钟内挖掘数量
|
||||
*/
|
||||
private void sendAdminWarning(Player player, int count1Min, int count10Min) {
|
||||
// 构建预警消息(用颜色区分关键信息,便于管理员快速识别)
|
||||
String warningMsg = ChatColor.RED + "[AntiXray预警] " + ChatColor.YELLOW
|
||||
+ "玩家 " + ChatColor.WHITE + player.getName()
|
||||
+ ChatColor.YELLOW + " 疑似透视:"
|
||||
+ ChatColor.RED + "1分钟内:" + ChatColor.WHITE + count1Min
|
||||
+ ChatColor.RED + " 10分钟内:" + ChatColor.WHITE + count10Min;
|
||||
BaseComponent[] message = new ComponentBuilder(warningMsg)
|
||||
.append(new ComponentBuilder(ChatColor.GREEN + "\n[传送到该玩家] ").event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/tp " + player.getName())).create())
|
||||
.append(new ComponentBuilder(ChatColor.RED + "[处理该玩家]").event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/warn " + player.getName() + " X-Ray")).create())
|
||||
.create();
|
||||
// 遍历所有在线玩家,仅向拥有管理员权限的玩家发送消息
|
||||
for (Player onlinePlayer : Bukkit.getOnlinePlayers()) {
|
||||
if (onlinePlayer.hasPermission(ADMIN_PERMISSION)) {
|
||||
onlinePlayer.spigot().sendMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
// 同时向控制台发送预警(确保离线管理员后续能看到日志)
|
||||
Bukkit.getConsoleSender().sendMessage(warningMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录预警日志到服务器日志文件
|
||||
*
|
||||
* @param player 疑似透视的玩家
|
||||
* @param count1Min 1分钟内挖掘数量
|
||||
* @param count10Min 10分钟内挖掘数量
|
||||
*/
|
||||
private void logWarning(Player player, int count1Min, int count10Min) {
|
||||
String logMsg = String.format(
|
||||
"[AntiXray] 疑似透视行为 - 玩家:%s(UUID:%s),1分钟内矿石数:%d(阈值:%d),10分钟内矿石数:%d(阈值:%d),时间:%s",
|
||||
player.getName(),
|
||||
player.getUniqueId(),
|
||||
count1Min,
|
||||
THRESHOLD_1MIN,
|
||||
count10Min,
|
||||
THRESHOLD_10MIN,
|
||||
new Date() // 记录当前时间
|
||||
);
|
||||
plugin.getLogger().warning(logMsg);
|
||||
}
|
||||
|
||||
// ==================== (可选)管理员工具方法 ====================
|
||||
|
||||
/**
|
||||
* 手动清理指定玩家的挖掘记录(用于管理员确认无违规后重置状态)
|
||||
*
|
||||
* @param player 目标玩家
|
||||
*/
|
||||
public void clearPlayerRecord(Player player) {
|
||||
if (player == null) return;
|
||||
playerMiningRecords.remove(player.getUniqueId());
|
||||
plugin.getLogger().info("已清理玩家 " + player.getName() + " 的AntiXray挖掘记录");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前所有玩家的挖掘记录统计(用于管理员调试或核查)
|
||||
*
|
||||
* @return 格式化的统计信息
|
||||
*/
|
||||
public String getMiningStats() {
|
||||
if (playerMiningRecords.isEmpty()) {
|
||||
return "当前无玩家挖掘记录";
|
||||
}
|
||||
|
||||
// 拼接每个玩家的统计信息
|
||||
return playerMiningRecords.entrySet().stream()
|
||||
.map(entry -> {
|
||||
UUID uuid = entry.getKey();
|
||||
List<MiningRecord> records = entry.getValue();
|
||||
Player player = Bukkit.getPlayer(uuid);
|
||||
String playerName = (player != null) ? player.getName() : "离线玩家(" + uuid.toString().substring(0, 8) + ")";
|
||||
|
||||
// 统计该玩家1分钟和10分钟内的矿石数
|
||||
int count1Min = countOresInWindow(records, WINDOW_1MIN);
|
||||
int count10Min = countOresInWindow(records, WINDOW_10MIN);
|
||||
|
||||
return String.format("- %s:1分钟内%d个,10分钟内%d个(总记录数:%d)",
|
||||
playerName, count1Min, count10Min, records.size());
|
||||
})
|
||||
.collect(Collectors.joining("\n"));
|
||||
}
|
||||
}
|
||||
117
src/main/java/org/xgqy/survival/event/ChatEvent.java
Normal file
117
src/main/java/org/xgqy/survival/event/ChatEvent.java
Normal file
@@ -0,0 +1,117 @@
|
||||
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.AsyncPlayerChatEvent;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.bukkit.scoreboard.Objective;
|
||||
import org.bukkit.scoreboard.Scoreboard;
|
||||
import org.xgqy.survival.Survival;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ChatEvent implements Listener {
|
||||
private static final String ADMIN_OBJECTIVE = "administrator";
|
||||
private static final int MAX_MESSAGE_LENGTH = 50;
|
||||
private static final long SHORT_INTERVAL = 1000;
|
||||
private static final long SHORT_INTERVAL_MUTE_TIME = 10000;
|
||||
private static final long SAME_MESSAGE_MUTE_TIME = 15000;
|
||||
private static final long CLEAR_INTERVAL = 30000;
|
||||
|
||||
private Map<Player, Long> lastChatTime = new HashMap<>();
|
||||
private Map<Player, String> lastMessage = new HashMap<>();
|
||||
private Map<Player, Long> muteEndTime = new HashMap<>();
|
||||
|
||||
private Survival plugin;
|
||||
|
||||
public ChatEvent(Survival plugin) {
|
||||
this.plugin = plugin;
|
||||
// 启动定时任务,每1秒检查一次
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
for (Map.Entry<Player, Long> entry : lastChatTime.entrySet()) {
|
||||
Player player = entry.getKey();
|
||||
long lastTime = entry.getValue();
|
||||
if (currentTime - lastTime > CLEAR_INTERVAL) {
|
||||
lastChatTime.remove(player);
|
||||
lastMessage.remove(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
}.runTaskTimer(this.plugin, 0L, 20L); // 20L表示每1秒执行一次
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerChat(AsyncPlayerChatEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
|
||||
// 检查玩家是否处于禁言状态
|
||||
if (muteEndTime.containsKey(player) && System.currentTimeMillis() < muteEndTime.get(player)) {
|
||||
long remainingSeconds = (muteEndTime.get(player) - System.currentTimeMillis()) / 1000;
|
||||
player.sendMessage(
|
||||
ChatColor.RED + "禁言 |" + ChatColor.WHITE + " " +
|
||||
ChatColor.GRAY + "请不要重复发送相同消息 (你需要等待: " +
|
||||
ChatColor.DARK_RED + remainingSeconds + "s" +
|
||||
ChatColor.GRAY + "才能发言)"
|
||||
);
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
Scoreboard scoreboard = player.getScoreboard();
|
||||
boolean isAdmin = false;
|
||||
Objective adminObj = scoreboard.getObjective(ADMIN_OBJECTIVE);
|
||||
if (adminObj != null && adminObj.getScore(player.getName()).getScore() == 1) {
|
||||
isAdmin = true;
|
||||
}
|
||||
|
||||
// 仅非管理员受消息长度限制(管理员忽略此检查)
|
||||
String message = event.getMessage();
|
||||
if (!isAdmin && message.length() > MAX_MESSAGE_LENGTH) {
|
||||
event.setCancelled(true);
|
||||
player.sendMessage(
|
||||
ChatColor.AQUA + "无法发言 |" + ChatColor.WHITE + ": " +
|
||||
ChatColor.RED + "消息过长(" +
|
||||
ChatColor.YELLOW + "非管理员仅可发送 30 字符以内的消息(String.message.length=" + message.length() + ")" +
|
||||
ChatColor.RED + ")"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查发言间隔和发言内容
|
||||
if (!isAdmin) {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
if (lastChatTime.containsKey(player)) {
|
||||
long interval = currentTime - lastChatTime.get(player);
|
||||
if (interval < SHORT_INTERVAL) {
|
||||
muteEndTime.put(player, currentTime + SHORT_INTERVAL_MUTE_TIME);
|
||||
player.sendMessage(
|
||||
ChatColor.RED + "无法发言 |" + ChatColor.WHITE + " " +
|
||||
ChatColor.GRAY + "你的发言太快了,你需要等待一会儿才能继续发言"
|
||||
);
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
if (lastMessage.containsKey(player) && lastMessage.get(player).equals(message)) {
|
||||
muteEndTime.put(player, currentTime + SAME_MESSAGE_MUTE_TIME);
|
||||
player.sendMessage(
|
||||
ChatColor.RED + "无法发言 |" + ChatColor.WHITE + " " +
|
||||
ChatColor.RED + "请不要发送相同的消息,你需要等待一会儿才能继续发言"
|
||||
);
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
lastChatTime.put(player, currentTime);
|
||||
lastMessage.put(player, message);
|
||||
}
|
||||
String formattedMessage = player.getPlayerListName() + ":" + message;
|
||||
event.setCancelled(true);
|
||||
player.getServer().broadcastMessage(formattedMessage);
|
||||
}
|
||||
}
|
||||
238
src/main/java/org/xgqy/survival/event/ChooseTagEvent.java
Normal file
238
src/main/java/org/xgqy/survival/event/ChooseTagEvent.java
Normal file
@@ -0,0 +1,238 @@
|
||||
package org.xgqy.survival.event;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.event.inventory.InventoryDragEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.xgqy.survival.PlayerTags;
|
||||
import org.xgqy.survival.Survival;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class ChooseTagEvent implements Listener {
|
||||
private final Survival plugin;
|
||||
|
||||
public ChooseTagEvent(Survival plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public static int getint(String str) {
|
||||
// 正则表达式:匹配1个或多个连续数字
|
||||
Pattern pattern = Pattern.compile("\\d+");
|
||||
Matcher matcher = pattern.matcher(str);
|
||||
|
||||
// 查找第一个匹配的数字序列
|
||||
if (matcher.find()) {
|
||||
String numberStr = matcher.group(); // 获取匹配的数字字符串
|
||||
return Integer.parseInt(numberStr); // 转换为int
|
||||
} else {
|
||||
// 没有找到数字时抛出异常
|
||||
throw new IllegalArgumentException("字符串中未包含数字: " + str);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onTitleInventoryClick(InventoryClickEvent event) {
|
||||
if (!(event.getWhoClicked() instanceof Player player)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!event.getView().getTitle().contains("称号") && !event.getView().getTitle().contains("商城")) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.setCancelled(true);
|
||||
if (event.getView().getTitle().contains("称号")) {
|
||||
ItemStack clickedItem = event.getCurrentItem();
|
||||
if (clickedItem == null || clickedItem.getType() != Material.NAME_TAG) {
|
||||
return;
|
||||
}
|
||||
|
||||
// -------------------------- 修复:逐步排查边界条件 --------------------------
|
||||
ItemMeta itemMeta = clickedItem.getItemMeta();
|
||||
if (itemMeta == null || !itemMeta.hasLore()) {
|
||||
player.sendMessage(ChatColor.RED + "称号物品数据异常,请联系管理员!");
|
||||
player.closeInventory();
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> lore = itemMeta.getLore();
|
||||
if (lore.isEmpty()) {
|
||||
player.sendMessage(ChatColor.RED + "称号物品缺少必要信息!");
|
||||
player.closeInventory();
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. 剥离颜色+二次trim,确保纯文本匹配
|
||||
String targetTag = lore.getFirst();
|
||||
// 2. 获取玩家有效称号(自动清理过期)
|
||||
List<String> playerTags = plugin.getPlayerTags().getTags(player);
|
||||
|
||||
// 3. 修复:无有效称号提示
|
||||
if (playerTags.isEmpty()) {
|
||||
player.sendMessage(ChatColor.RED + "你暂无有效称号,无法设置!");
|
||||
player.closeInventory();
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. 调试日志:打印关键变量(定位问题用)
|
||||
System.out.println("玩家 " + player.getName() + " 有效称号:" + playerTags);
|
||||
System.out.println("目标称号(纯文本):" + targetTag);
|
||||
|
||||
// 5. 修复:称号未匹配到(索引-1)
|
||||
int targetTagIndex = playerTags.indexOf(targetTag);
|
||||
if (targetTagIndex == -1) {
|
||||
player.sendMessage(ChatColor.RED + "称号匹配失败,请联系管理员!");
|
||||
player.closeInventory();
|
||||
return;
|
||||
}
|
||||
|
||||
// 6. 检查是否已选中当前称号
|
||||
int currentTagIndex = plugin.getPlayerTags().getCurrentTag(player);
|
||||
String currentTag = (currentTagIndex != -1 && currentTagIndex < playerTags.size())
|
||||
? playerTags.get(currentTagIndex)
|
||||
: null;
|
||||
if (targetTag.equals(currentTag)) {
|
||||
player.sendMessage(ChatColor.RED + "设置失败:已当前使用该称号!");
|
||||
player.closeInventory();
|
||||
return;
|
||||
}
|
||||
|
||||
// 7. 最终设置称号
|
||||
boolean setSuccess = plugin.getPlayerTags().setSelectedTag(player, targetTagIndex);
|
||||
if (setSuccess) {
|
||||
player.sendMessage(ChatColor.GREEN + "称号「" + targetTag + ChatColor.GREEN + "」设置成功!");
|
||||
// 优化:颜色代码闭合(避免后续文本变色)
|
||||
player.setDisplayName(ChatColor.RESET + "[" + targetTag + "]" + ChatColor.WHITE + player.getName());
|
||||
player.setPlayerListName(ChatColor.RESET + "[" + targetTag + "]" + ChatColor.WHITE + player.getName());
|
||||
} else {
|
||||
// 修复:明确失败原因
|
||||
player.sendMessage(ChatColor.RED + "称号设置失败!原因:" +
|
||||
(playerTags.isEmpty() ? "无有效称号" : "索引无效"));
|
||||
}
|
||||
} else {
|
||||
ItemStack clickedItem = event.getCurrentItem();
|
||||
if (clickedItem == null || clickedItem.getType() != Material.NAME_TAG) {
|
||||
return;
|
||||
}
|
||||
|
||||
// -------------------------- 修复:逐步排查边界条件 --------------------------
|
||||
ItemMeta itemMeta = clickedItem.getItemMeta();
|
||||
if (itemMeta == null || !itemMeta.hasLore()) {
|
||||
player.sendMessage(ChatColor.RED + "称号物品数据异常,请联系管理员!");
|
||||
player.closeInventory();
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> lore = itemMeta.getLore();
|
||||
if (lore.isEmpty()) {
|
||||
player.sendMessage(ChatColor.RED + "称号物品缺少必要信息!");
|
||||
player.closeInventory();
|
||||
return;
|
||||
}
|
||||
int dq = player.getScoreboard().getObjective("dq").getScore(player).getScore();
|
||||
int coin = player.getScoreboard().getObjective("coin").getScore(player).getScore();
|
||||
int cost = getint(lore.get(1));
|
||||
if (lore.get(1).contains("点券")) {
|
||||
if (dq < cost) {
|
||||
player.sendMessage(ChatColor.RED + "你的点卷不足!(还需: " + (cost - dq) + " 个点卷)");
|
||||
} else {
|
||||
if (clickedItem.getType() == Material.NAME_TAG) {
|
||||
PlayerTags playertags = plugin.getPlayerTags();
|
||||
String gtlore = lore.get(0);
|
||||
if (gtlore.contains("自定义")) {
|
||||
player.sendMessage(ChatColor.GREEN + "请在下方输入自定义的称号(&4 = 红色, &e = 黄色, &7 = 白色, &l = 斜体, &o = 加粗)");
|
||||
player.getScoreboard();
|
||||
}
|
||||
} else if (clickedItem.getType() == Material.IRON_INGOT) {
|
||||
if (player.getScoreboard().getObjective("ifcs").getScore(player).getScore() == 1) {
|
||||
player.getScoreboard().getObjective("isallowed").getScore(player).setScore(1);
|
||||
player.sendMessage(ChatColor.GREEN + "月卡购买成功!");
|
||||
} else {
|
||||
player.sendMessage(ChatColor.RED + "购买失败!(未查询到购买)");
|
||||
}
|
||||
} else if (clickedItem.getType() == Material.GOLD_INGOT) {
|
||||
if (player.getScoreboard().getObjective("gfcs").getScore(player).getScore() == 1) {
|
||||
player.getScoreboard().getObjective("isallowed").getScore(player).setScore(2);
|
||||
player.sendMessage(ChatColor.GREEN + "月卡购买成功!");
|
||||
} else {
|
||||
player.sendMessage(ChatColor.RED + "购买失败!(未查询到购买)");
|
||||
}
|
||||
} else if (clickedItem.getType() == Material.DIAMOND) {
|
||||
} else if (clickedItem.getType() == Material.DIRT) {
|
||||
} else if (clickedItem.getType() == Material.PAPER) {
|
||||
player.getScoreboard().getObjective("noban").getScore(player).setScore(player.getScoreboard().getObjective("noban").getScore(player).getScore() + 1);
|
||||
player.sendMessage(ChatColor.GREEN + "自动解封卡购买成功!");
|
||||
} else if (clickedItem.getType() == Material.RED_STAINED_GLASS_PANE) {
|
||||
player.closeInventory();
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
} else if (lore.get(1).contains("金币")) {
|
||||
if (coin < cost) {
|
||||
player.sendMessage(ChatColor.RED + "你的金币不足!(还需: " + (cost - coin) + " 个金币)");
|
||||
} else {
|
||||
if (clickedItem.getType() == Material.NAME_TAG) {
|
||||
|
||||
} else if (clickedItem.getType() == Material.DIAMOND) {
|
||||
|
||||
} else if (clickedItem.getType() == Material.RED_STAINED_GLASS_PANE) {
|
||||
|
||||
} else if (clickedItem.getType() == Material.BARRIER) {
|
||||
|
||||
} else if (clickedItem.getType() == Material.WOODEN_PICKAXE) {
|
||||
ItemStack ds = new ItemStack(Material.WOODEN_PICKAXE);
|
||||
ds.addEnchantment(Enchantment.EFFICIENCY, 20);
|
||||
player.sendMessage(ChatColor.GREEN + "已发放至背包!");
|
||||
player.getInventory().addItem(ds);
|
||||
coin -= cost;
|
||||
} else if (clickedItem.getType() == Material.IRON_PICKAXE) {
|
||||
ItemStack ds = new ItemStack(Material.IRON_PICKAXE);
|
||||
ds.addEnchantment(Enchantment.EFFICIENCY, 15);
|
||||
player.getInventory().addItem(ds);
|
||||
player.sendMessage(ChatColor.GREEN + "已发放至背包!");
|
||||
coin -= cost;
|
||||
} else if (clickedItem.getType() == Material.RED_STAINED_GLASS_PANE) {
|
||||
player.closeInventory();
|
||||
} else if (clickedItem.getType() == Material.DIAMOND_SWORD) {
|
||||
ItemStack ds = new ItemStack(Material.DIAMOND_SWORD);
|
||||
ds.addEnchantment(Enchantment.SHARPNESS, 10);
|
||||
player.getInventory().addItem(ds);
|
||||
player.sendMessage(ChatColor.GREEN + "已发放至背包!");
|
||||
coin -= cost;
|
||||
} else {
|
||||
player.sendMessage(ChatColor.RED + "购买失败:未知的物品");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
player.sendMessage("未知的货币!");
|
||||
}
|
||||
player.getScoreboard().getObjective("dq").getScore(player).setScore(dq);
|
||||
player.getScoreboard().getObjective("coin").getScore(player).setScore(coin);
|
||||
|
||||
}
|
||||
player.closeInventory();
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onTitleInventoryDrag(InventoryDragEvent event) {
|
||||
if (!(event.getWhoClicked() instanceof Player player)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.getView().getTitle().contains("称号")) {
|
||||
event.setCancelled(true);
|
||||
//player.sendMessage(ChatColor.RED + "称号界面不允许拖拽物品!");
|
||||
player.closeInventory();
|
||||
}
|
||||
}
|
||||
}
|
||||
35
src/main/java/org/xgqy/survival/event/JoinEvent.java
Normal file
35
src/main/java/org/xgqy/survival/event/JoinEvent.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package org.xgqy.survival.event;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.xgqy.survival.PlayerTags;
|
||||
import org.xgqy.survival.Survival;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class JoinEvent implements Listener {
|
||||
private Survival plugin;
|
||||
|
||||
public JoinEvent(Survival plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void join(PlayerJoinEvent e) {
|
||||
PlayerTags playertags = plugin.getPlayerTags();
|
||||
List<String> tags = playertags.getTags(e.getPlayer());
|
||||
if (!tags.isEmpty() && playertags.getCurrentTag(e.getPlayer()) != -1) {
|
||||
e.getPlayer().setDisplayName(ChatColor.WHITE + "[" + tags.get(playertags.getCurrentTag(e.getPlayer())) + ChatColor.WHITE + "]" + e.getPlayer().getName());
|
||||
e.getPlayer().setPlayerListName(ChatColor.WHITE + "[" + tags.get(playertags.getCurrentTag(e.getPlayer())) + ChatColor.WHITE + "]" + e.getPlayer().getName());
|
||||
} else {
|
||||
if (!tags.isEmpty())
|
||||
e.getPlayer().sendMessage(ChatColor.RED + "你还没有选择任何称号!已自动设置为第一个称号。可输入 /tag 进行切换");
|
||||
}
|
||||
e.setJoinMessage(e.getPlayer().getPlayerListName() + " 加入了 生存1区");
|
||||
e.getPlayer().sendMessage(ChatColor.YELLOW + "欢迎来到 星阁钱语 生存服!");
|
||||
e.getPlayer().sendMessage(ChatColor.YELLOW + "你可以输入 /help 来查看帮助");
|
||||
e.getPlayer().sendTitle(ChatColor.YELLOW + "欢迎来到 -生存1区-", ChatColor.AQUA + "星阁钱语", 20, 80, 20);
|
||||
}
|
||||
}
|
||||
51
src/main/resources/plugin.yml
Normal file
51
src/main/resources/plugin.yml
Normal file
@@ -0,0 +1,51 @@
|
||||
name: survival
|
||||
version: '1.0'
|
||||
main: org.xgqy.survival.Survival
|
||||
api-version: '1.21'
|
||||
authors: [ Chen yuchen_1 ]
|
||||
description: A plugin to make bedwars in spigot
|
||||
commands:
|
||||
pvp:
|
||||
description: to open/close the PVP
|
||||
usage: /<command>
|
||||
settag:
|
||||
description: to set player's tags
|
||||
usage: /<command> <Player:player> <add|remove> <Tag:tag> <Color:color>
|
||||
permission: permission.settag
|
||||
permission-message: 你没有权限执行此命令!
|
||||
report:
|
||||
description: Report players
|
||||
usage: /<command> <Player:player> <String:Reason>
|
||||
handle:
|
||||
description: handle players reported
|
||||
usage: /<command> <Player:player>
|
||||
permission: permission.handle
|
||||
permission-message: 无法执行该命令,请确认拥有管理员权限
|
||||
tag:
|
||||
description: to set tag
|
||||
usage: /<command>
|
||||
hub:
|
||||
description: get to the main lobby
|
||||
usage: /<command>
|
||||
teleport:
|
||||
description: teleport to a player
|
||||
usage: /<command> <Player:player>
|
||||
tpacc:
|
||||
description: teleport to a player
|
||||
usage: /<command> <Player:player>
|
||||
tpfin:
|
||||
description: teleport to a player
|
||||
usage: /<command> <Player:player>
|
||||
help:
|
||||
description: see the help
|
||||
usage: /<command>
|
||||
shop:
|
||||
description: to open the shop
|
||||
usage: /<command>
|
||||
permissions:
|
||||
permission.settag:
|
||||
description: Allows setting player tags
|
||||
default: op
|
||||
permission.handle:
|
||||
description: handle players reported
|
||||
default: op
|
||||
Reference in New Issue
Block a user