Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,14 @@ fair, heart-pounding battles that keep players on their toes. Here’s a rundown
in action:
<img alt="Combat log anti logout feature" src="https://github.com/EternalCodeTeam/EternalCombat/blob/master/assets/combatlog.gif?raw=true" width="420">

- **Support for newest features**
Our plugin features cooldown and delays for tridents, spears and much more. With **out of the box** configurable feats.
<img alt="Combat log anti logout feature" src="https://github.com/EternalCodeTeam/EternalCombat/blob/master/assets/spear.gif?raw=true" width="420">

- **Customize combat experience**
Add custom effects to players in combat or death. Everything should be configurable and user-friendly:
<img alt="Lightning strikes when players die" src="https://github.com/EternalCodeTeam/EternalCombat/blob/master/assets/lightning.gif?raw=true" width="420">

<img alt="Flare indicates players death location" src="https://github.com/EternalCodeTeam/EternalCombat/blob/master/assets/flare.gif?raw=true" width="420">

- **Spawn Protection (Configurable)**
Expand Down
Binary file added assets/spear.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 0 additions & 4 deletions buildSrc/src/main/kotlin/Versions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@ object Versions {

const val PLACEHOLDER_API = "2.12.3"
const val LANDS_API = "7.25.4"
const val WORLDEDIT = "3ISh7ADm" //cannot use numeric version bc of duplicated version on modrinth
const val PACKETEVENTS = "2.11.1"
const val WORLDGUARD = "7.0.15-beta-01"
const val LUCKPERMS = "5.5.17"

}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.eternalcode.combat.fight.spear;

import java.time.Duration;
import java.util.UUID;

public interface SpearService {
boolean isOnCooldown(UUID uuid);
void saveCooldown(UUID uuid);
Duration getRemainingCooldown(UUID uuid);
void removeCooldown(UUID uuid);
}
Comment thread
CitralFlo marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@
import com.eternalcode.combat.fight.pearl.PearlController;
import com.eternalcode.combat.fight.pearl.PearlService;
import com.eternalcode.combat.fight.pearl.PearlServiceImpl;
import com.eternalcode.combat.fight.spear.SpearLungeController;
import com.eternalcode.combat.fight.spear.SpearService;
import com.eternalcode.combat.fight.spear.SpearServiceImpl;
import com.eternalcode.combat.fight.tagout.FightTagOutCommand;
import com.eternalcode.combat.fight.tagout.FightTagOutController;
import com.eternalcode.combat.fight.tagout.FightTagOutService;
Expand Down Expand Up @@ -128,7 +131,7 @@
this.dropService = new DropServiceImpl();
this.dropKeepInventoryService = new DropKeepInventoryServiceImpl();

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

Check warning on line 134 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 All @@ -150,6 +153,8 @@
BorderService borderService = new BorderServiceImpl(scheduler, server, regionProvider, eventManager, () -> pluginConfig.border);
KnockbackService knockbackService = new KnockbackService(pluginConfig, scheduler, regionProvider);

SpearService spearService = new SpearServiceImpl(pluginConfig.spear);

this.liteCommands = LiteBukkitFactory.builder(FALLBACK_PREFIX, this, server)
.message(LiteBukkitMessages.PLAYER_NOT_FOUND, pluginConfig.messagesSettings.playerNotFound)
.message(LiteBukkitMessages.PLAYER_ONLY, pluginConfig.messagesSettings.admin.onlyForPlayers)
Expand Down Expand Up @@ -214,6 +219,8 @@

new KnockbackMountController(noticeService, this.regionProvider, this.fightManager).register(this);

new SpearLungeController(this, fightManager, spearService, pluginConfig.spear, noticeService);

eventManager.subscribe(
PlayerDeathEvent.class,
pluginConfig.drop.dropEventPriority,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.eternalcode.combat.fight.effect.FightEffectSettings;
import com.eternalcode.combat.fight.knockback.KnockbackSettings;
import com.eternalcode.combat.fight.pearl.PearlSettings;
import com.eternalcode.combat.fight.spear.SpearSettings;
import com.eternalcode.combat.fight.trident.TridentSettings;
import eu.okaeri.configs.OkaeriConfig;
import eu.okaeri.configs.annotation.Comment;
Expand Down Expand Up @@ -44,6 +45,13 @@ public class PluginConfig extends OkaeriConfig {
})
public TridentSettings trident = new TridentSettings();

@Comment({
" ",
"# Settings related to Spears with lunge",
"# Set cooldown for spear lunging"
})
public SpearSettings spear = new SpearSettings();

