This commit is contained in:
2026-06-20 12:07:10 -04:00
commit 7aad977d66
13 changed files with 1077 additions and 0 deletions
View File
+48
View File
@@ -0,0 +1,48 @@
<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>com.bitnix</groupId>
<artifactId>DirtPVP</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<name>DirtPVP</name>
<description>Advanced anti-flight and combat restriction plugin for Paper.</description>
<properties>
<java.version>21</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<repositories>
<repository>
<id>papermc</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.21.8-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>DirtPVP</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<release>21</release>
</configuration>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,785 @@
package com.bitnix.dirtpvp;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.World;
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.Entity;
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.entity.EntityDamageEvent;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerToggleFlightEvent;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitTask;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
public final class DirtPVPPlugin extends JavaPlugin implements Listener, TabExecutor {
private final Map<UUID, Long> combatUntil = new HashMap<>();
private final Map<UUID, BukkitTask> expiryTasks = new HashMap<>();
private final Map<UUID, Boolean> previousAllowFlight = new HashMap<>();
private final Set<UUID> notifyToggledOff = new HashSet<>();
private final List<KillRewardRule> killRewardRules = new ArrayList<>();
private long combatDurationTicks;
private long combatDurationMillis;
private boolean disableFlightOnDamage;
private boolean disableFlightOnAttack;
private boolean disableFlightOnJoinIfTagged;
private boolean restoreFlightAfterCombat;
private boolean opBypass;
private boolean permissionBypass;
private String bypassPermission;
private boolean anyDamage;
private boolean playerVsPlayerOnly;
private boolean attackAnyEntity;
private boolean projectileCounts;
private String worldsMode;
private Set<String> configuredWorlds;
private boolean blockedCommandsEnabled;
private Set<String> blockedCommands;
private boolean killRewardsEnabled;
private boolean notificationsEnabled;
private boolean notifyConsole;
private boolean requireNotifyPermission;
private String notifyPermission;
private boolean notifyOnCombatTag;
private boolean notifyOnCommandBlock;
private boolean notifyOnFlightBlock;
private boolean notifyOnKillReward;
private boolean notifyOnKillRewardBlockedByIp;
private String prefix;
private String msgCombatStartDamaged;
private String msgCombatStartAttack;
private String msgCombatFlyBlocked;
private String msgCombatEnded;
private String msgCommandBlocked;
private String msgNotifyCommandBlocked;
private String msgNotifyCombatTagged;
private String msgNotifyFlightBlocked;
private String msgNotifyKillReward;
private String msgNotifyKillRewardBlockedByIp;
private String msgReloadSuccess;
private String msgNoPermission;
private String msgNotifyEnabled;
private String msgNotifyDisabled;
private String msgUsage;
private String msgStatusSelf;
private String msgStatusOther;
private String msgPlayerNotFound;
private String msgOnlyPlayers;
@Override
public void onEnable() {
saveDefaultConfig();
loadPluginSettings();
getServer().getPluginManager().registerEvents(this, this);
if (getCommand("dirtpvp") != null) {
getCommand("dirtpvp").setExecutor(this);
getCommand("dirtpvp").setTabCompleter(this);
}
getLogger().info("DirtPVP enabled.");
}
@Override
public void onDisable() {
for (BukkitTask task : expiryTasks.values()) {
if (task != null) {
task.cancel();
}
}
expiryTasks.clear();
combatUntil.clear();
previousAllowFlight.clear();
notifyToggledOff.clear();
killRewardRules.clear();
}
private void loadPluginSettings() {
reloadConfig();
FileConfiguration config = getConfig();
int seconds = config.getInt("combat.duration-seconds", 300);
if (seconds < 1) {
seconds = 300;
}
this.combatDurationMillis = seconds * 1000L;
this.combatDurationTicks = seconds * 20L;
this.disableFlightOnDamage = config.getBoolean("flight.disable-on-damage", true);
this.disableFlightOnAttack = config.getBoolean("flight.disable-on-attack", true);
this.disableFlightOnJoinIfTagged = config.getBoolean("flight.disable-on-join-if-tagged", true);
this.restoreFlightAfterCombat = config.getBoolean("flight.restore-flight-after-combat", false);
this.opBypass = config.getBoolean("bypass.op-bypass", true);
this.permissionBypass = config.getBoolean("bypass.permission-bypass", true);
this.bypassPermission = config.getString("bypass.permission", "dirtpvp.bypass");
this.anyDamage = config.getBoolean("tag-triggers.any-damage", true);
this.playerVsPlayerOnly = config.getBoolean("tag-triggers.player-vs-player-only", false);
this.attackAnyEntity = config.getBoolean("tag-triggers.attack-any-entity", true);
this.projectileCounts = config.getBoolean("tag-triggers.projectile-counts", true);
this.worldsMode = config.getString("worlds.mode", "blacklist").toLowerCase(Locale.ROOT);
this.configuredWorlds = new HashSet<>();
for (String worldName : config.getStringList("worlds.list")) {
if (worldName != null && !worldName.isBlank()) {
configuredWorlds.add(worldName.toLowerCase(Locale.ROOT));
}
}
this.blockedCommandsEnabled = config.getBoolean("blocked-commands.enabled", true);
this.blockedCommands = new HashSet<>();
for (String command : config.getStringList("blocked-commands.commands")) {
if (command == null) {
continue;
}
String cleaned = normalizeCommand(command);
if (!cleaned.isEmpty()) {
blockedCommands.add(cleaned);
}
}
this.killRewardsEnabled = config.getBoolean("kill-rewards.enabled", false);
this.killRewardRules.clear();
ConfigurationSection rewardsSection = config.getConfigurationSection("kill-rewards.rewards");
if (rewardsSection != null) {
for (String key : rewardsSection.getKeys(false)) {
ConfigurationSection rewardSection = rewardsSection.getConfigurationSection(key);
if (rewardSection == null) {
continue;
}
boolean enabled = rewardSection.getBoolean("enabled", true);
boolean ipCheck = rewardSection.getBoolean("ip-check", false);
List<String> commands = new ArrayList<>();
for (String command : rewardSection.getStringList("commands")) {
if (command != null && !command.isBlank()) {
commands.add(command);
}
}
killRewardRules.add(new KillRewardRule(key, enabled, ipCheck, commands));
}
}
this.notificationsEnabled = config.getBoolean("notifications.enabled", true);
this.notifyConsole = config.getBoolean("notifications.notify-console", false);
this.requireNotifyPermission = config.getBoolean("notifications.require-permission", true);
this.notifyPermission = config.getString("notifications.permission", "dirtpvp.notify");
this.notifyOnCombatTag = config.getBoolean("notifications.send-on-combat-tag", false);
this.notifyOnCommandBlock = config.getBoolean("notifications.send-on-command-block", true);
this.notifyOnFlightBlock = config.getBoolean("notifications.send-on-flight-block", false);
this.notifyOnKillReward = config.getBoolean("notifications.send-on-kill-reward", false);
this.notifyOnKillRewardBlockedByIp = config.getBoolean("notifications.send-on-kill-reward-blocked-by-ip", false);
this.prefix = color(config.getString("messages.prefix", "&8[&6DirtPVP&8] &r"));
this.msgCombatStartDamaged = color(config.getString("messages.combat-start-damaged", "&cFlight disabled: you were damaged and are now in combat for &e%time%&c seconds."));
this.msgCombatStartAttack = color(config.getString("messages.combat-start-attack", "&cFlight disabled: you attacked something and are now in combat for &e%time%&c seconds."));
this.msgCombatFlyBlocked = color(config.getString("messages.combat-fly-blocked", "&cYou cannot fly while in combat. Time left: &e%time%&c seconds."));
this.msgCombatEnded = color(config.getString("messages.combat-ended", "&aYour combat timer ended. You may fly again if you still have permission."));
this.msgCommandBlocked = color(config.getString("messages.command-blocked", "&cYou cannot use &e/%command% &cwhile in combat. Time left: &e%time%&c seconds."));
this.msgNotifyCommandBlocked = color(config.getString("messages.notify-command-blocked", "&e%player% &7tried to use &f/%command% &7while in combat."));
this.msgNotifyCombatTagged = color(config.getString("messages.notify-combat-tagged", "&e%player% &7was combat tagged."));
this.msgNotifyFlightBlocked = color(config.getString("messages.notify-flight-blocked", "&e%player% &7tried to fly while in combat."));
this.msgNotifyKillReward = color(config.getString("messages.notify-kill-reward", "&eExecuted kill reward '&f%reward%&e' for &f%killer% &7after killing &f%victim%&7."));
this.msgNotifyKillRewardBlockedByIp = color(config.getString("messages.notify-kill-reward-blocked-by-ip", "&cSkipped kill reward '&f%reward%&c' for &f%killer% &7because killer/victim IP matched."));
this.msgReloadSuccess = color(config.getString("messages.reload-success", "&aDirtPVP config reloaded."));
this.msgNoPermission = color(config.getString("messages.no-permission", "&cYou do not have permission."));
this.msgNotifyEnabled = color(config.getString("messages.notify-enabled", "&aYou will now receive DirtPVP notifications."));
this.msgNotifyDisabled = color(config.getString("messages.notify-disabled", "&cYou will no longer receive DirtPVP notifications."));
this.msgUsage = color(config.getString("messages.usage", "&eUsage: /dirtpvp reload &7| &e/dirtpvp notify &7| &e/dirtpvp status [player]"));
this.msgStatusSelf = color(config.getString("messages.status-self", "&eCombat status: &f%status% &7| Time left: &f%time%s"));
this.msgStatusOther = color(config.getString("messages.status-other", "&e%player%'s combat status: &f%status% &7| Time left: &f%time%s"));
this.msgPlayerNotFound = color(config.getString("messages.player-not-found", "&cPlayer not found."));
this.msgOnlyPlayers = color(config.getString("messages.only-players", "&cOnly players can use this command."));
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerDamaged(EntityDamageEvent event) {
if (!disableFlightOnDamage) {
return;
}
if (!(event.getEntity() instanceof Player player)) {
return;
}
if (!isWorldEnabled(player.getWorld())) {
return;
}
if (shouldIgnore(player)) {
return;
}
if (playerVsPlayerOnly) {
return;
}
if (!anyDamage) {
return;
}
enterCombat(player, msgCombatStartDamaged, notifyOnCombatTag);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onEntityDamagedByEntity(EntityDamageByEntityEvent event) {
Player attacker = getResponsiblePlayer(event.getDamager());
boolean projectileHit = event.getDamager() instanceof Projectile;
if (attacker != null && disableFlightOnAttack) {
if (isWorldEnabled(attacker.getWorld()) && !shouldIgnore(attacker)) {
if (!projectileHit || projectileCounts) {
if (attackAnyEntity || event.getEntity() instanceof Player) {
enterCombat(attacker, msgCombatStartAttack, notifyOnCombatTag);
}
}
}
}
if (!(event.getEntity() instanceof Player victim)) {
return;
}
if (!disableFlightOnDamage) {
return;
}
if (!isWorldEnabled(victim.getWorld())) {
return;
}
if (shouldIgnore(victim)) {
return;
}
if (playerVsPlayerOnly) {
if (attacker == null) {
return;
}
if (projectileHit && !projectileCounts) {
return;
}
enterCombat(victim, msgCombatStartDamaged, notifyOnCombatTag);
return;
}
if (attacker != null) {
if (projectileHit && !projectileCounts) {
return;
}
enterCombat(victim, msgCombatStartDamaged, notifyOnCombatTag);
return;
}
if (anyDamage) {
enterCombat(victim, msgCombatStartDamaged, notifyOnCombatTag);
}
}
@EventHandler
public void onPlayerDeath(PlayerDeathEvent event) {
if (!killRewardsEnabled) {
return;
}
Player victim = event.getEntity();
Player killer = victim.getKiller();
if (killer == null) {
return;
}
if (!isWorldEnabled(victim.getWorld())) {
return;
}
boolean sameIp = hasSameIp(killer, victim);
for (KillRewardRule rule : killRewardRules) {
if (!rule.enabled()) {
continue;
}
if (rule.commands().isEmpty()) {
continue;
}
if (rule.ipCheck() && sameIp) {
if (notifyOnKillRewardBlockedByIp) {
notifyStaff(
msgNotifyKillRewardBlockedByIp
.replace("%reward%", rule.name())
.replace("%killer%", killer.getName())
.replace("%victim%", victim.getName())
);
}
continue;
}
for (String rawCommand : rule.commands()) {
String finalCommand = rawCommand
.replace("%killer%", killer.getName())
.replace("%victim%", victim.getName());
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), finalCommand);
}
if (notifyOnKillReward) {
notifyStaff(
msgNotifyKillReward
.replace("%reward%", rule.name())
.replace("%killer%", killer.getName())
.replace("%victim%", victim.getName())
);
}
}
}
@EventHandler(ignoreCancelled = true)
public void onFlightToggle(PlayerToggleFlightEvent event) {
Player player = event.getPlayer();
if (!isWorldEnabled(player.getWorld())) {
return;
}
if (shouldIgnore(player)) {
return;
}
if (!isInCombat(player.getUniqueId())) {
return;
}
event.setCancelled(true);
disableFlightNow(player);
player.sendMessage(withPrefix(replaceTime(msgCombatFlyBlocked, getRemainingSeconds(player.getUniqueId()))));
if (notifyOnFlightBlock) {
notifyStaff(replacePlayerAndCommand(msgNotifyFlightBlocked, player.getName(), ""));
}
}
@EventHandler
public void onJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
if (!disableFlightOnJoinIfTagged) {
return;
}
if (!isWorldEnabled(player.getWorld())) {
return;
}
if (shouldIgnore(player)) {
return;
}
if (isInCombat(player.getUniqueId())) {
disableFlightNow(player);
}
}
@EventHandler(ignoreCancelled = true)
public void onCommandPreprocess(PlayerCommandPreprocessEvent event) {
Player player = event.getPlayer();
if (!blockedCommandsEnabled) {
return;
}
if (!isWorldEnabled(player.getWorld())) {
return;
}
if (shouldIgnore(player)) {
return;
}
if (!isInCombat(player.getUniqueId())) {
return;
}
String message = event.getMessage();
if (message == null || message.isBlank() || !message.startsWith("/")) {
return;
}
String raw = message.substring(1).trim();
if (raw.isEmpty()) {
return;
}
String[] split = raw.split("\\s+");
String label = normalizeCommand(split[0]);
if (!isBlockedCommand(label)) {
return;
}
event.setCancelled(true);
player.sendMessage(withPrefix(
replaceTime(
replaceCommand(msgCommandBlocked, label),
getRemainingSeconds(player.getUniqueId())
)
));
if (notifyOnCommandBlock) {
notifyStaff(replacePlayerAndCommand(msgNotifyCommandBlocked, player.getName(), label));
}
}
private void enterCombat(Player player, String startMessage, boolean sendNotify) {
UUID uuid = player.getUniqueId();
boolean alreadyInCombat = isInCombat(uuid);
if (!previousAllowFlight.containsKey(uuid)) {
previousAllowFlight.put(uuid, player.getAllowFlight());
}
disableFlightNow(player);
long expiresAt = System.currentTimeMillis() + combatDurationMillis;
combatUntil.put(uuid, expiresAt);
BukkitTask oldTask = expiryTasks.remove(uuid);
if (oldTask != null) {
oldTask.cancel();
}
BukkitTask newTask = Bukkit.getScheduler().runTaskLater(this, () -> {
Long stored = combatUntil.get(uuid);
if (stored != null && stored <= System.currentTimeMillis()) {
combatUntil.remove(uuid);
expiryTasks.remove(uuid);
Player online = Bukkit.getPlayer(uuid);
Boolean hadAllowFlight = previousAllowFlight.remove(uuid);
if (online != null && online.isOnline()) {
if (restoreFlightAfterCombat && Boolean.TRUE.equals(hadAllowFlight)) {
online.setAllowFlight(true);
}
online.sendMessage(withPrefix(msgCombatEnded));
}
}
}, combatDurationTicks);
expiryTasks.put(uuid, newTask);
if (!alreadyInCombat) {
player.sendMessage(withPrefix(replaceTime(startMessage, combatDurationMillis / 1000L)));
if (sendNotify) {
notifyStaff(replacePlayerAndCommand(msgNotifyCombatTagged, player.getName(), ""));
}
}
}
private void disableFlightNow(Player player) {
if (player.isFlying()) {
player.setFlying(false);
}
if (player.getAllowFlight()) {
player.setAllowFlight(false);
}
}
private boolean isBlockedCommand(String label) {
if (blockedCommands.contains(label)) {
return true;
}
for (String blocked : blockedCommands) {
if (label.equals(blocked) || label.startsWith(blocked + ":")) {
return true;
}
}
return false;
}
private Player getResponsiblePlayer(Entity damager) {
if (damager instanceof Player player) {
return player;
}
if (damager instanceof Projectile projectile) {
if (!projectileCounts) {
return null;
}
Object shooter = projectile.getShooter();
if (shooter instanceof Player player) {
return player;
}
}
return null;
}
private boolean hasSameIp(Player first, Player second) {
InetSocketAddress firstAddress = first.getAddress();
InetSocketAddress secondAddress = second.getAddress();
if (firstAddress == null || secondAddress == null) {
return false;
}
if (firstAddress.getAddress() == null || secondAddress.getAddress() == null) {
return false;
}
return firstAddress.getAddress().getHostAddress().equals(secondAddress.getAddress().getHostAddress());
}
private boolean shouldIgnore(Player player) {
if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR) {
return true;
}
if (opBypass && player.isOp()) {
return true;
}
return permissionBypass
&& bypassPermission != null
&& !bypassPermission.isBlank()
&& player.hasPermission(bypassPermission);
}
private boolean isWorldEnabled(World world) {
String worldName = world.getName().toLowerCase(Locale.ROOT);
if (worldsMode.equals("whitelist")) {
return configuredWorlds.contains(worldName);
}
if (worldsMode.equals("blacklist")) {
return !configuredWorlds.contains(worldName);
}
return true;
}
private boolean isInCombat(UUID uuid) {
Long expiresAt = combatUntil.get(uuid);
if (expiresAt == null) {
return false;
}
if (expiresAt <= System.currentTimeMillis()) {
combatUntil.remove(uuid);
BukkitTask task = expiryTasks.remove(uuid);
if (task != null) {
task.cancel();
}
previousAllowFlight.remove(uuid);
return false;
}
return true;
}
private long getRemainingSeconds(UUID uuid) {
Long expiresAt = combatUntil.get(uuid);
if (expiresAt == null) {
return 0L;
}
long remaining = expiresAt - System.currentTimeMillis();
if (remaining <= 0L) {
return 0L;
}
return (remaining + 999L) / 1000L;
}
private void notifyStaff(String message) {
if (!notificationsEnabled) {
return;
}
String formatted = withPrefix(message);
if (notifyConsole) {
Bukkit.getConsoleSender().sendMessage(formatted);
}
for (Player online : Bukkit.getOnlinePlayers()) {
if (notifyToggledOff.contains(online.getUniqueId())) {
continue;
}
if (requireNotifyPermission) {
if (notifyPermission == null || notifyPermission.isBlank() || !online.hasPermission(notifyPermission)) {
continue;
}
}
online.sendMessage(formatted);
}
}
private String normalizeCommand(String command) {
String cleaned = command.toLowerCase(Locale.ROOT).trim();
if (cleaned.startsWith("/")) {
cleaned = cleaned.substring(1);
}
return cleaned;
}
private String replaceTime(String message, long seconds) {
return message.replace("%time%", String.valueOf(seconds));
}
private String replaceCommand(String message, String command) {
return message.replace("%command%", command);
}
private String replacePlayerAndCommand(String message, String player, String command) {
return message.replace("%player%", player).replace("%command%", command);
}
private String withPrefix(String message) {
return prefix + message;
}
private String color(String input) {
return input == null ? "" : input.replace("&", "§");
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (args.length == 0) {
sender.sendMessage(withPrefix(msgUsage));
return true;
}
if (args[0].equalsIgnoreCase("reload")) {
if (!sender.hasPermission("dirtpvp.reload")) {
sender.sendMessage(withPrefix(msgNoPermission));
return true;
}
loadPluginSettings();
sender.sendMessage(withPrefix(msgReloadSuccess));
return true;
}
if (args[0].equalsIgnoreCase("notify")) {
if (!(sender instanceof Player player)) {
sender.sendMessage(withPrefix(msgOnlyPlayers));
return true;
}
if (requireNotifyPermission) {
if (notifyPermission == null || notifyPermission.isBlank() || !player.hasPermission(notifyPermission)) {
player.sendMessage(withPrefix(msgNoPermission));
return true;
}
}
UUID uuid = player.getUniqueId();
if (notifyToggledOff.contains(uuid)) {
notifyToggledOff.remove(uuid);
player.sendMessage(withPrefix(msgNotifyEnabled));
} else {
notifyToggledOff.add(uuid);
player.sendMessage(withPrefix(msgNotifyDisabled));
}
return true;
}
if (args[0].equalsIgnoreCase("status")) {
if (args.length == 1) {
if (!(sender instanceof Player player)) {
sender.sendMessage(withPrefix(msgOnlyPlayers));
return true;
}
boolean inCombat = isInCombat(player.getUniqueId());
long time = getRemainingSeconds(player.getUniqueId());
String status = inCombat ? "IN_COMBAT" : "SAFE";
player.sendMessage(withPrefix(
msgStatusSelf
.replace("%status%", status)
.replace("%time%", String.valueOf(time))
));
return true;
}
Player target = Bukkit.getPlayerExact(args[1]);
if (target == null) {
sender.sendMessage(withPrefix(msgPlayerNotFound));
return true;
}
boolean inCombat = isInCombat(target.getUniqueId());
long time = getRemainingSeconds(target.getUniqueId());
String status = inCombat ? "IN_COMBAT" : "SAFE";
sender.sendMessage(withPrefix(
msgStatusOther
.replace("%player%", target.getName())
.replace("%status%", status)
.replace("%time%", String.valueOf(time))
));
return true;
}
sender.sendMessage(withPrefix(msgUsage));
return true;
}
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
List<String> completions = new ArrayList<>();
if (args.length == 1) {
String input = args[0].toLowerCase(Locale.ROOT);
if ("reload".startsWith(input)) {
completions.add("reload");
}
if ("notify".startsWith(input)) {
completions.add("notify");
}
if ("status".startsWith(input)) {
completions.add("status");
}
return completions;
}
if (args.length == 2 && args[0].equalsIgnoreCase("status")) {
String input = args[1].toLowerCase(Locale.ROOT);
for (Player online : Bukkit.getOnlinePlayers()) {
if (online.getName().toLowerCase(Locale.ROOT).startsWith(input)) {
completions.add(online.getName());
}
}
}
return completions;
}
private record KillRewardRule(String name, boolean enabled, boolean ipCheck, List<String> commands) {
}
}
+89
View File
@@ -0,0 +1,89 @@
combat:
duration-seconds: 300
flight:
disable-on-damage: true
disable-on-attack: true
disable-on-join-if-tagged: true
restore-flight-after-combat: false
bypass:
op-bypass: true
permission-bypass: true
permission: "dirtpvp.bypass"
tag-triggers:
any-damage: true
player-vs-player-only: false
attack-any-entity: true
projectile-counts: true
worlds:
mode: "blacklist"
list:
- "spawn"
blocked-commands:
enabled: true
commands:
- "spawn"
- "home"
- "rt"
- "wild"
- "tpa"
- "tpahere"
kill-rewards:
enabled: false
rewards:
money:
enabled: true
ip-check: true
commands:
- "eco give %killer% 1000"
shards:
enabled: false
ip-check: true
commands:
- "shards give %killer% 5"
broadcast:
enabled: false
ip-check: false
commands:
- "broadcast &e%killer% &7killed &e%victim%&7."
notifications:
enabled: true
notify-console: false
require-permission: true
permission: "dirtpvp.notify"
send-on-combat-tag: false
send-on-command-block: true
send-on-flight-block: false
send-on-kill-reward: false
send-on-kill-reward-blocked-by-ip: false
messages:
prefix: "&8[&6DirtPVP&8] &r"
combat-start-damaged: "&cFlight disabled: you were damaged and are now in combat for &e%time%&c seconds."
combat-start-attack: "&cFlight disabled: you attacked something and are now in combat for &e%time%&c seconds."
combat-fly-blocked: "&cYou cannot fly while in combat. Time left: &e%time%&c seconds."
combat-ended: "&aYour combat timer ended. You may fly again if you still have permission."
command-blocked: "&cYou cannot use &e/%command% &cwhile in combat. Time left: &e%time%&c seconds."
notify-command-blocked: "&e%player% &7tried to use &f/%command% &7while in combat."
notify-combat-tagged: "&e%player% &7was combat tagged."
notify-flight-blocked: "&e%player% &7tried to fly while in combat."
notify-kill-reward: "&eExecuted kill reward '&f%reward%&e' for &f%killer% &7after killing &f%victim%&7."
notify-kill-reward-blocked-by-ip: "&cSkipped kill reward '&f%reward%&c' for &f%killer% &7because killer/victim IP matched."
reload-success: "&aDirtPVP config reloaded."
no-permission: "&cYou do not have permission."
notify-enabled: "&aYou will now receive DirtPVP notifications."
notify-disabled: "&cYou will no longer receive DirtPVP notifications."
usage: "&eUsage: /dirtpvp reload &7| &e/dirtpvp notify &7| &e/dirtpvp status [player]"
status-self: "&eCombat status: &f%status% &7| Time left: &f%time%s"
status-other: "&e%player%'s combat status: &f%status% &7| Time left: &f%time%s"
player-not-found: "&cPlayer not found."
only-players: "&cOnly players can use this command."
+29
View File
@@ -0,0 +1,29 @@
name: DirtPVP
version: 1.0
main: com.bitnix.dirtpvp.DirtPVPPlugin
api-version: '1.21'
author: bitnix
description: Advanced anti-flight and combat restriction plugin.
commands:
dirtpvp:
description: Main DirtPVP command
usage: /dirtpvp
aliases: [antiflypvp]
permissions:
dirtpvp.use:
description: Allows use of DirtPVP
default: true
dirtpvp.reload:
description: Allows reloading DirtPVP config
default: op
dirtpvp.bypass:
description: Bypass combat restrictions
default: op
dirtpvp.notify:
description: Receive DirtPVP staff notifications
default: op
Binary file not shown.
+89
View File
@@ -0,0 +1,89 @@
combat:
duration-seconds: 300
flight:
disable-on-damage: true
disable-on-attack: true
disable-on-join-if-tagged: true
restore-flight-after-combat: false
bypass:
op-bypass: true
permission-bypass: true
permission: "dirtpvp.bypass"
tag-triggers:
any-damage: true
player-vs-player-only: false
attack-any-entity: true
projectile-counts: true
worlds:
mode: "blacklist"
list:
- "spawn"
blocked-commands:
enabled: true
commands:
- "spawn"
- "home"
- "rt"
- "wild"
- "tpa"
- "tpahere"
kill-rewards:
enabled: false
rewards:
money:
enabled: true
ip-check: true
commands:
- "eco give %killer% 1000"
shards:
enabled: false
ip-check: true
commands:
- "shards give %killer% 5"
broadcast:
enabled: false
ip-check: false
commands:
- "broadcast &e%killer% &7killed &e%victim%&7."
notifications:
enabled: true
notify-console: false
require-permission: true
permission: "dirtpvp.notify"
send-on-combat-tag: false
send-on-command-block: true
send-on-flight-block: false
send-on-kill-reward: false
send-on-kill-reward-blocked-by-ip: false
messages:
prefix: "&8[&6DirtPVP&8] &r"
combat-start-damaged: "&cFlight disabled: you were damaged and are now in combat for &e%time%&c seconds."
combat-start-attack: "&cFlight disabled: you attacked something and are now in combat for &e%time%&c seconds."
combat-fly-blocked: "&cYou cannot fly while in combat. Time left: &e%time%&c seconds."
combat-ended: "&aYour combat timer ended. You may fly again if you still have permission."
command-blocked: "&cYou cannot use &e/%command% &cwhile in combat. Time left: &e%time%&c seconds."
notify-command-blocked: "&e%player% &7tried to use &f/%command% &7while in combat."
notify-combat-tagged: "&e%player% &7was combat tagged."
notify-flight-blocked: "&e%player% &7tried to fly while in combat."
notify-kill-reward: "&eExecuted kill reward '&f%reward%&e' for &f%killer% &7after killing &f%victim%&7."
notify-kill-reward-blocked-by-ip: "&cSkipped kill reward '&f%reward%&c' for &f%killer% &7because killer/victim IP matched."
reload-success: "&aDirtPVP config reloaded."
no-permission: "&cYou do not have permission."
notify-enabled: "&aYou will now receive DirtPVP notifications."
notify-disabled: "&cYou will no longer receive DirtPVP notifications."
usage: "&eUsage: /dirtpvp reload &7| &e/dirtpvp notify &7| &e/dirtpvp status [player]"
status-self: "&eCombat status: &f%status% &7| Time left: &f%time%s"
status-other: "&e%player%'s combat status: &f%status% &7| Time left: &f%time%s"
player-not-found: "&cPlayer not found."
only-players: "&cOnly players can use this command."
+29
View File
@@ -0,0 +1,29 @@
name: DirtPVP
version: 1.0
main: com.bitnix.dirtpvp.DirtPVPPlugin
api-version: '1.21'
author: bitnix
description: Advanced anti-flight and combat restriction plugin.
commands:
dirtpvp:
description: Main DirtPVP command
usage: /dirtpvp
aliases: [antiflypvp]
permissions:
dirtpvp.use:
description: Allows use of DirtPVP
default: true
dirtpvp.reload:
description: Allows reloading DirtPVP config
default: op
dirtpvp.bypass:
description: Bypass combat restrictions
default: op
dirtpvp.notify:
description: Receive DirtPVP staff notifications
default: op
+5
View File
@@ -0,0 +1,5 @@
#Generated by Maven
#Sun Jun 14 18:05:11 EDT 2026
artifactId=DirtPVP
groupId=com.bitnix
version=1.0
@@ -0,0 +1,2 @@
com/bitnix/dirtpvp/DirtPVPPlugin.class
com/bitnix/dirtpvp/DirtPVPPlugin$KillRewardRule.class
@@ -0,0 +1 @@
/home/bitnix/Desktop/DirtPVP/src/main/java/com/bitnix/dirtpvp/DirtPVPPlugin.java