added
This commit is contained in:
@@ -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) {
|
||||
}
|
||||
}
|
||||
@@ -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."
|
||||
@@ -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.
Binary file not shown.
Binary file not shown.
@@ -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."
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user