Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
8dfe353
WIP: Post death command execution
Jakubk15 Feb 20, 2026
e333117
Post death command execution
Jakubk15 Feb 20, 2026
16f45ca
Apply suggestions from code review
Jakubk15 Feb 20, 2026
13d9c09
fix build
Jakubk15 Feb 20, 2026
911ef7b
adjust to gemini's suggestion
Jakubk15 Feb 20, 2026
ce87394
Remove CauseOfTag.LOGOUT from the check
Jakubk15 Feb 20, 2026
356481e
Update eternalcombat-plugin/src/main/java/com/eternalcode/combat/figh…
Jakubk15 Mar 15, 2026
2a99c85
Update eternalcombat-plugin/src/main/java/com/eternalcode/combat/figh…
Jakubk15 Mar 15, 2026
320dd90
Adjust to Mike's suggestions. Simplify logic
Jakubk15 Mar 15, 2026
903fdaa
Merge remote-tracking branch 'origin/feature/post-death-commands' int…
Jakubk15 Mar 15, 2026
cb015b5
Merge branch 'master' into feature/post-death-commands
Jakubk15 Mar 15, 2026
15b2267
fix build
Jakubk15 Mar 22, 2026
115f8bb
Change EternalCore version to latest stable
Jakubk15 Mar 22, 2026
a606358
Adjust to DMK's suggestion
Jakubk15 Mar 22, 2026
dc0724b
Move to DeathSettings
Jakubk15 Mar 22, 2026
81afc4c
Merge branch 'master' into feature/post-death-commands
Jakubk15 May 10, 2026
bc83f4d
Merge branch 'master' into feature/post-death-commands
Jakubk15 Jul 9, 2026
34b1252
Fix build
Jakubk15 Jul 9, 2026
6b09a6f
Add additional comments per Mike's suggestion
Jakubk15 Jul 9, 2026
7bc712d
Update eternalcombat-plugin/src/main/java/com/eternalcode/combat/figh…
Jakubk15 Jul 25, 2026
ca25b99
Update eternalcombat-plugin/src/main/java/com/eternalcode/combat/figh…
Jakubk15 Jul 25, 2026
b9d6a5b
Update placeholders in settings
Jakubk15 Jul 25, 2026
ca05679
Add scheduler support for post-death command execution, improve logic…
Jakubk15 Jul 25, 2026
fd94fe1
Simplify whole code.
vLuckyyy Jul 26, 2026
f6f3e3b
Simplify config declaration.
vLuckyyy Jul 26, 2026
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 @@ -30,6 +30,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.0"

}

Expand Down
2 changes: 1 addition & 1 deletion eternalcombat-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

import net.minecrell.pluginyml.paper.PaperPluginDescription
import net.minecrell.pluginyml.bukkit.BukkitPluginDescription
import net.minecrell.pluginyml.paper.PaperPluginDescription

plugins {
`eternalcombat-java`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,16 @@
import com.eternalcode.combat.fight.blocker.ElytraBlocker;
import com.eternalcode.combat.fight.blocker.ElytraEquipBlocker;
import com.eternalcode.combat.fight.blocker.FlyingBlocker;
import com.eternalcode.combat.fight.blocker.InventoryContainersBlocker;
import com.eternalcode.combat.fight.blocker.PlaceBlockBlocker;
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.blocker.InventoryContainersBlocker;
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.DeathCommandDispatcher;
import com.eternalcode.combat.fight.death.DeathFlareController;
import com.eternalcode.combat.fight.death.DeathLightningController;
import com.eternalcode.combat.fight.drop.DropController;
Expand All @@ -35,7 +38,6 @@
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.blocker.PlaceBlockBlocker;
import com.eternalcode.combat.fight.effect.FightEffectController;
import com.eternalcode.combat.fight.effect.FightEffectService;
import com.eternalcode.combat.fight.effect.FightEffectServiceImpl;
Expand Down Expand Up @@ -70,7 +72,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.text.minimessage.MiniMessage;
import org.bstats.bukkit.Metrics;
import org.bukkit.Server;
Expand All @@ -80,6 +81,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 @@ -128,7 +130,7 @@
this.dropService = new DropServiceImpl();
this.dropKeepInventoryService = new DropKeepInventoryServiceImpl();

UpdaterService updaterService = new UpdaterService(this.getDescription());

Check warning on line 133 in eternalcombat-plugin/src/main/java/com/eternalcode/combat/CombatPlugin.java

View workflow job for this annotation

GitHub Actions / build

[deprecation] getDescription() in JavaPlugin has been deprecated

MiniMessage miniMessage = MiniMessage.builder()
.postProcessor(new AdventureLegacyColorPostProcessor())
Expand Down Expand Up @@ -182,12 +184,15 @@
new PlayersHealthDropModifier(pluginConfig.drop, logoutService)
).forEach(this.dropService::registerModifier);

DeathCommandDispatcher deathCommandDispatcher = new DeathCommandDispatcher(server);