@Comment({
" ",
"# Custom effects applied during combat.",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package com.eternalcode.combat.fight.spear;

import com.eternalcode.combat.fight.FightManager;
import com.eternalcode.combat.notification.NoticeService;
import com.eternalcode.combat.util.DurationUtil;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.plugin.Plugin;
Comment thread
CitralFlo marked this conversation as resolved.

import java.lang.reflect.Method;
import java.time.Duration;
import java.util.UUID;

public class SpearLungeController implements Listener {

private final SpearService spearService;

public SpearLungeController(Plugin plugin, FightManager fightManager, SpearService spearService, SpearSettings settings, NoticeService noticeService) {
this.spearService = spearService;


Bukkit.getPluginManager().registerEvents(this, plugin);

try {
Class<? extends Event> lungeEventClass = (Class<? extends Event>) Class.forName("io.papermc.paper.event.entity.EntityLungeEvent");

Method getEntityMethod = lungeEventClass.getMethod("getEntity");
Method setCancelledMethod = lungeEventClass.getMethod("setCancelled", boolean.class);

Bukkit.getPluginManager().registerEvent(
lungeEventClass,
this,
EventPriority.NORMAL,
(listener, event) -> {
if (!settings.lungeCooldown) return;
if (!lungeEventClass.isInstance(event)) return;

try {
Object entity = getEntityMethod.invoke(event);

if (entity instanceof Player player) {
UUID uuid = player.getUniqueId();

if (settings.onlyForFight && !fightManager.isInCombat(uuid)) {
return;
}

// Query the remaining cooldown once to avoid redundant lookups and race conditions
Duration remaining = spearService.getRemainingCooldown(uuid);

if (!remaining.isZero() && !remaining.isNegative()) {
setCancelledMethod.invoke(event, true);

noticeService.create()
.player(uuid)
.notice(settings.lungeOnCooldown)
.placeholder("{TIME}", DurationUtil.format(remaining, !settings.useMillis))
.send();
} else {
spearService.saveCooldown(uuid);
}
}
} catch (Exception e) {
plugin.getLogger().warning("Failed to handle EntityLungeEvent reflectively: " + e.getMessage());
}
},
plugin
);
} catch (ClassNotFoundException e) {
plugin.getLogger().info("EntityLungeEvent not found, skipping spear lunge cooldown registration.");
} catch (NoSuchMethodException e) {
plugin.getLogger().warning("Failed to find necessary methods for EntityLungeEvent: " + e.getMessage());
}
}
Comment on lines +3 to +79

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
import com.eternalcode.combat.fight.FightManager;
import com.eternalcode.combat.notification.NoticeService;
import com.eternalcode.combat.util.DurationUtil;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.plugin.Plugin;
import java.lang.reflect.Method;
import java.time.Duration;
import java.util.UUID;
public class SpearLungeController implements Listener {
private final SpearService spearService;
public SpearLungeController(Plugin plugin, FightManager fightManager, SpearService spearService, SpearSettings settings, NoticeService noticeService) {
this.spearService = spearService;
Bukkit.getPluginManager().registerEvents(this, plugin);
try {
Class<? extends Event> lungeEventClass = (Class<? extends Event>) Class.forName("io.papermc.paper.event.entity.EntityLungeEvent");
Method getEntityMethod = lungeEventClass.getMethod("getEntity");
Method setCancelledMethod = lungeEventClass.getMethod("setCancelled", boolean.class);
Bukkit.getPluginManager().registerEvent(
lungeEventClass,
this,
EventPriority.NORMAL,
(listener, event) -> {
if (!settings.lungeCooldown) return;
if (!lungeEventClass.isInstance(event)) return;
try {
Object entity = getEntityMethod.invoke(event);
if (entity instanceof Player player) {
UUID uuid = player.getUniqueId();
if (settings.onlyForFight && !fightManager.isInCombat(uuid)) {
return;
}
// Query the remaining cooldown once to avoid redundant lookups and race conditions
Duration remaining = spearService.getRemainingCooldown(uuid);
if (!remaining.isZero() && !remaining.isNegative()) {
setCancelledMethod.invoke(event, true);
noticeService.create()
.player(uuid)
.notice(settings.lungeOnCooldown)
.placeholder("{TIME}", DurationUtil.format(remaining, !settings.useMillis))
.send();
} else {
spearService.saveCooldown(uuid);
}
}
} catch (Exception e) {
plugin.getLogger().warning("Failed to handle EntityLungeEvent reflectively: " + e.getMessage());
}
},
plugin
);
} catch (ClassNotFoundException e) {
plugin.getLogger().info("EntityLungeEvent not found, skipping spear lunge cooldown registration.");
} catch (NoSuchMethodException e) {
plugin.getLogger().warning("Failed to find necessary methods for EntityLungeEvent: " + e.getMessage());
}
}
import com.eternalcode.combat.fight.FightManager;
import com.eternalcode.combat.notification.NoticeService;
import com.eternalcode.combat.util.DurationUtil;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.plugin.Plugin;
import java.time.Duration;
import java.util.UUID;
public class SpearLungeController implements Listener {
private final SpearService spearService;
public SpearLungeController(Plugin plugin, FightManager fightManager, SpearService spearService, SpearSettings settings, NoticeService noticeService) {
this.spearService = spearService;
Bukkit.getPluginManager().registerEvents(this, plugin);
try {
Class<? extends Event> lungeEventClass = Class.forName("io.papermc.paper.event.entity.EntityLungeEvent").asSubclass(EntityEvent.class);
Bukkit.getPluginManager().registerEvent(
lungeEventClass,
this,
EventPriority.NORMAL,
(listener, event) -> {
if (!settings.lungeCooldown) return;
if (!(event instanceof EntityEvent entityEvent)) return;
if (!(entityEvent.getEntity() instanceof Player player)) return;
UUID uuid = player.getUniqueId();
if (settings.onlyForFight && !fightManager.isInCombat(uuid)) return;
Duration remaining = spearService.getRemainingCooldown(uuid);
if (!remaining.isZero() && !remaining.isNegative()) {
((Cancellable) event).setCancelled(true);
noticeService.create()
.player(uuid)
.notice(settings.lungeOnCooldown)
.placeholder("{TIME}", DurationUtil.format(remaining, !settings.useMillis))
.send();
} else {
spearService.saveCooldown(uuid);
}
},
plugin
);
} catch (ClassNotFoundException e) {
plugin.getLogger().info("EntityLungeEvent not found, skipping spear lunge cooldown registration.");
}
}


@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
this.spearService.removeCooldown(event.getPlayer().getUniqueId());
}
}
Comment thread
CitralFlo marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.eternalcode.combat.fight.spear;

import java.time.Duration;
import java.time.Instant;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

public class SpearServiceImpl implements SpearService {

private final SpearSettings settings;
private final Map<UUID, Instant> cooldowns = new ConcurrentHashMap<>();

public SpearServiceImpl(SpearSettings settings) {
this.settings = settings;
}

@Override
public boolean isOnCooldown(UUID uuid) {
return !this.getRemainingCooldown(uuid).isZero();
}

@Override
public void saveCooldown(UUID uuid) {
this.cooldowns.put(uuid, Instant.now().plus(this.settings.lungeCooldownDuration));
}

@Override
public Duration getRemainingCooldown(UUID uuid) {
Instant expiration = this.cooldowns.get(uuid);

if (expiration == null) {
return Duration.ZERO;
}

Instant now = Instant.now();
if (now.isAfter(expiration)) {
this.cooldowns.remove(uuid);
return Duration.ZERO;
}

return Duration.between(now, expiration);
}

@Override
public void removeCooldown(UUID uuid) {
this.cooldowns.remove(uuid);
}
}
Comment thread
CitralFlo marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.eternalcode.combat.fight.spear;

import com.eternalcode.multification.notice.Notice;
import eu.okaeri.configs.OkaeriConfig;
import eu.okaeri.configs.annotation.Comment;
import java.time.Duration;

public class SpearSettings extends OkaeriConfig {

@Comment("# Should spears have lunge cooldown or delay between uses? True for cooldown, False for vanilla mechanics")
public boolean lungeCooldown = false;

@Comment("# Should cooldown be applied only during fights")
public boolean onlyForFight = true;

@Comment("# Duration of cooldown")
public Duration lungeCooldownDuration = Duration.ofSeconds(5);

@Comment("# Should milliseconds be used for last second of cooldown for more precise time")
public boolean useMillis = true;

@Comment({
"# Notice sent to the players that try to use spears before cooldown ends.",
"# Placeholder: {TIME} - time left of cooldown"
})
public Notice lungeOnCooldown = Notice.builder().actionBar("<dark_red>Spear cannot be used for next <red>{TIME}</red></dark_red>")
.build();

}
Loading