From 7aad977d666a0997be4854c1e8a8ee32ba58f2fb Mon Sep 17 00:00:00 2001 From: Xelara Networks Date: Sat, 20 Jun 2026 12:07:10 -0400 Subject: [PATCH] added --- README.md | 0 pom.xml | 48 ++ .../com/bitnix/dirtpvp/DirtPVPPlugin.java | 785 ++++++++++++++++++ src/main/resources/config.yml | 89 ++ src/main/resources/plugin.yml | 29 + target/DirtPVP.jar | Bin 0 -> 15940 bytes .../DirtPVPPlugin$KillRewardRule.class | Bin 0 -> 2249 bytes .../com/bitnix/dirtpvp/DirtPVPPlugin.class | Bin 0 -> 24195 bytes target/classes/config.yml | 89 ++ target/classes/plugin.yml | 29 + target/maven-archiver/pom.properties | 5 + .../compile/default-compile/createdFiles.lst | 2 + .../compile/default-compile/inputFiles.lst | 1 + 13 files changed, 1077 insertions(+) create mode 100644 README.md create mode 100644 pom.xml create mode 100644 src/main/java/com/bitnix/dirtpvp/DirtPVPPlugin.java create mode 100644 src/main/resources/config.yml create mode 100644 src/main/resources/plugin.yml create mode 100644 target/DirtPVP.jar create mode 100644 target/classes/com/bitnix/dirtpvp/DirtPVPPlugin$KillRewardRule.class create mode 100644 target/classes/com/bitnix/dirtpvp/DirtPVPPlugin.class create mode 100644 target/classes/config.yml create mode 100644 target/classes/plugin.yml create mode 100644 target/maven-archiver/pom.properties create mode 100644 target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst create mode 100644 target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..3ad7cea --- /dev/null +++ b/pom.xml @@ -0,0 +1,48 @@ + + 4.0.0 + + com.bitnix + DirtPVP + 1.0 + jar + + DirtPVP + Advanced anti-flight and combat restriction plugin for Paper. + + + 21 + UTF-8 + + + + + papermc + https://repo.papermc.io/repository/maven-public/ + + + + + + io.papermc.paper + paper-api + 1.21.8-R0.1-SNAPSHOT + provided + + + + + DirtPVP + + + org.apache.maven.plugins + maven-compiler-plugin + 3.13.0 + + 21 + + + + + diff --git a/src/main/java/com/bitnix/dirtpvp/DirtPVPPlugin.java b/src/main/java/com/bitnix/dirtpvp/DirtPVPPlugin.java new file mode 100644 index 0000000..779e889 --- /dev/null +++ b/src/main/java/com/bitnix/dirtpvp/DirtPVPPlugin.java @@ -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 combatUntil = new HashMap<>(); + private final Map expiryTasks = new HashMap<>(); + private final Map previousAllowFlight = new HashMap<>(); + private final Set notifyToggledOff = new HashSet<>(); + private final List 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 configuredWorlds; + + private boolean blockedCommandsEnabled; + private Set 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 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 onTabComplete(CommandSender sender, Command command, String alias, String[] args) { + List 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 commands) { + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..971e226 --- /dev/null +++ b/src/main/resources/config.yml @@ -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." diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..24728ef --- /dev/null +++ b/src/main/resources/plugin.yml @@ -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 diff --git a/target/DirtPVP.jar b/target/DirtPVP.jar new file mode 100644 index 0000000000000000000000000000000000000000..64e78b221ea600a69ccd0400e0578cfbe7744e2b GIT binary patch literal 15940 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%0Ah z^HhNg;E5j5B|dGaz-f{8A&Kxn;jdF9W+8|et#yVwjsKz1)GY6WX}5h#-2$;qQ^1|I zPOfshMEn>xgtS^jR-5Y}rNr(8zvKKvHgzTXd0NQrc`$vmo->8JjFm8oTDK*n8Gdel z-vrtp6j*B;u?rmLcmsdPk1CgVncbjQh?kdKg^ZVbWSqQ)9 z_dg_PwuWw|cK=XY{kt>vzdQdHe$oAZWnD!7EAH>2qkohO2}B<=siQNe{mKCh1Oy5W z1Vs6N!aMw~io2~%w949#RR%<#BWi$8jBG9fb)uM7X{DAWeTj|wtCbCiFv?mTUaqhH z(BuG=sj6PsRA#IDVU%;>L7Z}iPMK2b)Hew01?atq%oYI=B+qS$Q(nDpo*j9MABW%S z3tl}^76t^a-1u^4JWlY??>s`ba`4&dghZ7P2ayFm&g~3LJ0293j07V-C34n{bYNi& zPCbURuAp;hs!8?^aFzxODLPn(h;7i_y=bi;0?|~|EMU}nNQdyyF-5eOk*x5p!5VbC zE1hu-{avi`&1ADL5oM~@w@eU>^l`%{Zyao>5es&OO?;uj2jLcS)5*{f&gWg?Nkudy zzLB0IEc(>x#5H-*F#5?jkYs|%7^Up|~YXnS7Ky&#d#`|S|{7qrHMSIqDAdH$LL@&u_7 zVoj;A_~9=b)kqg9#yuEb9PXz@1son4RvDzyN$bCh%dRJE#v_YIsJIbDO^1o$qVo+w z{_K(kTv+kL7tobWKskA*1lZOJ<@K4~BNBc(nc^Isow)nydG~(cN;JJWq`)547rhS} zRgW9$JRg2yNnB0f>QGHo7pnvvr)#|yj-gOCToHWSAu^N}fP zlQu43mX(!b>+-CmSvrxndeNu9Kmqli~k7{2ZL@9Za2EEKQx2-Q*{vX{Bf+WfdjLRIX)d zq$l>2;OQomuYrw(QNV;@s6mveg@wV?QxL(3L5P{F`T_Cd4gLGbQ>fZ0}bC z9RGg&1M!bF<$nhLAr}3c{Q*S!tqA=m;9t_wKMDS`c=S(-27jU71b<0M|7zoZ7Ek_# z^ZdO?{fC18|5D0-E#hD0TYnXy{d*1k{}u5!G1tFx{_9rmFV1|XKREx~*!_k3_b&Y- zMf}YWfj&P2+5Txz{FyNRjPbvN|412sfouL9{JUL$C6ND_=Z_Tf7titk%JWx3`IGNI zr<6Y#8~iEQ|E*E~RIfkz{+?j|iq+qS#=kBVf6*3m{$FYTS~wJ>!G8BP5D@(D&*JY* KxCYlB-~JzpS}|(? literal 0 HcmV?d00001 diff --git a/target/classes/com/bitnix/dirtpvp/DirtPVPPlugin$KillRewardRule.class b/target/classes/com/bitnix/dirtpvp/DirtPVPPlugin$KillRewardRule.class new file mode 100644 index 0000000000000000000000000000000000000000..01bd2eec93c7d4142c888c56d03fc8234bf60e65 GIT binary patch literal 2249 zcmbVOTT|Oc6#mv1WQd5dLr9?^NmEE{kwi&KZxN+!AOsqRxRBw2KFRhD-XKe^w4&`x ze^DQrnKGT>p+BHMs?)QQjOEy+kOz5>kM=v)?;QK@e@^}a@GW*GkU>^MPDdVN3{!9H zBinRr_rToYP0w#JjI9V)$W?}HrMfqc0>(8==(q%(!EAaRvmvA_el}aem))bTxlU(& zuU>b;1K}>b5RS9Mf3p47PUvukoNIUJDA)RqcBL=e1525{tfPo2293LR!{Owe?JIp* z$CXGgx@&FTJXE@?I%aTi%k4+4fp>+b3>G?vs7%JmHT_U8OW#8PV=& z0Ja#ercI}c%)CKbC$AzH^`P9d*=HxiogwAOm>4E@z0hy+4G|S~V8kA%Ng@Vr^FVHT zfz&|lyY+wkCYtpIeCny^$w2ycH?Ct)z}F1(YEZ3Y4y|PFtlq2@X-4Upq^CsBba4#N zCa{6$8a8!o;X8(#{R#I&SBefdV}`fsstWhWJLG13ag%1-;lV=9^A5wV zHB8HtCIP-CZmjd5>5H!P{4`|Ev4bG3NGSiqu#vR)f$=|WtiI*C+>cCh;@suIU8iB2 zVX60O%@`U>~4F7fk}FP#@8$pqg=l z4}jlE%nZd9Ti24fO0zKlj7qbdWRV6>o|Qu%d}SoKbUyG5KBbgY0n(nQ&l7e`xX*|v zpA$TTFYsk_KA~EIe%(~pb&_Jb#$+9&>R0r(Oo^`(^$J+UH}ogvDc0~J(q~Bb1T|vF KOT5DOnEe-{74B^S literal 0 HcmV?d00001 diff --git a/target/classes/com/bitnix/dirtpvp/DirtPVPPlugin.class b/target/classes/com/bitnix/dirtpvp/DirtPVPPlugin.class new file mode 100644 index 0000000000000000000000000000000000000000..efa051c8c79216942476eb1c4286d68f656777c0 GIT binary patch literal 24195 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 b-zU|}_=