eventManager.subscribe(
new FightTagController(this.fightManager, pluginConfig),
new FightUnTagController(this.fightManager, pluginConfig, logoutService),
new FightBypassAdminController(server, pluginConfig),
new FightBypassPermissionController(server, pluginConfig),
new FightBypassCreativeController(server, pluginConfig),
new DeathCommandController(pluginConfig, this.fightManager, deathCommandDispatcher, scheduler, server),
new PlaceBlockBlocker(this.fightManager, noticeService, pluginConfig),
new PearlController(pluginConfig, this.pearlService, noticeService, fightManager),
new TridentController(pluginConfig, noticeService, this.fightManager, this.tridentService, server),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ public class PluginConfig extends OkaeriConfig {

@Comment({
" ",
"# This section contains effects displayed on death of the player"
"# This section contains effects displayed on death of the player",
"# You can also execute which commands will be executed post-death and on logout of the player."
})
public DeathSettings death = new DeathSettings();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
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 com.eternalcode.commons.scheduler.Scheduler;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import java.time.Duration;
import java.util.UUID;
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.PlayerQuitEvent;
import org.bukkit.event.player.PlayerRespawnEvent;

public final class DeathCommandController implements Listener {

private final Cache<UUID, String> killerNames = Caffeine.newBuilder()
.expireAfterWrite(Duration.ofMinutes(2))
.build();

private final PluginConfig config;
private final FightManager fightManager;
private final DeathCommandDispatcher dispatcher;
private final Scheduler scheduler;
private final Server server;

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

@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
void onUntag(FightUntagEvent event) {
Player player = this.server.getPlayer(event.getPlayer());

if (player == null) {
return;
}

if (!this.isDeath(event.getCause())) {
this.scheduler.run(() -> this.dispatchUntag(player));
return;
}

Player killer = this.resolveKiller(player);
String killerName = killer != null ? killer.getName() : this.unknownKiller();

this.killerNames.put(player.getUniqueId(), killerName);
this.dispatcher.dispatch(this.config.death.onDeathInCombat, player, killerName);

if (killer != null) {
this.dispatcher.dispatch(
this.config.death.killerPostDeathCommands,
killer,
player.getName(),
killerName
);
}
}

@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
void onDeath(PlayerDeathEvent event) {
Player player = event.getEntity();

this.dispatcher.dispatch(
this.config.death.onAnyDeath,
player,
this.killerNames.get(player.getUniqueId(), ignored -> this.resolveKillerName(player))
);
}

@EventHandler(priority = EventPriority.MONITOR)
void onRespawn(PlayerRespawnEvent event) {
Player player = event.getPlayer();
String killerName = this.killerNames.asMap().remove(player.getUniqueId());

this.dispatcher.dispatch(
this.config.death.afterRespawn,
player,
killerName != null ? killerName : this.unknownKiller()
);
}

@EventHandler
void onQuit(PlayerQuitEvent event) {
this.killerNames.invalidate(event.getPlayer().getUniqueId());
}

private Player resolveKiller(Player player) {
Player killer = player.getKiller();

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

FightTag tag = this.fightManager.getTag(player.getUniqueId());
return tag == null ? null : this.server.getPlayer(tag.getTagger());
}

private String resolveKillerName(Player player) {
Player killer = this.resolveKiller(player);
return killer != null ? killer.getName() : this.unknownKiller();
}

private void dispatchUntag(Player player) {
if (player.isOnline()) {
this.dispatcher.dispatch(
this.config.death.onUntag,
player,
this.unknownKiller()
);
}
}

private boolean isDeath(CauseOfUnTag cause) {
return cause == CauseOfUnTag.DEATH || cause == CauseOfUnTag.DEATH_BY_PLAYER;
}

private String unknownKiller() {
return this.config.death.unknownKillerPlaceholder;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.eternalcode.combat.fight.death;

import org.bukkit.Server;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;

import java.util.Collection;

public class DeathCommandDispatcher {

private final Server server;

public DeathCommandDispatcher(Server server) {
this.server = server;
}

public void dispatch(DeathSettings.PostDeathCommands settings, Player player, String killerName) {
String playerName = player.getName();

this.dispatch(settings.console, this.server.getConsoleSender(), playerName, killerName);
this.dispatch(settings.player, player, playerName, killerName);
}

public void dispatch(Collection<String> commands, CommandSender sender, String playerName, String killerName) {
for (String command : commands) {
String trimmed = command.trim();

if (trimmed.isEmpty()) {
continue;
}

if (trimmed.startsWith("/")) {
trimmed = trimmed.substring(1);
}

String resolved = this.replacePlaceholders(trimmed, playerName, killerName);
this.server.dispatchCommand(sender, resolved);
}
}

private String replacePlaceholders(String command, String playerName, String killerName) {
return command
.replace("{PLAYER}", playerName)
.replace("{KILLER}", killerName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import eu.okaeri.configs.annotation.Comment;
import org.bukkit.FireworkEffect;

import java.util.List;

public class DeathSettings extends OkaeriConfig {

@Comment({
Expand Down Expand Up @@ -74,4 +76,37 @@ public static class FlareSettings extends OkaeriConfig {
public int secondaryParticleCount = 3;

}

@Comment({
"Commands that will be executed after a player's death/respawn/untag.",
"You can use {PLAYER} to represent the name of the player who died and {KILLER} for the killer's name (if applicable).",
"Do not include the leading slash (/) in the commands.",
"",
"Commands executed when a player dies in combat"
})
public PostDeathCommands onDeathInCombat = new PostDeathCommands();

@Comment("Commands executed on any player death")
public PostDeathCommands onAnyDeath = new PostDeathCommands();

Comment thread
Jakubk15 marked this conversation as resolved.
@Comment("Commands executed after a player respawns")
public PostDeathCommands afterRespawn = new PostDeathCommands();

@Comment("Commands executed when a player is untagged from combat")
public PostDeathCommands onUntag = new PostDeathCommands();

public static class PostDeathCommands extends OkaeriConfig {
public List<String> console = List.of();
public List<String> player = List.of();
}

@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).",
"Example: 'give {KILLER} diamond 1' will give the killer a diamond after killing a player."
})
public List<String> killerPostDeathCommands = List.of();

@Comment("The returned string when the killer is unknown")
public String unknownKillerPlaceholder = "Unknown";
}
Loading