Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions buildSrc/src/main/kotlin/Versions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ object Versions {
const val PACKETEVENTS = "2.11.1"
const val WORLDGUARD = "7.0.15-beta-01"
const val LUCKPERMS = "5.5.17"
const val ETERNALCORE = "2.0.1-SNAPSHOT+12"
Comment thread
Jakubk15 marked this conversation as resolved.
Outdated
Comment thread
Jakubk15 marked this conversation as resolved.
Outdated
Comment thread
Jakubk15 marked this conversation as resolved.
Outdated

}

Expand Down
17 changes: 10 additions & 7 deletions eternalcombat-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import net.minecrell.pluginyml.bukkit.BukkitPluginDescription

import io.papermc.hangarpublishplugin.model.Platforms
import org.gradle.kotlin.dsl.shadowJar
import net.minecrell.pluginyml.bukkit.BukkitPluginDescription
Comment thread
Jakubk15 marked this conversation as resolved.

plugins {
`eternalcombat-java`
Expand Down Expand Up @@ -93,11 +93,14 @@ bukkit {

tasks {
runServer {
minecraftVersion("1.21.10")
downloadPlugins.modrinth("WorldEdit", Versions.WORLDEDIT)
downloadPlugins.modrinth("PacketEvents", "${Versions.PACKETEVENTS}+spigot")
downloadPlugins.modrinth("WorldGuard", Versions.WORLDGUARD)
downloadPlugins.modrinth("LuckPerms", "v${Versions.LUCKPERMS}-bukkit")
minecraftVersion("1.21.11")
Comment thread
Jakubk15 marked this conversation as resolved.
downloadPlugins {
modrinth("WorldEdit", Versions.WORLDEDIT)
modrinth("PacketEvents", "${Versions.PACKETEVENTS}+spigot")
modrinth("WorldGuard", Versions.WORLDGUARD)
modrinth("LuckPerms", "v${Versions.LUCKPERMS}-bukkit")
modrinth("EternalCore", Versions.ETERNALCORE)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,54 +1,55 @@
package com.eternalcode.combat;

import com.eternalcode.combat.border.BorderTriggerController;
import com.eternalcode.combat.border.BorderService;
import com.eternalcode.combat.border.BorderServiceImpl;
import com.eternalcode.combat.border.BorderTriggerController;
import com.eternalcode.combat.border.animation.block.BorderBlockController;
import com.eternalcode.combat.border.animation.particle.ParticleController;
import com.eternalcode.combat.bridge.BridgeService;
import com.eternalcode.combat.crystalpvp.RespawnAnchorListener;
import com.eternalcode.combat.config.ConfigService;
import com.eternalcode.combat.config.implementation.PluginConfig;
import com.eternalcode.combat.crystalpvp.EndCrystalListener;
import com.eternalcode.combat.crystalpvp.RespawnAnchorListener;
import com.eternalcode.combat.event.EventManager;
import com.eternalcode.combat.fight.FightManager;
import com.eternalcode.combat.fight.FightManagerImpl;
import com.eternalcode.combat.fight.FightTagCommand;
import com.eternalcode.combat.fight.FightTask;
import com.eternalcode.combat.fight.controller.FightActionBlockerController;
import com.eternalcode.combat.fight.controller.FightBypassAdminController;
import com.eternalcode.combat.fight.controller.FightBypassCreativeController;
import com.eternalcode.combat.fight.controller.FightBypassPermissionController;
import com.eternalcode.combat.fight.controller.FightInventoryController;
import com.eternalcode.combat.fight.controller.FightMessageController;
import com.eternalcode.combat.fight.controller.FightTagController;
import com.eternalcode.combat.fight.controller.FightUnTagController;
import com.eternalcode.combat.fight.death.DeathCommandController;
import com.eternalcode.combat.fight.death.DeathEffectController;
import com.eternalcode.combat.fight.drop.DropKeepInventoryService;
import com.eternalcode.combat.fight.FightManager;
import com.eternalcode.combat.fight.drop.DropService;
import com.eternalcode.combat.fight.effect.FightEffectService;
import com.eternalcode.combat.fight.firework.FireworkController;
import com.eternalcode.combat.fight.knockback.KnockbackService;
import com.eternalcode.combat.fight.tagout.FightTagOutService;
import com.eternalcode.combat.fight.pearl.FightPearlService;
import com.eternalcode.combat.handler.InvalidUsageHandlerImpl;
import com.eternalcode.combat.handler.MissingPermissionHandlerImpl;
import com.eternalcode.combat.config.ConfigService;
import com.eternalcode.combat.config.implementation.PluginConfig;
import com.eternalcode.combat.fight.drop.DropController;
import com.eternalcode.combat.fight.drop.DropKeepInventoryService;
import com.eternalcode.combat.fight.drop.DropKeepInventoryServiceImpl;
import com.eternalcode.combat.fight.drop.DropService;
import com.eternalcode.combat.fight.drop.DropServiceImpl;
import com.eternalcode.combat.fight.drop.impl.PercentDropModifier;
import com.eternalcode.combat.fight.drop.impl.PlayersHealthDropModifier;
import com.eternalcode.combat.fight.FightTagCommand;
import com.eternalcode.combat.fight.controller.FightActionBlockerController;
import com.eternalcode.combat.fight.controller.FightMessageController;
import com.eternalcode.combat.fight.controller.FightTagController;
import com.eternalcode.combat.fight.controller.FightUnTagController;
import com.eternalcode.combat.fight.effect.FightEffectController;
import com.eternalcode.combat.event.EventManager;
import com.eternalcode.combat.fight.FightManagerImpl;
import com.eternalcode.combat.fight.FightTask;
import com.eternalcode.combat.fight.effect.FightEffectService;
import com.eternalcode.combat.fight.effect.FightEffectServiceImpl;
import com.eternalcode.combat.fight.firework.FireworkController;
import com.eternalcode.combat.fight.knockback.KnockbackRegionController;
import com.eternalcode.combat.fight.knockback.KnockbackService;
import com.eternalcode.combat.fight.logout.LogoutController;
import com.eternalcode.combat.fight.logout.LogoutService;
import com.eternalcode.combat.fight.pearl.FightPearlController;
import com.eternalcode.combat.fight.pearl.FightPearlService;
import com.eternalcode.combat.fight.pearl.FightPearlServiceImpl;
import com.eternalcode.combat.fight.tagout.FightTagOutCommand;
import com.eternalcode.combat.fight.tagout.FightTagOutController;
import com.eternalcode.combat.fight.tagout.FightTagOutService;
import com.eternalcode.combat.fight.tagout.FightTagOutServiceImpl;
import com.eternalcode.combat.fight.tagout.FightTagOutCommand;
import com.eternalcode.combat.handler.InvalidUsageHandlerImpl;
import com.eternalcode.combat.handler.MissingPermissionHandlerImpl;
import com.eternalcode.combat.notification.NoticeService;
import com.eternalcode.combat.fight.knockback.KnockbackRegionController;
import com.eternalcode.combat.region.RegionProvider;
import com.eternalcode.combat.updater.UpdaterNotificationController;
import com.eternalcode.combat.updater.UpdaterService;
Expand All @@ -61,7 +62,6 @@
import dev.rollczi.litecommands.bukkit.LiteBukkitFactory;
import dev.rollczi.litecommands.bukkit.LiteBukkitMessages;
import dev.rollczi.litecommands.folia.FoliaExtension;
import java.time.Duration;
import net.kyori.adventure.platform.AudienceProvider;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.minimessage.MiniMessage;
Expand All @@ -73,6 +73,7 @@
import org.bukkit.plugin.java.JavaPlugin;

import java.io.File;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;

Expand Down Expand Up @@ -183,6 +184,7 @@ public void onEnable() {
new FightActionBlockerController(this.fightManager, noticeService, pluginConfig, server),
new FightPearlController(pluginConfig.pearl, noticeService, this.fightManager, this.fightPearlService),
new DeathEffectController(pluginConfig),
new DeathCommandController(pluginConfig, this.fightManager, server),
new UpdaterNotificationController(updaterService, pluginConfig, this.audienceProvider, miniMessage),
new KnockbackRegionController(noticeService, this.regionProvider, this.fightManager, knockbackService, server),
new FightEffectController(pluginConfig.effect, this.fightEffectService, this.fightManager, server),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,40 @@ public class CommandSettings extends OkaeriConfig {
"tpa",
"tpaccept"
);

@Comment({
"# List of commands that will be executed from console after player death.",
"# Use {player} to represent the name of the player who died and {killer} for the killer's name (if applicable)."
})
public List<String> consolePostDeathCommands = List.of(
Comment thread
Jakubk15 marked this conversation as resolved.
Outdated
"broadcast {player} has died in combat!"
Comment thread
Jakubk15 marked this conversation as resolved.
Outdated
);

@Comment("# When this is set to true, the plugin will execute the console commands only after the dead player has respawned.")
public boolean deferConsoleAfterRespawn = false;

@Comment({
"# List of commands that will be executed from the dead player's perspective after death.",
"# Use {player} to represent the name of the player who died and {killer} for the killer's name (if applicable)."
})
public List<String> deadPostDeathCommands = List.of(
"say You have died in combat!"
);

@Comment("# When this is set to true, the plugin will execute the commands above only after the dead player has respawned.")
public boolean deferDeadAfterRespawn = true;

@Comment({
"# List of commands that will be executed from the killer's perspective after killing a player.",
"# Use {player} to represent the name of the player who was killed and {killer} for the killer's name (if applicable)."
})
public List<String> killerPostDeathCommands = List.of(
"say You have killed {player} in combat!"
);

@Comment("# When this is set to true, the plugin will only execute the post-death commands if the players were tagged")
public boolean onlyExecuteIfTagged = true;

@Comment("# The returned string when the killer is unknown")
Comment thread
Jakubk15 marked this conversation as resolved.
Outdated
public String unknownKillerPlaceholder = "Unknown";
}
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ public class PluginConfig extends OkaeriConfig {
@Comment({
" ",
"# Settings related to commands during combat.",
"# Configure command restrictions and behaviors for players in combat."
"# Configure command restrictions and behaviors for players in combat.",
"# You can also execute which commands will be executed post-death and on logout of the player."
})
public CommandSettings commands = new CommandSettings();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package com.eternalcode.combat.fight.death;
Comment thread
Jakubk15 marked this conversation as resolved.

import com.eternalcode.combat.config.implementation.PluginConfig;
import com.eternalcode.combat.fight.FightManager;
import com.eternalcode.combat.fight.FightTag;
import com.eternalcode.combat.fight.event.CauseOfUnTag;
import com.eternalcode.combat.fight.event.FightUntagEvent;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerRespawnEvent;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

public class DeathCommandController implements Listener {
Comment thread
Jakubk15 marked this conversation as resolved.

private final PluginConfig config;
private final FightManager fightManager;
private final Server server;

private final Map<UUID, List<PendingCommand>> pendingCommands = new ConcurrentHashMap<>();
private final Set<UUID> handledByUntag = Collections.newSetFromMap(new ConcurrentHashMap<>());

public DeathCommandController(PluginConfig config, FightManager fightManager, Server server) {
this.config = config;
this.fightManager = fightManager;
this.server = server;
}

@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
void onPlayerUntag(FightUntagEvent event) {
UUID playerUUID = event.getPlayer();
CauseOfUnTag cause = event.getCause();

if (cause != CauseOfUnTag.DEATH && cause != CauseOfUnTag.DEATH_BY_PLAYER) {
return;
}

Player deadPlayer = this.server.getPlayer(playerUUID);

if (deadPlayer == null) {
return;
}

this.handledByUntag.add(playerUUID);
this.executeDeathCommands(deadPlayer);
}

@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
void onPlayerDeath(PlayerDeathEvent event) {
if (this.config.commands.onlyExecuteIfTagged) {
return;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The onlyExecuteIfTagged check is performed here, but it's only for PlayerDeathEvent. If onPlayerUntag is also meant to respect this setting, the check should be moved to executeDeathCommands or duplicated in onPlayerUntag to ensure consistent behavior.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested it and I wasn't able to produce any bugs, however I encourage other team members to try it nevertheless


Player deadPlayer = event.getEntity();
UUID playerUUID = deadPlayer.getUniqueId();

if (this.handledByUntag.remove(playerUUID)) {
return;
}

this.executeDeathCommands(deadPlayer);
}

@EventHandler(priority = EventPriority.MONITOR)
void onPlayerRespawn(PlayerRespawnEvent event) {
Player player = event.getPlayer();
UUID playerUUID = player.getUniqueId();

this.handledByUntag.remove(playerUUID);

List<PendingCommand> commands = this.pendingCommands.remove(playerUUID);

if (commands == null) {
return;
}

for (PendingCommand pending : commands) {
switch (pending.executor()) {
case CONSOLE -> this.server.dispatchCommand(this.server.getConsoleSender(), pending.command());
case DEAD_PLAYER -> this.server.dispatchCommand(player, pending.command());
}
}
}

private void executeDeathCommands(Player deadPlayer) {
Comment thread
Jakubk15 marked this conversation as resolved.
Outdated
UUID playerUUID = deadPlayer.getUniqueId();
String deadPlayerName = deadPlayer.getName();
String killerName = this.resolveKillerName(playerUUID, deadPlayer);

List<PendingCommand> deferred = new ArrayList<>();

for (String command : this.config.commands.consolePostDeathCommands) {
String resolved = this.replacePlaceholders(command, deadPlayerName, killerName);
if (this.config.commands.deferConsoleAfterRespawn) {
deferred.add(new PendingCommand(CommandSource.CONSOLE, resolved));
} else {
this.server.dispatchCommand(this.server.getConsoleSender(), resolved);
}
}

for (String command : this.config.commands.deadPostDeathCommands) {
String resolved = this.replacePlaceholders(command, deadPlayerName, killerName);
Comment thread
Jakubk15 marked this conversation as resolved.
Outdated
if (this.config.commands.deferDeadAfterRespawn) {
deferred.add(new PendingCommand(CommandSource.DEAD_PLAYER, resolved));
} else {
this.server.dispatchCommand(deadPlayer, resolved);
}
}

Player killer = this.resolveKiller(playerUUID, deadPlayer);

if (killer != null) {
for (String command : this.config.commands.killerPostDeathCommands) {
String resolved = this.replacePlaceholders(command, deadPlayerName, killerName);
this.server.dispatchCommand(killer, resolved);
}
}

if (!deferred.isEmpty()) {
this.pendingCommands.put(playerUUID, deferred);
}
}


private String resolveKillerName(UUID deadPlayerUUID, Player deadPlayer) {
Player killer = this.resolveKiller(deadPlayerUUID, deadPlayer);
return killer != null ? killer.getName() : this.config.commands.unknownKillerPlaceholder;
Comment thread
Jakubk15 marked this conversation as resolved.
Outdated
}

private Player resolveKiller(UUID deadPlayerUUID, Player deadPlayer) {
Player killer = deadPlayer.getKiller();

if (killer != null) {
return killer;
}

FightTag tag = this.fightManager.getTag(deadPlayerUUID);

if (tag != null && tag.getTagger() != null) {
return this.server.getPlayer(tag.getTagger());
}

return null;
}

private String replacePlaceholders(String command, String playerName, String killerName) {
return command
.replace("{player}", playerName)
.replace("{killer}", killerName);
Comment thread
Jakubk15 marked this conversation as resolved.
Outdated
}

private enum CommandSource {
CONSOLE,
DEAD_PLAYER
}

private record PendingCommand(CommandSource executor, String command) {
Comment thread
Jakubk15 marked this conversation as resolved.
Outdated
}
}