1
This commit is contained in:
38
.gitignore
vendored
Normal file
38
.gitignore
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/modules.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/compiler.xml
|
||||
.idea/libraries/
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### Eclipse ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
||||
3
.idea/.gitignore
generated
vendored
Normal file
3
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
7
.idea/encodings.xml
generated
Normal file
7
.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding">
|
||||
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
|
||||
</component>
|
||||
</project>
|
||||
14
.idea/misc.xml
generated
Normal file
14
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="MavenProjectsManager">
|
||||
<option name="originalFiles">
|
||||
<list>
|
||||
<option value="$PROJECT_DIR$/pom.xml" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="azul-1.8" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
BIN
libs/ProtocolLib.jar
Normal file
BIN
libs/ProtocolLib.jar
Normal file
Binary file not shown.
BIN
libs/spigot-1.8.8-R0.1-SNAPSHOT-latest.jar
Normal file
BIN
libs/spigot-1.8.8-R0.1-SNAPSHOT-latest.jar
Normal file
Binary file not shown.
45
pom.xml
Normal file
45
pom.xml
Normal file
@@ -0,0 +1,45 @@
|
||||
<?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>me.dw1e.kbm</groupId>
|
||||
<artifactId>KnockBackManager</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.github.spigot</groupId>
|
||||
<artifactId>1.8.8</artifactId>
|
||||
<version>1.8.8</version>
|
||||
<scope>system</scope>
|
||||
<systemPath>${basedir}/libs/spigot-1.8.8-R0.1-SNAPSHOT-latest.jar</systemPath>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.comphenix.protocol</groupId>
|
||||
<artifactId>ProtocolLib</artifactId>
|
||||
<version>5.1.0</version>
|
||||
<scope>system</scope>
|
||||
<systemPath>${basedir}/libs/ProtocolLib.jar</systemPath>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<configuration>
|
||||
<source>8</source>
|
||||
<target>8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
101
src/main/java/me/dw1e/kbm/KnockbackManager.java
Normal file
101
src/main/java/me/dw1e/kbm/KnockbackManager.java
Normal file
@@ -0,0 +1,101 @@
|
||||
package me.dw1e.kbm;
|
||||
|
||||
import me.dw1e.kbm.api.KnockbackManagerAPI;
|
||||
import me.dw1e.kbm.commands.KBMCommandHandler;
|
||||
import me.dw1e.kbm.config.ConfigManager;
|
||||
import me.dw1e.kbm.data.PlayerDataManager;
|
||||
import me.dw1e.kbm.listeners.PacketListener;
|
||||
import me.dw1e.kbm.listeners.PlayerEventListener;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
public final class KnockbackManager extends JavaPlugin {
|
||||
private static KnockbackManager instance;
|
||||
private ConfigManager configManager;
|
||||
private PlayerDataManager playerDataManager;
|
||||
private KnockbackManagerAPI api;
|
||||
private PacketListener packetListener;
|
||||
private int serverTick;
|
||||
|
||||
private final String pluginPrefix = ChatColor.DARK_GRAY + "[" + ChatColor.YELLOW + "KBM" + ChatColor.DARK_GRAY + "] ";
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
instance = this;
|
||||
|
||||
if (!getDescription().getName().equals("KnockbackManager") ||
|
||||
!getDescription().getAuthors().contains("dw1e")) {
|
||||
getLogger().severe("检测到插件信息篡改,已停止运行!");
|
||||
Bukkit.getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
}
|
||||
|
||||
this.configManager = new ConfigManager(this);
|
||||
this.playerDataManager = new PlayerDataManager();
|
||||
this.api = new KnockbackManagerAPIImpl(this);
|
||||
|
||||
this.configManager.loadConfigs(Bukkit.getConsoleSender());
|
||||
|
||||
this.playerDataManager.registerAllPlayers();
|
||||
|
||||
getCommand("kbm").setExecutor(new KBMCommandHandler(this));
|
||||
|
||||
Bukkit.getPluginManager().registerEvents(new PlayerEventListener(this), this);
|
||||
|
||||
Bukkit.getScheduler().runTaskTimer(this, () -> serverTick++, 0L, 1L);
|
||||
|
||||
setupProtocolLib();
|
||||
|
||||
getLogger().info("Knockback Manager 已启用!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
if (packetListener != null) {
|
||||
packetListener.unregister();
|
||||
}
|
||||
|
||||
if (playerDataManager != null) {
|
||||
playerDataManager.clearAllData();
|
||||
}
|
||||
|
||||
instance = null;
|
||||
getLogger().info("Knockback Manager 已禁用!");
|
||||
}
|
||||
|
||||
private void setupProtocolLib() {
|
||||
org.bukkit.plugin.Plugin protocolLib = Bukkit.getPluginManager().getPlugin("ProtocolLib");
|
||||
if (protocolLib != null && protocolLib.isEnabled()) {
|
||||
String version = protocolLib.getDescription().getVersion();
|
||||
try {
|
||||
int minorVersion = Integer.parseInt(version.split("\\.")[1]);
|
||||
if (minorVersion < 5) {
|
||||
logMessage(ChatColor.RED + "不支持的 ProtocolLib 版本: " + ChatColor.YELLOW + version +
|
||||
ChatColor.RED + ", 请使用 5.0.0 或之后的版本, Misplace 模块将不会启用!");
|
||||
} else {
|
||||
logMessage(ChatColor.GREEN + "检测到 ProtocolLib " + ChatColor.YELLOW + version +
|
||||
ChatColor.GREEN + ", 已启用 Misplace 模块!");
|
||||
this.packetListener = new PacketListener(this);
|
||||
this.packetListener.register();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logMessage(ChatColor.RED + "解析 ProtocolLib 版本时出错, Misplace 模块将不会启用!");
|
||||
}
|
||||
} else {
|
||||
logMessage(ChatColor.RED + "未检测到 ProtocolLib 安装/运行, Misplace 模块将不会启用!");
|
||||
}
|
||||
}
|
||||
|
||||
public void logMessage(String message) {
|
||||
Bukkit.getConsoleSender().sendMessage(pluginPrefix + message);
|
||||
}
|
||||
|
||||
// Getters
|
||||
public static KnockbackManager getInstance() { return instance; }
|
||||
public ConfigManager getConfigManager() { return configManager; }
|
||||
public PlayerDataManager getPlayerDataManager() { return playerDataManager; }
|
||||
public KnockbackManagerAPI getAPI() { return api; }
|
||||
public int getServerTick() { return serverTick; }
|
||||
public String getPluginPrefix() { return pluginPrefix; }
|
||||
}
|
||||
68
src/main/java/me/dw1e/kbm/KnockbackManagerAPIImpl.java
Normal file
68
src/main/java/me/dw1e/kbm/KnockbackManagerAPIImpl.java
Normal file
@@ -0,0 +1,68 @@
|
||||
package me.dw1e.kbm;
|
||||
|
||||
import me.dw1e.kbm.api.KnockbackManagerAPI;
|
||||
import me.dw1e.kbm.config.ConfigHolder;
|
||||
import me.dw1e.kbm.data.PlayerKnockbackData;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class KnockbackManagerAPIImpl implements KnockbackManagerAPI {
|
||||
private final KnockbackManager plugin;
|
||||
|
||||
public KnockbackManagerAPIImpl(KnockbackManager plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMisplacing(Player player) {
|
||||
PlayerKnockbackData data = getPlayerData(player);
|
||||
FileConfiguration config = getKBConfig(data.getCurrentKBFile());
|
||||
|
||||
return config != null &&
|
||||
plugin.getServerTick() - data.getLastHitTick() < config.getInt("misplace.delay");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFilter(Player player) {
|
||||
return getPlayerData(player).isFiltered();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileConfiguration getKBConfig(String configName) {
|
||||
if (!isKBFileExist(configName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ConfigHolder holder = plugin.getConfigManager().getConfigs().get(configName);
|
||||
return (FileConfiguration) holder.getConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKBFile(Player player) {
|
||||
return getPlayerData(player).getCurrentKBFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setKBFile(Player player, String configName) {
|
||||
if (!isKBFileExist(configName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
getPlayerData(player).setCurrentKBFile(configName);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isKBFileExist(String configName) {
|
||||
return plugin.getConfigManager().getConfigs().containsKey(configName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFilter(Player player, boolean filtered) {
|
||||
getPlayerData(player).setFiltered(filtered);
|
||||
}
|
||||
|
||||
private PlayerKnockbackData getPlayerData(Player player) {
|
||||
return plugin.getPlayerDataManager().getPlayerData(player.getUniqueId());
|
||||
}
|
||||
}
|
||||
46
src/main/java/me/dw1e/kbm/api/KBMPlayerVelocityEvent.java
Normal file
46
src/main/java/me/dw1e/kbm/api/KBMPlayerVelocityEvent.java
Normal file
@@ -0,0 +1,46 @@
|
||||
package me.dw1e.kbm.api;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.player.PlayerEvent;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class KBMPlayerVelocityEvent extends PlayerEvent implements Cancellable {
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
private Vector velocity;
|
||||
private boolean cancelled;
|
||||
|
||||
public KBMPlayerVelocityEvent(Player player, Vector velocity) {
|
||||
super(player);
|
||||
this.velocity = velocity;
|
||||
this.cancelled = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return this.cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(boolean cancelled) {
|
||||
this.cancelled = cancelled;
|
||||
}
|
||||
|
||||
public Vector getVelocity() {
|
||||
return this.velocity;
|
||||
}
|
||||
|
||||
public void setVelocity(Vector velocity) {
|
||||
this.velocity = velocity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return handlers;
|
||||
}
|
||||
}
|
||||
14
src/main/java/me/dw1e/kbm/api/KnockbackManagerAPI.java
Normal file
14
src/main/java/me/dw1e/kbm/api/KnockbackManagerAPI.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package me.dw1e.kbm.api;
|
||||
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public interface KnockbackManagerAPI {
|
||||
boolean isMisplacing(Player player);
|
||||
boolean isFilter(Player player);
|
||||
FileConfiguration getKBConfig(String configName);
|
||||
String getKBFile(Player player);
|
||||
boolean setKBFile(Player player, String configName);
|
||||
boolean isKBFileExist(String configName);
|
||||
void setFilter(Player player, boolean filtered);
|
||||
}
|
||||
491
src/main/java/me/dw1e/kbm/commands/KBMCommandHandler.java
Normal file
491
src/main/java/me/dw1e/kbm/commands/KBMCommandHandler.java
Normal file
@@ -0,0 +1,491 @@
|
||||
package me.dw1e.kbm.commands;
|
||||
|
||||
import me.dw1e.kbm.KnockbackManager;
|
||||
import me.dw1e.kbm.config.ConfigHolder;
|
||||
import me.dw1e.kbm.data.PlayerKnockbackData;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabExecutor;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class KBMCommandHandler implements TabExecutor {
|
||||
private final KnockbackManager plugin;
|
||||
private final List<String> editableConfigPaths;
|
||||
|
||||
public KBMCommandHandler(KnockbackManager plugin) {
|
||||
this.plugin = plugin;
|
||||
this.editableConfigPaths = Arrays.asList(
|
||||
"horizontal.ground", "horizontal.air", "horizontal.sprint_extra",
|
||||
"horizontal.projectile_multiplier", "vertical.ground", "vertical.air",
|
||||
"vertical.sprint_extra", "vertical.projectile_multiplier",
|
||||
"misplace.enabled", "misplace.delay", "stop_sprint", "y_limit", "hit_delay"
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if (!sender.hasPermission("kbm.use")) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED + "你没有此命令的使用权限!");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length == 0) {
|
||||
sendHelp(sender, label);
|
||||
return true;
|
||||
}
|
||||
|
||||
String subCommand = args[0].toLowerCase();
|
||||
|
||||
switch (subCommand) {
|
||||
case "help":
|
||||
sendHelp(sender, label);
|
||||
break;
|
||||
case "create":
|
||||
handleCreate(sender, args, label);
|
||||
break;
|
||||
case "delete":
|
||||
handleDelete(sender, args, label);
|
||||
break;
|
||||
case "list":
|
||||
handleList(sender);
|
||||
break;
|
||||
case "edit":
|
||||
handleEdit(sender, args, label);
|
||||
break;
|
||||
case "view":
|
||||
handleView(sender, args, label);
|
||||
break;
|
||||
case "reload":
|
||||
handleReload(sender, args, label);
|
||||
break;
|
||||
case "getkb":
|
||||
handleGetKB(sender, args, label);
|
||||
break;
|
||||
case "setkb":
|
||||
handleSetKB(sender, args, label);
|
||||
break;
|
||||
case "filter":
|
||||
handleFilter(sender, args, label);
|
||||
break;
|
||||
default:
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED + "未知子命令: " + args[0]);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void sendHelp(CommandSender sender, String label) {
|
||||
String commandBase = ChatColor.WHITE + " /" + label;
|
||||
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.GRAY + "可用命令:");
|
||||
sender.sendMessage(commandBase + " create" + ChatColor.GRAY + ": 创建KB文件");
|
||||
sender.sendMessage(commandBase + " delete" + ChatColor.GRAY + ": 删除KB文件");
|
||||
sender.sendMessage(commandBase + " list" + ChatColor.GRAY + ": 查看已读取的KB文件");
|
||||
sender.sendMessage(commandBase + " edit" + ChatColor.GRAY + ": 编辑KB文件的数值");
|
||||
sender.sendMessage(commandBase + " view" + ChatColor.GRAY + ": 查看KB文件的数值");
|
||||
sender.sendMessage(commandBase + " reload" + ChatColor.GRAY + ": 重新加载KB文件");
|
||||
sender.sendMessage(commandBase + " getkb" + ChatColor.GRAY + ": 查看玩家使用的KB文件");
|
||||
sender.sendMessage(commandBase + " setkb" + ChatColor.GRAY + ": 设置玩家使用的KB文件");
|
||||
sender.sendMessage(commandBase + " filter" + ChatColor.GRAY + ": 过滤玩家");
|
||||
sender.sendMessage(ChatColor.DARK_GREEN + "使用教程&参考配置: 请见群(673765463)文件");
|
||||
}
|
||||
|
||||
private void handleCreate(CommandSender sender, String[] args, String label) {
|
||||
if (args.length != 2) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED +
|
||||
"用法: /" + label + " create <KB文件名>");
|
||||
return;
|
||||
}
|
||||
plugin.getConfigManager().createConfig(args[1], sender);
|
||||
}
|
||||
|
||||
private void handleDelete(CommandSender sender, String[] args, String label) {
|
||||
if (args.length != 2) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED +
|
||||
"用法: /" + label + " delete <KB文件名>");
|
||||
return;
|
||||
}
|
||||
|
||||
String configName = args[1];
|
||||
if (configName.equals("default")) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED + "默认KB文件不允许删除!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!plugin.getConfigManager().getConfigs().containsKey(configName)) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED + "KB文件 " + configName + " 不存在!");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
plugin.getPlayerDataManager().getAllData().values().stream()
|
||||
.filter(data -> data.getCurrentKBFile().equals(configName))
|
||||
.forEach(data -> data.setCurrentKBFile("default"));
|
||||
|
||||
|
||||
plugin.getConfigManager().getConfigs().remove(configName);
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.GREEN + "删除 " + configName + " 成功!");
|
||||
}
|
||||
|
||||
private void handleList(CommandSender sender) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.GRAY + "已读取的KB文件:");
|
||||
plugin.getConfigManager().getConfigs().keySet().forEach(configName ->
|
||||
sender.sendMessage(" " + ChatColor.WHITE + configName)
|
||||
);
|
||||
}
|
||||
|
||||
private void handleEdit(CommandSender sender, String[] args, String label) {
|
||||
if (args.length != 4) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED +
|
||||
"用法: /" + label + " edit <KB文件名> <配置路径> <数值>");
|
||||
return;
|
||||
}
|
||||
|
||||
String configName = args[1];
|
||||
String configPath = args[2].toLowerCase();
|
||||
String value = args[3];
|
||||
|
||||
if (!plugin.getConfigManager().getConfigs().containsKey(configName)) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED + "KB文件 " + configName + " 不存在!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!editableConfigPaths.contains(configPath)) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED + "无效的配置路径: " + configPath);
|
||||
return;
|
||||
}
|
||||
|
||||
ConfigHolder holder = plugin.getConfigManager().getConfigs().get(configName);
|
||||
FileConfiguration config = (FileConfiguration) holder.getConfig();
|
||||
|
||||
try {
|
||||
Object oldValue = config.get(configPath);
|
||||
Object newValue = parseValue(configPath, value);
|
||||
|
||||
if (oldValue != null && oldValue.equals(newValue)) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED +
|
||||
"无变化. " + configName + " 中的 " + configPath + " 原已设置为 " + oldValue);
|
||||
return;
|
||||
}
|
||||
|
||||
config.set(configPath, newValue);
|
||||
config.save(holder.getFile());
|
||||
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.GRAY +
|
||||
"已将 " + ChatColor.WHITE + configName + ChatColor.GRAY + " 中的 " +
|
||||
ChatColor.WHITE + configPath + ChatColor.GRAY + " 设置为 " +
|
||||
ChatColor.WHITE + newValue);
|
||||
|
||||
} catch (Exception e) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED +
|
||||
"设置配置值时发生错误: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleView(CommandSender sender, String[] args, String label) {
|
||||
if (args.length != 2) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED +
|
||||
"用法: /" + label + " view <KB文件名>");
|
||||
return;
|
||||
}
|
||||
|
||||
String configName = args[1];
|
||||
if (!plugin.getConfigManager().getConfigs().containsKey(configName)) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED + "KB文件 " + configName + " 不存在!");
|
||||
return;
|
||||
}
|
||||
|
||||
ConfigHolder holder = plugin.getConfigManager().getConfigs().get(configName);
|
||||
FileConfiguration config = (FileConfiguration) holder.getConfig();
|
||||
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.GRAY +
|
||||
"查看KB文件 " + ChatColor.WHITE + configName + ChatColor.GRAY + " 的数值:");
|
||||
|
||||
StringBuilder configDisplay = new StringBuilder();
|
||||
formatConfigSection(configDisplay, config, 1);
|
||||
|
||||
String[] lines = configDisplay.toString().split("\n");
|
||||
for (String line : lines) {
|
||||
sender.sendMessage(ChatColor.YELLOW + line);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleReload(CommandSender sender, String[] args, String label) {
|
||||
if (args.length != 2) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED +
|
||||
"用法: /" + label + " reload <KB文件名|*>");
|
||||
return;
|
||||
}
|
||||
|
||||
String configName = args[1];
|
||||
if (configName.equals("*")) {
|
||||
plugin.getConfigManager().loadConfigs(sender);
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.GREEN + "已重载所有KB文件!");
|
||||
} else {
|
||||
plugin.getConfigManager().reloadConfig(configName, sender);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleGetKB(CommandSender sender, String[] args, String label) {
|
||||
if (args.length != 2) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED +
|
||||
"用法: /" + label + " getkb <玩家>");
|
||||
return;
|
||||
}
|
||||
|
||||
Player player = Bukkit.getPlayerExact(args[1]);
|
||||
if (player == null) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED +
|
||||
"没有找到名为 " + args[1] + " 的玩家!");
|
||||
return;
|
||||
}
|
||||
|
||||
PlayerKnockbackData data = plugin.getPlayerDataManager().getPlayerData(player.getUniqueId());
|
||||
if (data == null) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED +
|
||||
"无法获取玩家 " + player.getName() + " 的数据!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.isFiltered()) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED +
|
||||
player.getName() + " 当前位于过滤名单中!");
|
||||
} else {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.WHITE +
|
||||
player.getName() + ChatColor.GRAY + " 当前使用的KB为: " +
|
||||
ChatColor.WHITE + data.getCurrentKBFile());
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSetKB(CommandSender sender, String[] args, String label) {
|
||||
if (args.length != 3) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED +
|
||||
"用法: /" + label + " setkb <玩家|*> <KB文件名>");
|
||||
return;
|
||||
}
|
||||
|
||||
String playerName = args[1];
|
||||
String configName = args[2];
|
||||
|
||||
if (!plugin.getConfigManager().getConfigs().containsKey(configName)) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED + "KB文件 " + configName + " 不存在!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (playerName.equals("*")) {
|
||||
// 设置所有玩家
|
||||
plugin.getPlayerDataManager().getAllData().values().forEach(data ->
|
||||
data.setCurrentKBFile(configName)
|
||||
);
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.GRAY +
|
||||
"已将所有玩家使用的KB设置为 " + ChatColor.WHITE + configName);
|
||||
} else {
|
||||
// 设置单个玩家
|
||||
Player player = Bukkit.getPlayerExact(playerName);
|
||||
if (player == null) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED +
|
||||
"没有找到名为 " + playerName + " 的玩家!");
|
||||
return;
|
||||
}
|
||||
|
||||
PlayerKnockbackData data = plugin.getPlayerDataManager().getPlayerData(player.getUniqueId());
|
||||
if (data == null) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED +
|
||||
"无法获取玩家 " + player.getName() + " 的数据!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.getCurrentKBFile().equals(configName)) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED +
|
||||
"无变化. " + player.getName() + " 原已使用 " + configName);
|
||||
return;
|
||||
}
|
||||
|
||||
data.setCurrentKBFile(configName);
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.GRAY +
|
||||
"已将 " + ChatColor.WHITE + player.getName() + ChatColor.GRAY +
|
||||
" 使用的KB设置为 " + ChatColor.WHITE + configName);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleFilter(CommandSender sender, String[] args, String label) {
|
||||
if (args.length != 3) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED +
|
||||
"用法: /" + label + " filter <玩家> <true/false>");
|
||||
return;
|
||||
}
|
||||
|
||||
Player player = Bukkit.getPlayerExact(args[1]);
|
||||
if (player == null) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED +
|
||||
"没有找到名为 " + args[1] + " 的玩家!");
|
||||
return;
|
||||
}
|
||||
|
||||
String filterValue = args[2].toLowerCase();
|
||||
if (!filterValue.equals("true") && !filterValue.equals("false")) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED +
|
||||
"无效的布尔值: " + args[2]);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean filtered = Boolean.parseBoolean(filterValue);
|
||||
PlayerKnockbackData data = plugin.getPlayerDataManager().getPlayerData(player.getUniqueId());
|
||||
if (data == null) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED +
|
||||
"无法获取玩家 " + player.getName() + " 的数据!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.isFiltered() == filtered) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED +
|
||||
"无变化. " + player.getName() + " 原已" + (filtered ? "加入" : "移出") + "过滤名单!");
|
||||
return;
|
||||
}
|
||||
|
||||
data.setFiltered(filtered);
|
||||
String action = filtered ? ChatColor.GREEN + "加入" : ChatColor.RED + "移出";
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.GRAY +
|
||||
"已将 " + ChatColor.WHITE + player.getName() + " " + action +
|
||||
ChatColor.GRAY + "过滤名单!");
|
||||
}
|
||||
|
||||
|
||||
private Object parseValue(String configPath, String value) {
|
||||
// 处理布尔值
|
||||
if (configPath.equals("misplace.enabled") || configPath.equals("stop_sprint")) {
|
||||
if (value.equalsIgnoreCase("true") || value.equals("1")) {
|
||||
return true;
|
||||
} else if (value.equalsIgnoreCase("false") || value.equals("0")) {
|
||||
return false;
|
||||
}
|
||||
throw new IllegalArgumentException("无效的布尔值: " + value);
|
||||
}
|
||||
|
||||
|
||||
if (configPath.equals("misplace.delay") || configPath.equals("hit_delay")) {
|
||||
try {
|
||||
int intValue = Integer.parseInt(value);
|
||||
if (configPath.equals("misplace.delay") && (intValue < 1 || intValue > 5)) {
|
||||
throw new IllegalArgumentException("misplace.delay 必须在 1-5 范围内");
|
||||
}
|
||||
return intValue;
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("无效的整数值: " + value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
return Double.parseDouble(value);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("无效的数值: " + value);
|
||||
}
|
||||
}
|
||||
|
||||
private void formatConfigSection(StringBuilder builder, ConfigurationSection section, int depth) {
|
||||
StringBuilder indent = new StringBuilder();
|
||||
for (int i = 0; i < depth; i++) {
|
||||
indent.append(" ");
|
||||
}
|
||||
|
||||
for (String key : section.getKeys(false)) {
|
||||
if (section.isConfigurationSection(key)) {
|
||||
builder.append(ChatColor.YELLOW).append(indent).append(key)
|
||||
.append(ChatColor.WHITE).append(":\n");
|
||||
formatConfigSection(builder, section.getConfigurationSection(key), depth + 1);
|
||||
} else {
|
||||
Object value = section.get(key);
|
||||
builder.append(ChatColor.GRAY).append(indent).append(key)
|
||||
.append(ChatColor.WHITE).append(": ").append(value).append("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
|
||||
List<String> completions = new ArrayList<>();
|
||||
if (!sender.hasPermission("kbm.use")) return completions;
|
||||
|
||||
if (args.length == 1) {
|
||||
return filterCompletions(Arrays.asList(
|
||||
"create", "delete", "list", "edit", "view", "reload", "getkb", "setkb", "filter", "help"
|
||||
), args[0]);
|
||||
}
|
||||
|
||||
List<String> configNames = new ArrayList<>(plugin.getConfigManager().getConfigs().keySet());
|
||||
List<String> playerNames = Bukkit.getOnlinePlayers().stream()
|
||||
.map(Player::getName)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
String subCommand = args[0].toLowerCase();
|
||||
|
||||
switch (subCommand) {
|
||||
case "delete":
|
||||
case "edit":
|
||||
case "view":
|
||||
case "reload":
|
||||
if (args.length == 2) {
|
||||
return filterCompletions(configNames, args[1]);
|
||||
}
|
||||
if (args.length == 3 && subCommand.equals("edit")) {
|
||||
return filterCompletions(editableConfigPaths, args[2]);
|
||||
}
|
||||
if (args.length == 4 && subCommand.equals("edit")) {
|
||||
|
||||
String configPath = args[2].toLowerCase();
|
||||
if (configPath.equals("misplace.enabled") || configPath.equals("stop_sprint")) {
|
||||
return filterCompletions(Arrays.asList("true", "false"), args[3]);
|
||||
} else if (configPath.equals("misplace.delay")) {
|
||||
return filterCompletions(Arrays.asList("1", "2", "3", "4", "5"), args[3]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "getkb":
|
||||
case "setkb":
|
||||
case "filter":
|
||||
if (args.length == 2) {
|
||||
List<String> allOptions = new ArrayList<>(playerNames);
|
||||
allOptions.add("*");
|
||||
return filterCompletions(allOptions, args[1]);
|
||||
}
|
||||
if (args.length == 3) {
|
||||
if (subCommand.equals("filter")) {
|
||||
return filterCompletions(Arrays.asList("true", "false"), args[2]);
|
||||
} else if (subCommand.equals("setkb")) {
|
||||
return filterCompletions(configNames, args[2]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "create":
|
||||
if (args.length == 2) {
|
||||
|
||||
return filterCompletions(Arrays.asList("custom", "new", "test"), args[1]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return completions;
|
||||
}
|
||||
|
||||
private List<String> filterCompletions(List<String> options, String input) {
|
||||
return options.stream()
|
||||
.filter(option -> option.toLowerCase().startsWith(input.toLowerCase()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
21
src/main/java/me/dw1e/kbm/config/ConfigHolder.java
Normal file
21
src/main/java/me/dw1e/kbm/config/ConfigHolder.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package me.dw1e.kbm.config;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class ConfigHolder {
|
||||
private final File configFile;
|
||||
private final Object config;
|
||||
|
||||
public ConfigHolder(File configFile, Object config) {
|
||||
this.configFile = configFile;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return this.configFile;
|
||||
}
|
||||
|
||||
public Object getConfig() {
|
||||
return this.config;
|
||||
}
|
||||
}
|
||||
242
src/main/java/me/dw1e/kbm/config/ConfigManager.java
Normal file
242
src/main/java/me/dw1e/kbm/config/ConfigManager.java
Normal file
@@ -0,0 +1,242 @@
|
||||
package me.dw1e.kbm.config;
|
||||
|
||||
import me.dw1e.kbm.KnockbackManager;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ConfigManager {
|
||||
private final KnockbackManager plugin;
|
||||
private final Map<String, ConfigHolder> configs;
|
||||
private final FileConfiguration defaultConfig;
|
||||
|
||||
public ConfigManager(KnockbackManager plugin) {
|
||||
this.plugin = plugin;
|
||||
this.configs = new HashMap<>();
|
||||
|
||||
this.defaultConfig = YamlConfiguration.loadConfiguration(
|
||||
new InputStreamReader(plugin.getResource("default.yml"))
|
||||
);
|
||||
}
|
||||
|
||||
public void loadConfigs(CommandSender sender) {
|
||||
if (!plugin.getDataFolder().exists()) {
|
||||
plugin.getDataFolder().mkdirs();
|
||||
}
|
||||
|
||||
File defaultFile = new File(plugin.getDataFolder(), "default.yml");
|
||||
if (!defaultFile.exists()) {
|
||||
plugin.saveResource("default.yml", false);
|
||||
}
|
||||
|
||||
configs.clear();
|
||||
|
||||
try {
|
||||
Files.list(Paths.get(plugin.getDataFolder().getPath()))
|
||||
.filter(path -> Files.isRegularFile(path) && path.toString().endsWith(".yml"))
|
||||
.forEach(path -> {
|
||||
String fileName = path.getFileName().toString().replace(".yml", "");
|
||||
File file = path.toFile();
|
||||
FileConfiguration config = YamlConfiguration.loadConfiguration(file);
|
||||
|
||||
configs.put(fileName, new ConfigHolder(file, config));
|
||||
updateConfigValues(fileName, config, sender);
|
||||
|
||||
try {
|
||||
config.save(file);
|
||||
} catch (IOException e) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED +
|
||||
"保存 " + fileName + " 失败!");
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED +
|
||||
"读取配置文件时发生错误!");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateConfigValues(String configName, FileConfiguration config, CommandSender sender) {
|
||||
if (config.get("ground_horizontal") != null) {
|
||||
plugin.logMessage(ChatColor.GREEN + "已将旧版配置文件 " + configName + " 更新!");
|
||||
|
||||
migrateOldConfig(config);
|
||||
}
|
||||
|
||||
config.options().copyDefaults(true).copyHeader(true);
|
||||
config.addDefaults(defaultConfig);
|
||||
|
||||
config.set("misplace.delay", Math.max(1, Math.min(5, config.getInt("misplace.delay"))));
|
||||
}
|
||||
|
||||
private void migrateOldConfig(FileConfiguration config) {
|
||||
|
||||
config.set("horizontal.ground", config.get("ground_horizontal"));
|
||||
config.set("horizontal.air", config.get("air_horizontal"));
|
||||
config.set("horizontal.sprint_extra", config.get("extra_horizontal"));
|
||||
config.set("horizontal.projectile_multiplier", config.get("projectile_multiplier_horizontal"));
|
||||
|
||||
|
||||
config.set("vertical.ground", config.get("ground_vertical"));
|
||||
config.set("vertical.air", config.get("air_vertical"));
|
||||
config.set("vertical.sprint_extra", config.get("extra_vertical"));
|
||||
config.set("vertical.projectile_multiplier", config.get("projectile_multiplier_vertical"));
|
||||
|
||||
|
||||
String[] oldKeys = {
|
||||
"ground_horizontal", "air_horizontal", "extra_horizontal", "projectile_multiplier_horizontal",
|
||||
"ground_vertical", "air_vertical", "extra_vertical", "projectile_multiplier_vertical"
|
||||
};
|
||||
|
||||
for (String key : oldKeys) {
|
||||
config.set(key, null);
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, ConfigHolder> getConfigs() {
|
||||
return configs;
|
||||
}
|
||||
|
||||
public void createConfig(String name, CommandSender sender) {
|
||||
if (configs.containsKey(name)) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED +
|
||||
"配置文件 " + name + " 已存在!");
|
||||
return;
|
||||
}
|
||||
|
||||
File file = new File(plugin.getDataFolder(), name + ".yml");
|
||||
try {
|
||||
if (!file.createNewFile()) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED +
|
||||
"创建配置文件 " + name + " 失败!");
|
||||
return;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED +
|
||||
"创建配置文件 " + name + " 失败!");
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
FileConfiguration config = YamlConfiguration.loadConfiguration(file);
|
||||
config.options().copyDefaults(true).copyHeader(true);
|
||||
config.addDefaults(defaultConfig);
|
||||
|
||||
try {
|
||||
config.save(file);
|
||||
} catch (IOException e) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED +
|
||||
"保存配置文件 " + name + " 失败!");
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
configs.put(name, new ConfigHolder(file, config));
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.GREEN +
|
||||
"创建配置文件 " + name + " 成功!");
|
||||
}
|
||||
|
||||
public void reloadConfig(String name, CommandSender sender) {
|
||||
if (!configs.containsKey(name)) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED +
|
||||
"配置文件 " + name + " 不存在!");
|
||||
return;
|
||||
}
|
||||
|
||||
ConfigHolder holder = configs.get(name);
|
||||
try {
|
||||
((FileConfiguration) holder.getConfig()).load(holder.getFile());
|
||||
updateConfigValues(name, (FileConfiguration) holder.getConfig(), sender);
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.GREEN +
|
||||
"重载配置文件 " + name + " 成功!");
|
||||
} catch (Exception e) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED +
|
||||
"重载配置文件 " + name + " 失败!");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void deleteConfig(String name, CommandSender sender) {
|
||||
if (name.equals("default")) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED + "默认KB文件不允许删除!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!configs.containsKey(name)) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED + "KB文件 " + name + " 不存在!");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
plugin.getPlayerDataManager().getAllData().values().stream()
|
||||
.filter(data -> data.getCurrentKBFile().equals(name))
|
||||
.forEach(data -> data.setCurrentKBFile("default"));
|
||||
|
||||
ConfigHolder holder = configs.get(name);
|
||||
File configFile = holder.getFile();
|
||||
|
||||
boolean deleted = configFile.delete();
|
||||
configs.remove(name);
|
||||
|
||||
if (deleted) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.GREEN + "删除 " + name + " 成功!");
|
||||
} else {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED + "删除 " + name + " 失败!");
|
||||
}
|
||||
}
|
||||
|
||||
public void saveConfig(String name, CommandSender sender) {
|
||||
if (!configs.containsKey(name)) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED + "配置文件 " + name + " 不存在!");
|
||||
return;
|
||||
}
|
||||
|
||||
ConfigHolder holder = configs.get(name);
|
||||
try {
|
||||
((FileConfiguration) holder.getConfig()).save(holder.getFile());
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.GREEN + "保存配置文件 " + name + " 成功!");
|
||||
} catch (IOException e) {
|
||||
sender.sendMessage(plugin.getPluginPrefix() + ChatColor.RED + "保存配置文件 " + name + " 失败!");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void validateConfigValues(String configName, FileConfiguration config) {
|
||||
|
||||
int misplaceDelay = config.getInt("misplace.delay");
|
||||
if (misplaceDelay < 1 || misplaceDelay > 5) {
|
||||
plugin.logMessage(ChatColor.YELLOW + "配置 " + configName + " 中的 misplace.delay 超出范围 (1-5),已自动修正");
|
||||
config.set("misplace.delay", Math.max(1, Math.min(5, misplaceDelay)));
|
||||
}
|
||||
|
||||
|
||||
String[] requiredPaths = {
|
||||
"horizontal.ground", "horizontal.air", "vertical.ground", "vertical.air"
|
||||
};
|
||||
|
||||
for (String path : requiredPaths) {
|
||||
if (!config.contains(path)) {
|
||||
plugin.logMessage(ChatColor.YELLOW + "配置 " + configName + " 缺少必需字段: " + path + ",使用默认值");
|
||||
config.set(path, defaultConfig.get(path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public FileConfiguration getDefaultConfig() {
|
||||
return this.defaultConfig;
|
||||
}
|
||||
}
|
||||
40
src/main/java/me/dw1e/kbm/data/PlayerDataManager.java
Normal file
40
src/main/java/me/dw1e/kbm/data/PlayerDataManager.java
Normal file
@@ -0,0 +1,40 @@
|
||||
package me.dw1e.kbm.data;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class PlayerDataManager {
|
||||
private final Map<UUID, PlayerKnockbackData> playerData;
|
||||
|
||||
public PlayerDataManager() {
|
||||
this.playerData = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
public void registerAllPlayers() {
|
||||
Bukkit.getOnlinePlayers().forEach(this::registerPlayer);
|
||||
}
|
||||
|
||||
public void registerPlayer(Player player) {
|
||||
playerData.putIfAbsent(player.getUniqueId(), new PlayerKnockbackData(player));
|
||||
}
|
||||
|
||||
public void unregisterPlayer(UUID uuid) {
|
||||
playerData.remove(uuid);
|
||||
}
|
||||
|
||||
public PlayerKnockbackData getPlayerData(UUID uuid) {
|
||||
return playerData.get(uuid);
|
||||
}
|
||||
|
||||
public Map<UUID, PlayerKnockbackData> getAllData() {
|
||||
return playerData;
|
||||
}
|
||||
|
||||
public void clearAllData() {
|
||||
playerData.clear();
|
||||
}
|
||||
}
|
||||
103
src/main/java/me/dw1e/kbm/data/PlayerKnockbackData.java
Normal file
103
src/main/java/me/dw1e/kbm/data/PlayerKnockbackData.java
Normal file
@@ -0,0 +1,103 @@
|
||||
package me.dw1e.kbm.data;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class PlayerKnockbackData {
|
||||
private Vector pendingVelocity;
|
||||
private boolean isOnGround;
|
||||
private boolean isFiltered;
|
||||
private String currentKBFile;
|
||||
private int lastAttackerId;
|
||||
private double lastGroundY;
|
||||
private final Player player;
|
||||
private int lastHitTick;
|
||||
private int hitDelay;
|
||||
private int serverTick;
|
||||
|
||||
public void setServerTick(int serverTick) {
|
||||
this.serverTick = serverTick;
|
||||
}
|
||||
|
||||
public int getLastAttackerId() {
|
||||
return this.lastAttackerId;
|
||||
}
|
||||
|
||||
public int getLastHitTick() {
|
||||
return this.lastHitTick;
|
||||
}
|
||||
|
||||
public int getHitDelay() {
|
||||
return this.hitDelay;
|
||||
}
|
||||
|
||||
public boolean isOnGround() {
|
||||
return this.isOnGround;
|
||||
}
|
||||
|
||||
public boolean isFiltered() {
|
||||
return this.isFiltered;
|
||||
}
|
||||
|
||||
public void setHitDelay(int hitDelay) {
|
||||
this.hitDelay = hitDelay;
|
||||
}
|
||||
|
||||
public void setLastHitTick(int lastHitTick) {
|
||||
this.lastHitTick = lastHitTick;
|
||||
}
|
||||
|
||||
public void setPendingVelocity(Vector velocity) {
|
||||
this.pendingVelocity = velocity;
|
||||
}
|
||||
|
||||
public int getServerTick() {
|
||||
return this.serverTick;
|
||||
}
|
||||
|
||||
public Vector getPendingVelocity() {
|
||||
return this.pendingVelocity;
|
||||
}
|
||||
|
||||
public void setLastAttackerId(int lastAttackerId) {
|
||||
this.lastAttackerId = lastAttackerId;
|
||||
}
|
||||
|
||||
public double getLastGroundY() {
|
||||
return this.lastGroundY;
|
||||
}
|
||||
|
||||
public void setLastGroundY(double lastGroundY) {
|
||||
this.lastGroundY = lastGroundY;
|
||||
}
|
||||
|
||||
public boolean checkOnGround() {
|
||||
return this.player.isOnGround();
|
||||
}
|
||||
|
||||
public void setSprinting(boolean sprinting) {
|
||||
this.isOnGround = sprinting;
|
||||
}
|
||||
|
||||
public PlayerKnockbackData(Player player) {
|
||||
this.currentKBFile = "default";
|
||||
this.pendingVelocity = null;
|
||||
this.lastAttackerId = -1;
|
||||
this.player = player;
|
||||
this.lastGroundY = player.getLocation().getY();
|
||||
}
|
||||
|
||||
public String getCurrentKBFile() {
|
||||
return this.currentKBFile;
|
||||
}
|
||||
|
||||
public void setFiltered(boolean filtered) {
|
||||
this.isFiltered = filtered;
|
||||
// 设置无敌时间
|
||||
this.player.setMaximumNoDamageTicks(20);
|
||||
}
|
||||
|
||||
public void setCurrentKBFile(String kbFile) {
|
||||
this.currentKBFile = kbFile;
|
||||
}
|
||||
}
|
||||
158
src/main/java/me/dw1e/kbm/knockback/KnockbackCalculator.java
Normal file
158
src/main/java/me/dw1e/kbm/knockback/KnockbackCalculator.java
Normal file
@@ -0,0 +1,158 @@
|
||||
package me.dw1e.kbm.knockback;
|
||||
|
||||
import me.dw1e.kbm.KnockbackManager;
|
||||
import me.dw1e.kbm.api.KBMPlayerVelocityEvent;
|
||||
import me.dw1e.kbm.config.ConfigHolder;
|
||||
import me.dw1e.kbm.data.PlayerKnockbackData;
|
||||
import me.dw1e.kbm.data.PlayerDataManager;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.entity.Arrow;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.Projectile;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class KnockbackCalculator {
|
||||
private final KnockbackManager plugin;
|
||||
|
||||
public KnockbackCalculator(KnockbackManager plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public void calculateKnockback(Player victim, Player attacker,
|
||||
PlayerKnockbackData victimData, Entity damager) {
|
||||
PlayerKnockbackData attackerData = plugin.getPlayerDataManager()
|
||||
.getPlayerData(attacker.getUniqueId());
|
||||
|
||||
if (attackerData == null) return;
|
||||
|
||||
int currentTick = plugin.getServerTick();
|
||||
|
||||
victimData.setLastAttackerId(attacker.getEntityId());
|
||||
victimData.setHitDelay(currentTick);
|
||||
attackerData.setLastHitTick(currentTick);
|
||||
|
||||
FileConfiguration config = getVictimConfig(victimData);
|
||||
if (config == null) return;
|
||||
|
||||
Vector knockback = calculateBaseKnockback(victim, attacker, config, damager);
|
||||
|
||||
applyKnockbackModifiers(knockback, victim, attacker, config, damager, victimData);
|
||||
|
||||
KBMPlayerVelocityEvent velocityEvent = new KBMPlayerVelocityEvent(victim, knockback);
|
||||
Bukkit.getPluginManager().callEvent(velocityEvent);
|
||||
|
||||
if (!velocityEvent.isCancelled()) {
|
||||
int hitDelay = config.getInt("hit_delay");
|
||||
if (victim.getMaximumNoDamageTicks() != hitDelay) {
|
||||
victim.setMaximumNoDamageTicks(hitDelay);
|
||||
}
|
||||
|
||||
victimData.setPendingVelocity(velocityEvent.getVelocity());
|
||||
}
|
||||
}
|
||||
|
||||
private FileConfiguration getVictimConfig(PlayerKnockbackData victimData) {
|
||||
ConfigHolder holder = plugin.getConfigManager().getConfigs()
|
||||
.get(victimData.getCurrentKBFile());
|
||||
return holder != null ? (FileConfiguration) holder.getConfig() : null;
|
||||
}
|
||||
|
||||
private Vector calculateBaseKnockback(Player victim, Player attacker,
|
||||
FileConfiguration config, Entity damager) {
|
||||
Vector direction = calculateDirectionVector(victim, attacker);
|
||||
|
||||
boolean victimOnGround = victim.isOnGround();
|
||||
double horizontal = victimOnGround ?
|
||||
config.getDouble("horizontal.ground") : config.getDouble("horizontal.air");
|
||||
double vertical = victimOnGround ?
|
||||
config.getDouble("vertical.ground") : config.getDouble("vertical.air");
|
||||
|
||||
Vector baseKnockback = direction.multiply(new Vector(horizontal, vertical, horizontal));
|
||||
|
||||
if (damager instanceof Projectile) {
|
||||
double projectileMultiplier = config.getDouble("horizontal.projectile_multiplier");
|
||||
baseKnockback.multiply(new Vector(projectileMultiplier,
|
||||
config.getDouble("vertical.projectile_multiplier"), projectileMultiplier));
|
||||
}
|
||||
|
||||
return baseKnockback;
|
||||
}
|
||||
|
||||
private Vector calculateDirectionVector(Player victim, Player attacker) {
|
||||
double x = victim.getLocation().getX() - attacker.getLocation().getX();
|
||||
double z = victim.getLocation().getZ() - attacker.getLocation().getZ();
|
||||
|
||||
if (Math.hypot(x, z) < 1.0E-4D) {
|
||||
x = (Math.random() - Math.random()) * 0.01D;
|
||||
z = (Math.random() - Math.random()) * 0.01D;
|
||||
}
|
||||
|
||||
float attackerYaw = attacker.getLocation().getYaw() * 3.1415927F / 180.0F;
|
||||
|
||||
return new Vector(-Math.sin(attackerYaw), 0.0D, Math.cos(attackerYaw))
|
||||
.normalize().setY(1.0D);
|
||||
}
|
||||
|
||||
private void applyKnockbackModifiers(Vector knockback, Player victim, Player attacker,
|
||||
FileConfiguration config, Entity damager,
|
||||
PlayerKnockbackData victimData) {
|
||||
if (!(damager instanceof Projectile)) {
|
||||
applyKnockbackEnchantment(knockback, attacker, config);
|
||||
} else if (damager instanceof Arrow) {
|
||||
applyArrowKnockback(knockback, (Arrow) damager, config);
|
||||
}
|
||||
|
||||
|
||||
double yLimit = config.getDouble("y_limit");
|
||||
if (victim.getLocation().getY() - victimData.getLastGroundY() > yLimit) {
|
||||
knockback.setY(0.0D);
|
||||
}
|
||||
}
|
||||
|
||||
private void applyKnockbackEnchantment(Vector knockback, Player attacker,
|
||||
FileConfiguration config) {
|
||||
int knockbackLevel = attacker.getItemInHand().getEnchantmentLevel(Enchantment.KNOCKBACK);
|
||||
|
||||
boolean stopSprint = config.getBoolean("stop_sprint");
|
||||
PlayerKnockbackData attackerData = plugin.getPlayerDataManager()
|
||||
.getPlayerData(attacker.getUniqueId());
|
||||
|
||||
if ((!stopSprint && attackerData.isOnGround()) ||
|
||||
(stopSprint && attacker.isSprinting())) {
|
||||
knockbackLevel++;
|
||||
}
|
||||
|
||||
if (knockbackLevel > 0) {
|
||||
float attackerYaw = attacker.getLocation().getYaw() * 3.1415927F / 180.0F;
|
||||
double sprintExtra = config.getDouble("horizontal.sprint_extra");
|
||||
double verticalExtra = config.getDouble("vertical.sprint_extra");
|
||||
|
||||
Vector extraKnockback = new Vector(
|
||||
-Math.sin(attackerYaw) * knockbackLevel * sprintExtra,
|
||||
verticalExtra,
|
||||
Math.cos(attackerYaw) * knockbackLevel * sprintExtra
|
||||
);
|
||||
|
||||
knockback.add(extraKnockback);
|
||||
}
|
||||
}
|
||||
|
||||
private void applyArrowKnockback(Vector knockback, Arrow arrow, FileConfiguration config) {
|
||||
int arrowKnockback = arrow.getKnockbackStrength();
|
||||
if (arrowKnockback > 0) {
|
||||
double knockbackStrength = arrowKnockback * 0.6;
|
||||
double horizontalMagnitude = Math.hypot(knockback.getX(), knockback.getZ());
|
||||
|
||||
Vector extraKnockback = new Vector(
|
||||
knockback.getX() * knockbackStrength / horizontalMagnitude,
|
||||
config.getDouble("vertical.sprint_extra"),
|
||||
knockback.getZ() * knockbackStrength / horizontalMagnitude
|
||||
);
|
||||
|
||||
knockback.add(extraKnockback);
|
||||
}
|
||||
}
|
||||
}
|
||||
106
src/main/java/me/dw1e/kbm/listeners/PacketListener.java
Normal file
106
src/main/java/me/dw1e/kbm/listeners/PacketListener.java
Normal file
@@ -0,0 +1,106 @@
|
||||
package me.dw1e.kbm.listeners;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.events.ListenerPriority;
|
||||
import com.comphenix.protocol.events.PacketAdapter;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import me.dw1e.kbm.KnockbackManager;
|
||||
import me.dw1e.kbm.config.ConfigHolder;
|
||||
import me.dw1e.kbm.data.PlayerKnockbackData;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class PacketListener extends PacketAdapter {
|
||||
private final KnockbackManager plugin;
|
||||
private static final List<PacketType> LISTENED_PACKETS = Arrays.asList(
|
||||
PacketType.Play.Server.ENTITY_TELEPORT,
|
||||
PacketType.Play.Server.REL_ENTITY_MOVE_LOOK,
|
||||
PacketType.Play.Server.REL_ENTITY_MOVE,
|
||||
PacketType.Play.Server.ENTITY_LOOK
|
||||
);
|
||||
|
||||
public PacketListener(KnockbackManager plugin) {
|
||||
super(plugin, ListenerPriority.NORMAL, LISTENED_PACKETS);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public void register() {
|
||||
ProtocolLibrary.getProtocolManager().addPacketListener(this);
|
||||
}
|
||||
|
||||
public void unregister() {
|
||||
ProtocolLibrary.getProtocolManager().removePacketListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketSending(PacketEvent event) {
|
||||
if (!LISTENED_PACKETS.contains(event.getPacketType())) return;
|
||||
|
||||
Player player = event.getPlayer();
|
||||
PlayerKnockbackData data = plugin.getPlayerDataManager().getPlayerData(player.getUniqueId());
|
||||
|
||||
if (data == null) return;
|
||||
|
||||
FileConfiguration config = getPlayerConfig(data);
|
||||
if (config == null || !config.getBoolean("misplace.enabled")) return;
|
||||
|
||||
handleMisplaceCheck(event, data, player, config);
|
||||
}
|
||||
|
||||
private void handleMisplaceCheck(PacketEvent event, PlayerKnockbackData data,
|
||||
Player player, FileConfiguration config) {
|
||||
int entityId = event.getPacket().getIntegers().read(0);
|
||||
int currentTick = plugin.getServerTick();
|
||||
|
||||
// 检查是否是需要防Misplace的实体
|
||||
if (entityId == data.getLastAttackerId() &&
|
||||
currentTick - data.getLastHitTick() <= player.getMaximumNoDamageTicks()) {
|
||||
|
||||
Player attacker = findPlayerById(entityId);
|
||||
if (attacker != null && shouldDelayPacket(data, attacker, currentTick, config)) {
|
||||
delayPacket(event, data, player, config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Player findPlayerById(int entityId) {
|
||||
return Bukkit.getOnlinePlayers().stream()
|
||||
.filter(p -> p.getEntityId() == entityId)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private boolean shouldDelayPacket(PlayerKnockbackData data, Player attacker,
|
||||
int currentTick, FileConfiguration config) {
|
||||
PlayerKnockbackData attackerData = plugin.getPlayerDataManager()
|
||||
.getPlayerData(attacker.getUniqueId());
|
||||
|
||||
return attackerData != null &&
|
||||
currentTick - data.getHitDelay() > attacker.getMaximumNoDamageTicks();
|
||||
}
|
||||
|
||||
private void delayPacket(PacketEvent event, PlayerKnockbackData data,
|
||||
Player player, FileConfiguration config) {
|
||||
PacketContainer packet = event.getPacket().shallowClone();
|
||||
event.setCancelled(true);
|
||||
|
||||
data.setServerTick(plugin.getServerTick());
|
||||
|
||||
int delayTicks = config.getInt("misplace.delay");
|
||||
Bukkit.getScheduler().runTaskLater(plugin, () -> {
|
||||
ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet, false);
|
||||
}, delayTicks);
|
||||
}
|
||||
|
||||
private FileConfiguration getPlayerConfig(PlayerKnockbackData data) {
|
||||
ConfigHolder holder = plugin.getConfigManager().getConfigs()
|
||||
.get(data.getCurrentKBFile());
|
||||
return holder != null ? (FileConfiguration) holder.getConfig() : null;
|
||||
}
|
||||
}
|
||||
157
src/main/java/me/dw1e/kbm/listeners/PlayerEventListener.java
Normal file
157
src/main/java/me/dw1e/kbm/listeners/PlayerEventListener.java
Normal file
@@ -0,0 +1,157 @@
|
||||
package me.dw1e.kbm.listeners;
|
||||
|
||||
import me.dw1e.kbm.KnockbackManager;
|
||||
import me.dw1e.kbm.api.KBMPlayerVelocityEvent;
|
||||
import me.dw1e.kbm.config.ConfigHolder;
|
||||
import me.dw1e.kbm.data.PlayerKnockbackData;
|
||||
import me.dw1e.kbm.knockback.KnockbackCalculator;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.entity.Arrow;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.Projectile;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||
import org.bukkit.event.player.*;
|
||||
import org.bukkit.projectiles.ProjectileSource;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class PlayerEventListener implements Listener {
|
||||
private final KnockbackManager plugin;
|
||||
private final KnockbackCalculator knockbackCalculator;
|
||||
|
||||
public PlayerEventListener(KnockbackManager plugin) {
|
||||
this.plugin = plugin;
|
||||
this.knockbackCalculator = new KnockbackCalculator(plugin);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onPlayerMove(PlayerMoveEvent event) {
|
||||
Location to = event.getTo();
|
||||
Location from = event.getFrom();
|
||||
|
||||
if (to.equals(from)) return;
|
||||
|
||||
Player player = event.getPlayer();
|
||||
PlayerKnockbackData data = plugin.getPlayerDataManager().getPlayerData(player.getUniqueId());
|
||||
|
||||
if (data != null && data.checkOnGround()) {
|
||||
data.setLastGroundY(to.getY());
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onPlayerToggleSprint(PlayerToggleSprintEvent event) {
|
||||
PlayerKnockbackData data = plugin.getPlayerDataManager()
|
||||
.getPlayerData(event.getPlayer().getUniqueId());
|
||||
|
||||
if (data != null) {
|
||||
data.setSprinting(event.isSprinting());
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onPlayerCommand(PlayerCommandPreprocessEvent event) {
|
||||
String message = event.getMessage().toLowerCase();
|
||||
|
||||
// 支持的命令列表
|
||||
String[] supportedCommands = {
|
||||
"/knockbackmanager:kbm", "/kb", "/kbm", "/knockback",
|
||||
"/knockbackmanager:kb", "/knockbackmanager:knockback"
|
||||
};
|
||||
|
||||
for (String cmd : supportedCommands) {
|
||||
if (message.equals(cmd)) {
|
||||
event.getPlayer().sendMessage(plugin.getPluginPrefix() + ChatColor.GRAY +
|
||||
"此服务器正在使用 " + ChatColor.WHITE + "Knockback Manager" +
|
||||
ChatColor.GRAY + "(v" + plugin.getDescription().getVersion() +
|
||||
") 击退修改插件");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onPlayerQuit(PlayerQuitEvent event) {
|
||||
plugin.getPlayerDataManager().unregisterPlayer(event.getPlayer().getUniqueId());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
private void onPlayerVelocity(PlayerVelocityEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
PlayerKnockbackData data = plugin.getPlayerDataManager().getPlayerData(player.getUniqueId());
|
||||
|
||||
if (data != null && data.getPendingVelocity() != null) {
|
||||
event.setVelocity(data.getPendingVelocity());
|
||||
data.setPendingVelocity(null);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onPlayerJoin(PlayerJoinEvent event) {
|
||||
plugin.getPlayerDataManager().registerPlayer(event.getPlayer());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
private void onEntityDamageByEntity(EntityDamageByEntityEvent event) {
|
||||
if (!(event.getEntity() instanceof Player)) return;
|
||||
|
||||
Entity damager = event.getDamager();
|
||||
Player victim = (Player) event.getEntity();
|
||||
PlayerKnockbackData victimData = plugin.getPlayerDataManager().getPlayerData(victim.getUniqueId());
|
||||
|
||||
if (victimData == null || victimData.isFiltered()) return;
|
||||
|
||||
// 处理攻击者
|
||||
Player attacker = extractAttacker(damager);
|
||||
if (attacker == null) return;
|
||||
|
||||
// 检查自伤
|
||||
boolean isSelfDamage = attacker.equals(victim);
|
||||
if (isSelfDamage) return;
|
||||
|
||||
// 处理击退计算
|
||||
knockbackCalculator.calculateKnockback(victim, attacker, victimData, damager);
|
||||
}
|
||||
|
||||
private Player extractAttacker(Entity damager) {
|
||||
if (damager instanceof Player) {
|
||||
return (Player) damager;
|
||||
} else if (damager instanceof Projectile) {
|
||||
ProjectileSource shooter = ((Projectile) damager).getShooter();
|
||||
return (shooter instanceof Player) ? (Player) shooter : null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@EventHandler(priority = EventPriority.HIGH)
|
||||
private void onEntityDamage(EntityDamageByEntityEvent event) {
|
||||
if (!(event.getEntity() instanceof Player)) return;
|
||||
|
||||
Player victim = (Player) event.getEntity();
|
||||
PlayerKnockbackData victimData = plugin.getPlayerDataManager().getPlayerData(victim.getUniqueId());
|
||||
|
||||
// 如果玩家被过滤,取消击退
|
||||
if (victimData != null && victimData.isFiltered()) {
|
||||
// 重置击退速度
|
||||
victim.setVelocity(new Vector(0, 0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
// 处理玩家重生事件,重置数据
|
||||
@EventHandler
|
||||
private void onPlayerRespawn(PlayerRespawnEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
PlayerKnockbackData data = plugin.getPlayerDataManager().getPlayerData(player.getUniqueId());
|
||||
if (data != null) {
|
||||
data.setLastGroundY(player.getLocation().getY());
|
||||
data.setPendingVelocity(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
16
src/main/resources/default.yml
Normal file
16
src/main/resources/default.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
horizontal:
|
||||
ground: 0.4
|
||||
air: 0.4
|
||||
sprint_extra: 0.5
|
||||
projectile_multiplier: 1.0
|
||||
vertical:
|
||||
ground: 0.36075
|
||||
air: 0.24775
|
||||
sprint_extra: 0.1
|
||||
projectile_multiplier: 1.0
|
||||
misplace:
|
||||
enabled: false
|
||||
delay: 1
|
||||
stop_sprint: true
|
||||
y_limit: 0.675
|
||||
hit_delay: 20
|
||||
18
src/main/resources/plugin.yml
Normal file
18
src/main/resources/plugin.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
name: KnockbackManager
|
||||
main: me.dw1e.kbm.KnockbackManager
|
||||
version: 1.13.4
|
||||
author: dw1e
|
||||
softdepend: [ ProtocolLib ]
|
||||
api-version: 1.13
|
||||
|
||||
commands:
|
||||
kbm:
|
||||
aliases: [ kb, knockback ]
|
||||
|
||||
permissions:
|
||||
kbm.use:
|
||||
description: Allows usage of Knockback Manager commands
|
||||
default: op
|
||||
kbm.admin:
|
||||
description: Full access to Knockback Manager features
|
||||
default: op
|
||||
Reference in New Issue
Block a user