Compare commits

...
Author SHA1 Message Date
Michał Wojtas 520196417b Use already defined versions of plugins in runServer script
Signed-off-by: Michał Wojtas <wojtas.michal90@gmail.com>
2026-06-21 12:17:23 +02:00
renovate[bot] fb6db7a008 dependency: Update Gradle to v9.6.0 (#397)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-20 14:14:16 +02:00
renovate[bot] 7d8a01b7d7 dependency: Update Versions.LITE_COMMANDS to v3.11.0 (#398)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-20 14:13:50 +02:00
renovate[bot] 08c7482cee dependency: Update actions/checkout action to v7 (#399)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-20 14:12:36 +02:00
7e24e5afa6 GH-266 New features for Trident and Pearls (#303)
* I've added new options to Pearl and Trident that reset the timer when we use them

* I made the corrections that gemini noticed

* The improvements suggested by the team have been implemented

* Implemented suggestions from the team

* Make tridents throw check work on all tridents, regardless of enchantment

* Implemented suggestions from the team and fixed `FightTridentController` logic

* Corrected comments and field names

* Implemented CitralFlo suggestions

* Implemented CitralFlo suggestions again

* Follow igoyek's review from 21.10.25

* CR

* Simplify naming - extract logic to service for Pearls

* make it more logical

* Refactor trident-related classes and services for improved clarity and consistency

* Rename trident cooldown methods to improve clarity and update comments for riptide usage

* Fixes after tests - everything should be working now!

* Remove commented line

* Add cooldown to enderpearl - hack by:https://www.spigotmc.org/threads/modify-vanilla-ender-pearl-cooldown.629232/

* Bump minecraft version in runServer task to 1.21.11

* Add combat restrictions for Riptide tridents and improve event handling

* Update eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/trident/TridentSettings.java

* Apply suggestion from @Jakubk15

* Migrate to commons delay

* Resolve conflicts

* CR

* resolve merge

* resolve merge conflicts and update

---------

Co-authored-by: Jakubk15 <77227023+Jakubk15@users.noreply.github.com>
Co-authored-by: Michał Wojtas <wojtas.michal90@gmail.com>
Co-authored-by: Rollczi <ndejlich5@gmail.com>
Co-authored-by: Martin Sulikowski <vluckyyy.biznes@gmail.com>
2026-06-07 22:36:05 +02:00
bb130ae10e GH-373 Add opponent placeholder in combat notification (#373)
* Add opponent placeholder in combat notification

Signed-off-by: Michał Wojtas <wojtas.michal90@gmail.com>

* Update eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/FightTask.java

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

---------

Signed-off-by: Michał Wojtas <wojtas.michal90@gmail.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Norbert Dejlich <ndejlich5@gmail.com>
2026-06-07 21:46:10 +02:00
Igor MichalskiandRollczi 2c5b007d02 GH-331 Add pearl teleport (#331)
* Add pearl stasis prevention feature

Took 27 minutes

* Add message to pearl stasis prevention feature

Took 8 minutes

* CR

---------

Co-authored-by: Rollczi <ndejlich5@gmail.com>
2026-06-07 21:11:41 +02:00
Martin Sulikowski 51b365df1b bump version to 2.5.2-SNAPSHOT for further development 2026-06-06 21:25:32 +02:00
Martin Sulikowski 4f23105ba1 Release: bump version to 2.5.1 for official release 2026-06-06 21:21:25 +02:00
Martin Sulikowski d0c221c184 Fix: Add PlaceholderAPI dependency and ensure safe API initialization/deinitialization 2026-06-06 21:18:29 +02:00
Martin Sulikowski bf529dce32 Readme: Extend supported Minecraft versions to 26.1.2 2026-06-06 02:04:05 +02:00
Martin Sulikowski 74ec795581 Fix: bump version to 2.5.1-SNAPSHOT for further development 2026-06-06 02:03:30 +02:00
Martin Sulikowski b3550110c8 bump version to 2.5.1-SNAPSHOT for further development 2026-06-06 01:57:48 +02:00
28 changed files with 512 additions and 160 deletions

No files matched your search

+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
contents: read
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
fetch-depth: 0
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
contents: read
if: (github.event_name == 'push' && !contains(github.event.head_commit.message, '[ci skip]')) || github.event_name == 'release'
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Set up JDK 21
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
contents: read
if: (github.event_name == 'push' && !contains(github.event.head_commit.message, '[ci skip]')) || github.event_name == 'release'
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Set up JDK 21
+1 -1
View File
@@ -9,7 +9,7 @@ jobs:
environment: deployment
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v7
- name: Set up JDK
uses: actions/setup-java@v5
+3 -3
View File
@@ -25,7 +25,7 @@
## EternalCombat requires PacketEvents plugin to work.
You can download it from [here](https://modrinth.com/plugin/packetevents)
EternalCombat 2.0 has been tested on Minecraft versions **1.17.1 to 1.21.11**, but it should work seamlessly on most
EternalCombat 2.0 has been tested on Minecraft versions **1.17.1 to 26.1.2**, but it should work seamlessly on most
other versions too.
If you run into any compatibility issues, please let us know in
the [Issues tab](https://github.com/EternalCodeTeam/EternalCombat/issues).
@@ -139,7 +139,7 @@ With Maven:
With Gradle:
```kts
compileOnly("com.eternalcode:eternalcombat-api:2.5.0-SNAPSHOT")
compileOnly("com.eternalcode:eternalcombat-api:2.5.2-SNAPSHOT")
```
With Maven:
@@ -149,7 +149,7 @@ With Maven:
<dependency>
<groupId>com.eternalcode</groupId>
<artifactId>eternalcombat-api</artifactId>
<version>2.5.0-SNAPSHOT</version>
<version>2.5.2-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
```
+7 -5
View File
@@ -13,7 +13,7 @@ object Versions {
const val MULTIFICATION = "1.2.5-SNAPSHOT"
const val PACKETS_EVENTS = "2.12.2"
const val LITE_COMMANDS = "3.10.9"
const val LITE_COMMANDS = "3.11.0"
const val OKAERI_CONFIGS_SERDES_COMMONS = "5.0.13"
const val OKAERI_CONFIGS_SERDES_BUKKIT = "5.0.13"
@@ -26,10 +26,12 @@ object Versions {
const val PLACEHOLDER_API = "2.12.2"
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"
//runServer task
const val FAST_ASYNC_WORLD_EDIT = "2.15.2"
const val PACKETEVENTS = "2.12.2+spigot"
const val WORLDGUARD = "7.0.17"
const val LUCKPERMS = "v5.5.53-bukkit"
}
@@ -3,7 +3,7 @@ plugins {
}
group = "com.eternalcode"
version = "2.5.0-SNAPSHOT"
version = "2.5.2-SNAPSHOT"
tasks.compileJava {
options.compilerArgs = listOf("-Xlint:deprecation", "-parameters")
@@ -4,7 +4,7 @@ plugins {
}
group = "com.eternalcode"
version = "2.5.0"
version = "2.5.2-SNAPSHOT"
java {
withSourcesJar()
@@ -14,9 +14,9 @@ tasks {
})
minecraftVersion("26.1.2")
downloadPlugins.modrinth("FastAsyncWorldEdit", "2.15.2")
downloadPlugins.modrinth("PacketEvents", "2.12.2+spigot")
downloadPlugins.modrinth("WorldGuard", "7.0.17")
downloadPlugins.modrinth("LuckPerms", "v5.5.53-bukkit")
downloadPlugins.modrinth("FastAsyncWorldEdit", Versions.FAST_ASYNC_WORLD_EDIT)
downloadPlugins.modrinth("PacketEvents", Versions.PACKETEVENTS)
downloadPlugins.modrinth("WorldGuard", Versions.WORLDGUARD)
downloadPlugins.modrinth("LuckPerms", Versions.LUCKPERMS)
}
}
@@ -4,7 +4,7 @@ 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.pearl.FightPearlService;
import com.eternalcode.combat.fight.pearl.PearlService;
import com.eternalcode.combat.region.RegionProvider;
import com.eternalcode.combat.fight.tagout.FightTagOutService;
@@ -14,7 +14,7 @@ public interface EternalCombatApi {
RegionProvider getRegionProvider();
FightPearlService getFightPearlService();
PearlService getFightPearlService();
FightTagOutService getFightTagOutService();
@@ -23,6 +23,17 @@ public enum CauseOfTag {
*/
CRYSTAL,
/**
* Trident usage extending combat tag.
*/
TRIDENT,
/**
* Ender pearl usage extending combat tag.
*/
ENDER_PEARL,
/**
* A custom cause, typically defined by external plugins or systems, applied the combat tag.
*/
@@ -1,16 +1,16 @@
package com.eternalcode.combat.fight.pearl;
import java.time.Duration;
import java.time.Instant;
import java.util.UUID;
import org.bukkit.entity.Player;
public interface FightPearlService {
public interface PearlService {
Instant getDelay(UUID uuid);
boolean shouldCancelEvent(UUID playerId);
Duration getRemainingDelay(UUID uuid);
boolean hasDelay(UUID uuid);
void markDelay(UUID uuid);
void handleDelay(Player uuid);
}
@@ -0,0 +1,31 @@
package com.eternalcode.combat.fight.trident;
import java.time.Duration;
import java.util.UUID;
public interface TridentService {
/**
* returns remaining delay for the player to use trident again
* @param uuid unique id of the player
* @return remaining duration left to use trident again by the player or zero
*/
Duration getRemainingDelay(UUID uuid);
/**
* checks if player still has delay left to use trident
* @param uuid unique id of the player
* @return true if user still has cooldown left to use trident
*/
boolean hasDelay(UUID uuid);
/**
* marks start of the delay for the user
* @param uuid unique id of the player
*/
void markDelay(UUID uuid);
void removeDelay(UUID playerId);
}
+7 -2
View File
@@ -1,6 +1,6 @@
import net.minecrell.pluginyml.bukkit.BukkitPluginDescription
import net.minecrell.pluginyml.paper.PaperPluginDescription
import org.gradle.kotlin.dsl.shadowJar
import net.minecrell.pluginyml.bukkit.BukkitPluginDescription
plugins {
`eternalcombat-java`
@@ -95,6 +95,11 @@ paper {
required = false
load = PaperPluginDescription.RelativeLoadOrder.BEFORE
}
register("PlaceholderAPI") {
required = false
load = PaperPluginDescription.RelativeLoadOrder.BEFORE
}
}
}
@@ -1,13 +1,20 @@
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.blocker.CommandsBlocker;
import com.eternalcode.combat.fight.blocker.ElytraBlocker;
import com.eternalcode.combat.fight.blocker.ElytraEquipBlocker;
@@ -16,44 +23,40 @@ 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.death.DeathFlareController;
import com.eternalcode.combat.fight.death.DeathLightningController;
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.DropKeepInventoryServiceImpl;
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.blocker.PlaceBlockBlocker;
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.DeathFlareController;
import com.eternalcode.combat.fight.death.DeathLightningController;
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.blocker.PlaceBlockBlocker;
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.FightPearlServiceImpl;
import com.eternalcode.combat.fight.tagout.FightTagOutController;
import com.eternalcode.combat.fight.tagout.FightTagOutServiceImpl;
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.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.trident.TridentController;
import com.eternalcode.combat.fight.trident.TridentService;
import com.eternalcode.combat.fight.trident.TridentServiceImpl;
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;
@@ -85,7 +88,8 @@ public final class CombatPlugin extends JavaPlugin implements EternalCombatApi {
private static final int BSTATS_METRICS_ID = 17803;
private FightManager fightManager;
private FightPearlService fightPearlService;
private PearlService pearlService;
private TridentService tridentService;
private FightTagOutService fightTagOutService;
private FightEffectService fightEffectService;
@@ -95,6 +99,7 @@ public final class CombatPlugin extends JavaPlugin implements EternalCombatApi {
private RegionProvider regionProvider;
private LiteCommands<CommandSender> liteCommands;
private boolean apiInitialized;
@Override
@@ -112,7 +117,8 @@ public final class CombatPlugin extends JavaPlugin implements EternalCombatApi {
MinecraftScheduler scheduler = CombatSchedulerAdapter.getAdaptiveScheduler(this);
this.fightManager = new FightManagerImpl(eventManager);
this.fightPearlService = new FightPearlServiceImpl(pluginConfig.pearl);
this.pearlService = new PearlServiceImpl(this.fightManager, pluginConfig, scheduler);
this.tridentService = new TridentServiceImpl(pluginConfig);
this.fightTagOutService = new FightTagOutServiceImpl();
this.fightEffectService = new FightEffectServiceImpl();
@@ -182,7 +188,8 @@ public final class CombatPlugin extends JavaPlugin implements EternalCombatApi {
new FightBypassPermissionController(server, pluginConfig),
new FightBypassCreativeController(server, pluginConfig),
new PlaceBlockBlocker(this.fightManager, noticeService, pluginConfig),
new FightPearlController(pluginConfig.pearl, noticeService, this.fightManager, this.fightPearlService),
new PearlController(pluginConfig, this.pearlService, noticeService, fightManager),
new TridentController(pluginConfig, noticeService, this.fightManager, this.tridentService, server),
new DeathFlareController(pluginConfig, server, scheduler, this),
new DeathLightningController(pluginConfig, server),
new UpdaterNotificationController(updaterService, pluginConfig, miniMessage),
@@ -217,6 +224,7 @@ public final class CombatPlugin extends JavaPlugin implements EternalCombatApi {
);
EternalCombatProvider.initialize(this);
this.apiInitialized = true;
long millis = started.elapsed(TimeUnit.MILLISECONDS);
this.getLogger().info("Successfully loaded EternalCombat in " + millis + "ms");
@@ -230,7 +238,11 @@ public final class CombatPlugin extends JavaPlugin implements EternalCombatApi {
if (this.fightManager != null) {
this.fightManager.untagAll();
}
if (this.apiInitialized) {
EternalCombatProvider.deinitialize();
this.apiInitialized = false;
}
}
@@ -245,8 +257,8 @@ public final class CombatPlugin extends JavaPlugin implements EternalCombatApi {
}
@Override
public FightPearlService getFightPearlService() {
return this.fightPearlService;
public PearlService getFightPearlService() {
return this.pearlService;
}
@Override
@@ -16,6 +16,7 @@ public class MessagesSettings extends OkaeriConfig {
" ",
"# Configure the combat log notification displayed to players.",
"# You can use the {TIME} variable to display the remaining combat time.",
"# Placeholder {OPPONENT} will be replaced with player name or 'unknownPlayerPlaceholder' variable",
" ",
})
public Notice combatNotification = Notice.builder()
@@ -27,6 +28,12 @@ public class MessagesSettings extends OkaeriConfig {
})
public boolean withoutMillis = true;
@Comment({
" ",
"# When player is not known this text will be used as {OPPONENT} in 'combatNotification'"
})
public String unknownPlayerPlaceholder = "Unknown";
@Comment({
"# Message displayed when a player lacks permission to execute a command.",
"# The {PERMISSION} placeholder is replaced with the required permission."
@@ -5,7 +5,8 @@ import com.eternalcode.combat.fight.death.DeathSettings;
import com.eternalcode.combat.fight.drop.DropSettings;
import com.eternalcode.combat.fight.effect.FightEffectSettings;
import com.eternalcode.combat.fight.knockback.KnockbackSettings;
import com.eternalcode.combat.fight.pearl.FightPearlSettings;
import com.eternalcode.combat.fight.pearl.PearlSettings;
import com.eternalcode.combat.fight.trident.TridentSettings;
import eu.okaeri.configs.OkaeriConfig;
import eu.okaeri.configs.annotation.Comment;
import java.time.Duration;
@@ -34,7 +35,14 @@ public class PluginConfig extends OkaeriConfig {
"# Settings related to Ender Pearls.",
"# Configure cooldowns, restrictions, and other behaviors for Ender Pearls during combat."
})
public FightPearlSettings pearl = new FightPearlSettings();
public PearlSettings pearl = new PearlSettings();
@Comment({
" ",
"# Settings related to Trident",
"# Configure cooldowns, restrictions, and other behaviors for Trident during combat."
})
public TridentSettings trident = new TridentSettings();
@Comment({
" ",
@@ -4,6 +4,7 @@ import com.eternalcode.combat.config.implementation.PluginConfig;
import com.eternalcode.combat.fight.event.CauseOfUnTag;
import com.eternalcode.combat.notification.NoticeService;
import com.eternalcode.combat.util.DurationUtil;
import java.util.Optional;
import org.bukkit.Server;
import org.bukkit.entity.Player;
@@ -17,7 +18,7 @@ public class FightTask implements Runnable {
private final FightManager fightManager;
private final NoticeService noticeService;
public FightTask(Server server, PluginConfig config, FightManager fightManager, NoticeService noticeService) {
public FightTask(Server server, PluginConfig config, FightManager fightManager, NoticeService noticeService) {
this.server = server;
this.config = config;
this.fightManager = fightManager;
@@ -42,10 +43,17 @@ public class FightTask implements Runnable {
Duration remaining = fightTag.getRemainingDuration();
String opponent = Optional.ofNullable(fightTag.getTagger())
.filter(uuid -> !uuid.equals(playerUniqueId))
.map(this.server::getPlayer)
.map(Player::getName)
.orElse(this.config.messagesSettings.unknownPlayerPlaceholder);
this.noticeService.create()
.player(player.getUniqueId())
.notice(this.config.messagesSettings.combatNotification)
.placeholder("{TIME}", DurationUtil.format(remaining, this.config.messagesSettings.withoutMillis))
.placeholder("{OPPONENT}", opponent)
.send();
}
@@ -1,50 +0,0 @@
package com.eternalcode.combat.fight.pearl;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import java.time.Duration;
import java.time.Instant;
import java.util.UUID;
public class FightPearlServiceImpl implements FightPearlService {
private final FightPearlSettings pearlSettings;
private final Cache<UUID, Instant> pearlStartTimes;
public FightPearlServiceImpl(FightPearlSettings pearlSettings) {
this.pearlSettings = pearlSettings;
this.pearlStartTimes = Caffeine.newBuilder()
.expireAfterWrite(pearlSettings.pearlThrowDelay)
.build();
}
@Override
public void markDelay(UUID uuid) {
this.pearlStartTimes.put(uuid, Instant.now());
}
@Override
public boolean hasDelay(UUID uuid) {
return this.pearlStartTimes.getIfPresent(uuid) != null;
}
@Override
public Duration getRemainingDelay(UUID uuid) {
Instant startTime = this.pearlStartTimes.getIfPresent(uuid);
if (startTime == null) {
return Duration.ZERO;
}
Duration elapsed = Duration.between(startTime, Instant.now());
Duration remaining = this.pearlSettings.pearlThrowDelay.minus(elapsed);
return remaining.isNegative() ? Duration.ZERO : remaining;
}
@Override
public Instant getDelay(UUID uuid) {
Instant startTime = this.pearlStartTimes.getIfPresent(uuid);
return startTime != null ? startTime.plus(this.pearlSettings.pearlThrowDelay) : Instant.MIN;
}
}
@@ -1,11 +1,11 @@
package com.eternalcode.combat.fight.pearl;
import com.eternalcode.combat.config.implementation.PluginConfig;
import com.eternalcode.combat.fight.FightManager;
import com.eternalcode.combat.notification.NoticeService;
import com.eternalcode.combat.util.DurationUtil;
import java.time.Duration;
import java.util.UUID;
import org.bukkit.Material;
import org.bukkit.entity.EnderPearl;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@@ -14,28 +14,26 @@ import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.ProjectileLaunchEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.event.player.PlayerTeleportEvent;
public class FightPearlController implements Listener {
public class PearlController implements Listener {
private final FightPearlSettings settings;
private final PluginConfig pluginConfig;
private final PearlService pearlService;
private final NoticeService noticeService;
private final FightManager fightManager;
private final FightPearlService fightPearlService;
public FightPearlController(
FightPearlSettings settings,
NoticeService noticeService,
FightManager fightManager,
FightPearlService fightPearlService
public PearlController(
PluginConfig pluginConfig,
PearlService pearlService, NoticeService noticeService, FightManager fightManager
) {
this.settings = settings;
this.pluginConfig = pluginConfig;
this.pearlService = pearlService;
this.noticeService = noticeService;
this.fightManager = fightManager;
this.fightPearlService = fightPearlService;
}
@EventHandler(priority = EventPriority.HIGHEST)
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onPearlThrow(ProjectileLaunchEvent event) {
if (!(event.getEntity() instanceof EnderPearl)) {
return;
@@ -47,58 +45,68 @@ public class FightPearlController implements Listener {
UUID playerId = player.getUniqueId();
if (!this.fightManager.isInCombat(playerId)) {
return;
}
if (this.settings.pearlThrowDisabledDuringCombat) {
if (this.pearlService.shouldCancelEvent(playerId)) {
event.setCancelled(true);
if (this.pluginConfig.pearl.pearlThrowDisabledDuringCombat) {
this.noticeService.create()
.player(playerId)
.notice(this.pluginConfig.pearl.pearlThrowBlockedDuringCombat)
.send();
return;
}
Duration remainingDelay = this.pearlService.getRemainingDelay(playerId);
this.noticeService.create()
.player(playerId)
.notice(this.settings.pearlThrowBlockedDuringCombat)
.notice(this.pluginConfig.pearl.pearlThrowBlockedDelayDuringCombat)
.placeholder("{TIME}", DurationUtil.format(remainingDelay))
.send();
return;
}
if (this.settings.pearlCooldownEnabled) {
handlePearlCooldown(event, player, playerId);
}
this.pearlService.handleDelay(player);
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onPearlDamage(EntityDamageByEntityEvent event) {
if (this.settings.pearlThrowDamageEnabled) {
if (this.pluginConfig.pearl.pearlThrowDamageEnabled) {
return;
}
if (!(event.getEntity() instanceof Player) ||
!(event.getDamager() instanceof EnderPearl) ||
event.getCause() != EntityDamageEvent.DamageCause.FALL) {
event.getCause() != EntityDamageEvent.DamageCause.PROJECTILE) {
return;
}
event.setDamage(0.0);
}
private void handlePearlCooldown(ProjectileLaunchEvent event, Player player, UUID playerId) {
if (this.settings.pearlThrowDelay.isZero()) {
@EventHandler(priority = EventPriority.HIGHEST)
public void onPearlTeleport(PlayerTeleportEvent event) {
if (event.getCause() != PlayerTeleportEvent.TeleportCause.ENDER_PEARL) {
return;
}
if (this.fightPearlService.hasDelay(playerId)) {
event.setCancelled(true);
Duration remainingDelay = this.fightPearlService.getRemainingDelay(playerId);
this.noticeService.create()
.player(playerId)
.notice(this.settings.pearlThrowBlockedDelayDuringCombat)
.placeholder("{TIME}", DurationUtil.format(remainingDelay))
.send();
if (!this.pluginConfig.pearl.pearlThrowDisabledDuringCombat) {
return;
}
this.fightPearlService.markDelay(playerId);
int cooldownTicks = (int) (this.settings.pearlThrowDelay.toMillis() / 50);
player.setCooldown(Material.ENDER_PEARL, cooldownTicks);
Player player = event.getPlayer();
UUID playerId = player.getUniqueId();
if (!this.fightManager.isInCombat(playerId)) {
return;
}
event.setCancelled(true);
this.noticeService.create()
.player(playerId)
.notice(this.pluginConfig.pearl.pearlThrowBlockedDuringCombat)
.send();
}
}
@@ -0,0 +1,81 @@
package com.eternalcode.combat.fight.pearl;
import com.eternalcode.combat.config.implementation.PluginConfig;
import com.eternalcode.combat.fight.FightManager;
import com.eternalcode.combat.fight.event.CauseOfTag;
import com.eternalcode.commons.delay.Delay;
import com.eternalcode.commons.scheduler.Scheduler;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import java.time.Duration;
import java.util.UUID;
public class PearlServiceImpl implements PearlService {
private final FightManager fightManager;
private final PluginConfig pluginConfig;
private final Scheduler scheduler;
private final Delay<UUID> pearlStartTimes;
public PearlServiceImpl(FightManager fightManager, PluginConfig pluginConfig, Scheduler scheduler) {
this.fightManager = fightManager;
this.pluginConfig = pluginConfig;
this.pearlStartTimes = Delay.withDefault(() -> pluginConfig.pearl.pearlThrowDelay);
this.scheduler = scheduler;
}
@Override
public boolean shouldCancelEvent(UUID playerId) {
if (fightManager.isInCombat(playerId)) {
if (this.pluginConfig.pearl.pearlCooldownEnabled) {
return this.pearlStartTimes.hasDelay(playerId);
}
return pluginConfig.pearl.pearlThrowDisabledDuringCombat;
}
return false;
}
@Override
public void handleDelay(Player player) {
UUID uniqueId = player.getUniqueId();
if (this.hasDelay(uniqueId)) {
return;
}
if (this.fightManager.isInCombat(uniqueId)) {
if (this.pluginConfig.pearl.pearlExtendsCombatTag) {
Duration combatTime = this.pluginConfig.settings.combatTimerDuration;
this.fightManager.tag(uniqueId, combatTime, CauseOfTag.ENDER_PEARL);
}
if (this.pluginConfig.pearl.pearlCooldownEnabled && !this.pluginConfig.pearl.pearlThrowDelay.isZero()) {
this.pearlStartTimes.markDelay(uniqueId);
this.scheduler.runLater(
() -> player.setCooldown(
Material.ENDER_PEARL,
(int) this.pluginConfig.pearl.pearlThrowDelay.toMillis() / 50
), Duration.ofMillis(50)
);
}
}
}
@Override
public boolean hasDelay(UUID uuid) {
return this.pearlStartTimes.hasDelay(uuid);
}
@Override
public Duration getRemainingDelay(UUID uuid) {
return this.pearlStartTimes.getRemaining(uuid);
}
}
@@ -6,7 +6,7 @@ import eu.okaeri.configs.annotation.Comment;
import java.time.Duration;
public class FightPearlSettings extends OkaeriConfig {
public class PearlSettings extends OkaeriConfig {
@Comment({ "# Is pearl damage to be enabled?", "# This will work globally" })
public boolean pearlThrowDamageEnabled = true;
@@ -20,6 +20,9 @@ public class FightPearlSettings extends OkaeriConfig {
@Comment("# Set true, If you want add cooldown to pearls")
public boolean pearlCooldownEnabled = false;
@Comment("# Set true, If you want to reset timer when player throws ender pearl")
public boolean pearlExtendsCombatTag = false;
@Comment({
"# Block throwing pearls with delay?",
"# If you set this to for example 3s, player will have to wait 3 seconds before throwing another pearl"
@@ -0,0 +1,146 @@
package com.eternalcode.combat.fight.trident;
import com.eternalcode.combat.config.implementation.PluginConfig;
import com.eternalcode.combat.fight.FightManager;
import com.eternalcode.combat.fight.event.CauseOfTag;
import com.eternalcode.combat.fight.event.FightUntagEvent;
import com.eternalcode.combat.notification.NoticeService;
import com.eternalcode.combat.util.DurationUtil;
import java.time.Duration;
import java.util.UUID;
import org.bukkit.Material;
import org.bukkit.Server;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerRiptideEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
public class TridentController implements Listener {
private final PluginConfig pluginConfig;
private final NoticeService noticeService;
private final FightManager fightManager;
private final TridentService tridentService;
private final Server server;
public TridentController(
PluginConfig pluginConfig,
NoticeService noticeService,
FightManager fightManager,
TridentService tridentService, Server server
) {
this.pluginConfig = pluginConfig;
this.noticeService = noticeService;
this.fightManager = fightManager;
this.tridentService = tridentService;
this.server = server;
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onRiptideInteract(PlayerInteractEvent event) {
if (!this.isRiptideInteract(event)) {
return;
}
Player player = event.getPlayer();
UUID playerId = player.getUniqueId();
if (!this.fightManager.isInCombat(playerId)) {
return;
}
if (this.pluginConfig.trident.tridentRiptideDisabledDuringCombat) {
event.setCancelled(true);
this.noticeService.create()
.player(playerId)
.notice(this.pluginConfig.trident.tridentRiptideBlocked)
.send();
return;
}
if (!this.tridentService.hasDelay(playerId)) {
return;
}
event.setCancelled(true);
}
// we need to send message without ignoring canceled event because of riptide cooldown
@EventHandler(priority = EventPriority.LOWEST)
public void messageOnRiptideCooldown(PlayerInteractEvent event) {
if (!this.isRiptideInteract(event)) {
return;
}
UUID playerId = event.getPlayer().getUniqueId();
if (!this.tridentService.hasDelay(playerId)) {
return;
}
Duration remainingDelay = this.tridentService.getRemainingDelay(playerId);
this.noticeService.create()
.player(playerId)
.notice(this.pluginConfig.trident.tridentRiptideOnCooldown)
.placeholder("{TIME}", DurationUtil.format(remainingDelay))
.send();
}
@EventHandler(ignoreCancelled = true)
public void onRiptide(PlayerRiptideEvent event) {
Player player = event.getPlayer();
UUID uniqueId = player.getUniqueId();
if (this.pluginConfig.trident.tridentRiptideDisabledDuringCombat) {
return;
}
if (!this.fightManager.isInCombat(uniqueId)) {
return;
}
if (this.tridentService.hasDelay(uniqueId)) {
return;
}
this.tridentService.markDelay(player.getUniqueId());
player.setCooldown(Material.TRIDENT, (int) this.pluginConfig.trident.tridentRiptideDelay.toMillis() / 50);
if (this.pluginConfig.trident.tridentRiptideExtendsCombatTag) {
this.fightManager.tag(uniqueId, this.pluginConfig.settings.combatTimerDuration, CauseOfTag.TRIDENT);
}
}
@EventHandler(ignoreCancelled = true)
public void onUntag(FightUntagEvent event) {
Player player = server.getPlayer(event.getPlayer());
player.setCooldown(Material.TRIDENT, 0);
this.tridentService.removeDelay(player.getUniqueId());
}
private boolean isRiptideInteract(PlayerInteractEvent event) {
EquipmentSlot hand = event.getHand();
if (hand != EquipmentSlot.HAND && hand != EquipmentSlot.OFF_HAND) {
return false;
}
Action action = event.getAction();
if (action != Action.RIGHT_CLICK_AIR && action != Action.RIGHT_CLICK_BLOCK) {
return false;
}
return this.isRiptideTrident(event.getItem());
}
private boolean isRiptideTrident(ItemStack itemStack) {
return itemStack != null
&& itemStack.getType() == Material.TRIDENT
&& itemStack.containsEnchantment(Enchantment.RIPTIDE);
}
}
@@ -0,0 +1,41 @@
package com.eternalcode.combat.fight.trident;
import com.eternalcode.combat.config.implementation.PluginConfig;
import com.eternalcode.combat.fight.FightManager;
import com.eternalcode.commons.delay.Delay;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import java.time.Duration;
import java.util.UUID;
public class TridentServiceImpl implements TridentService {
private final Delay<UUID> delay;
public TridentServiceImpl(PluginConfig pluginConfig) {
this.delay = Delay.withDefault(() -> pluginConfig.trident.tridentRiptideDelay);
}
@Override
public void markDelay(UUID uuid) {
this.delay.markDelay(uuid);
}
@Override
public boolean hasDelay(UUID uuid) {
return this.delay.hasDelay(uuid);
}
@Override
public void removeDelay(UUID playerId) {
this.delay.unmarkDelay(playerId);
}
@Override
public Duration getRemainingDelay(UUID uuid) {
return this.delay.getRemaining(uuid);
}
}
@@ -0,0 +1,39 @@
package com.eternalcode.combat.fight.trident;
import com.eternalcode.multification.notice.Notice;
import eu.okaeri.configs.OkaeriConfig;
import eu.okaeri.configs.annotation.Comment;
import java.time.Duration;
public class TridentSettings extends OkaeriConfig {
@Comment({
"# Set to true to disable riptide usage during combat",
"# This setting works globally, but can be overridden by region settings"
})
public boolean tridentRiptideDisabledDuringCombat = false;
@Comment("# Set to true so then riptide usage during combat will extend combat tag duration, works only if riptide usage is not disabled")
public boolean tridentRiptideExtendsCombatTag = false;
@Comment({
"# Should riptide enchantment be on cooldown during combat?",
"# Setting this option to 3s will make players wait 3 seconds between trident throws",
"# Setting this to 0s or below will remove cooldown"
})
public Duration tridentRiptideDelay = Duration.ofSeconds(10);
@Comment("# Message shown when riptide is blocked during combat")
public Notice tridentRiptideBlocked = Notice.builder()
.chat("<red>Using riptide is prohibited during combat!")
.build();
@Comment({
"# Message sent to the player when riptide is on cooldown",
"# Available placeholder: {TIME} - remaining time left to use riptide again"
})
public Notice tridentRiptideOnCooldown = Notice.builder()
.chat("<red>You must wait {TIME} before next riptide!")
.build();
}
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.0-bin.zip
networkTimeout=10000
retries=0
retryBackOffMs=500
Vendored
+2 -2
View File
@@ -20,7 +20,7 @@
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
# gradlew start up script for POSIX generated by Gradle.
#
# Important for running:
#
@@ -29,7 +29,7 @@
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
# ksh gradlew
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
Vendored
+2 -2
View File
@@ -19,7 +19,7 @@
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem gradlew startup script for Windows
@rem
@rem ##########################################################################
@@ -72,7 +72,7 @@ echo location of your Java installation. 1>&2
@rem Execute Gradle
@rem Execute gradlew
@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
@rem which allows us to clear the local environment before executing the java command
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel