mirror of
https://github.com/EternalCodeTeam/EternalCombat.git
synced 2026-11-05 01:00:11 -05:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
520196417b |
No files matched your search
@@ -20,7 +20,7 @@ jobs:
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
java-version: '25'
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
|
||||
- name: Setup Gradle
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
java-version: '25'
|
||||
java-version: '21'
|
||||
distribution: 'temurin'
|
||||
|
||||
- name: Setup Gradle
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
java-version: '25'
|
||||
java-version: '21'
|
||||
distribution: 'temurin'
|
||||
|
||||
- name: Setup Gradle
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 25
|
||||
java-version: 17
|
||||
cache: 'gradle'
|
||||
|
||||
- name: Grant execute permission for gradlew
|
||||
|
||||
@@ -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 26.2**, 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).
|
||||
@@ -45,13 +45,9 @@ 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="Trident and spear cooldown 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">
|
||||
- **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)**
|
||||
@@ -61,7 +57,7 @@ fair, heart-pounding battles that keep players on their toes. Here’s a rundown
|
||||
|
||||
- **Crystal PvP support**
|
||||
Engage in intense Crystal PvP battles without worrying about players logging out mid-fight! EternalCombat keeps
|
||||
everyone in the game until the last anchor hit. Check it out:
|
||||
everyone in the game until the last anchor hit. Check it out:
|
||||
<img alt="Crystal PvP showcase" src="https://github.com/EternalCodeTeam/EternalCombat/blob/master/assets/crystals.gif?raw=true" width="420">
|
||||
|
||||
- **Fully Customizable Combat**
|
||||
@@ -143,7 +139,7 @@ With Maven:
|
||||
With Gradle:
|
||||
|
||||
```kts
|
||||
compileOnly("com.eternalcode:eternalcombat-api:2.6.0-SNAPSHOT")
|
||||
compileOnly("com.eternalcode:eternalcombat-api:2.5.2-SNAPSHOT")
|
||||
```
|
||||
|
||||
With Maven:
|
||||
@@ -153,7 +149,7 @@ With Maven:
|
||||
<dependency>
|
||||
<groupId>com.eternalcode</groupId>
|
||||
<artifactId>eternalcombat-api</artifactId>
|
||||
<version>2.6.0-SNAPSHOT</version>
|
||||
<version>2.5.2-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.1 MiB |
@@ -8,12 +8,12 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("com.gradleup.shadow:com.gradleup.shadow.gradle.plugin:9.6.1")
|
||||
implementation("com.modrinth.minotaur:com.modrinth.minotaur.gradle.plugin:2.10.0")
|
||||
implementation("com.gradleup.shadow:com.gradleup.shadow.gradle.plugin:9.4.2")
|
||||
implementation("com.modrinth.minotaur:com.modrinth.minotaur.gradle.plugin:2.9.0")
|
||||
implementation("io.papermc.hangar-publish-plugin:io.papermc.hangar-publish-plugin.gradle.plugin:0.1.4")
|
||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.4.20")
|
||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.4.0")
|
||||
implementation("net.minecrell:plugin-yml:0.6.0")
|
||||
implementation("xyz.jpenilla:run-task:3.1.0")
|
||||
implementation("xyz.jpenilla:run-task:3.0.2")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
||||
@@ -3,15 +3,15 @@ object Versions {
|
||||
// Keep this version for best compatibility.
|
||||
const val PAPER_API = "1.19.3-R0.1-SNAPSHOT"
|
||||
|
||||
const val JUNIT_JUPITER_API = "6.1.3"
|
||||
const val JUNIT_JUPITER_PARAMS = "6.1.3"
|
||||
const val JUNIT_JUPITER_ENGINE = "6.1.3"
|
||||
const val JUNIT_JUPITER_API = "6.1.0"
|
||||
const val JUNIT_JUPITER_PARAMS = "6.1.0"
|
||||
const val JUNIT_JUPITER_ENGINE = "6.1.0"
|
||||
|
||||
const val JETBRAINS_ANNOTATIONS = "26.1.0"
|
||||
|
||||
const val ETERNALCODE_COMMONS = "1.4.1"
|
||||
const val MULTIFICATION = "1.2.5-SNAPSHOT"
|
||||
const val PACKETS_EVENTS = "2.13.0"
|
||||
const val PACKETS_EVENTS = "2.12.2"
|
||||
|
||||
const val LITE_COMMANDS = "3.11.0"
|
||||
const val OKAERI_CONFIGS_SERDES_COMMONS = "5.0.13"
|
||||
@@ -19,16 +19,19 @@ object Versions {
|
||||
|
||||
const val XSERIES = "13.8.0"
|
||||
|
||||
const val CAFFEINE = "3.3.0"
|
||||
const val CAFFEINE = "3.2.4"
|
||||
|
||||
const val B_STATS_BUKKIT = "3.2.1"
|
||||
const val WORLD_GUARD_BUKKIT = "7.0.17"
|
||||
|
||||
const val PLACEHOLDER_API = "2.12.3"
|
||||
const val PLACEHOLDER_API = "2.12.2"
|
||||
const val LANDS_API = "7.25.4"
|
||||
const val GRIEF_PREVENTION = "16.18.7"
|
||||
const val WORLDEDIT = "3ISh7ADm" //cannot use numeric version bc of duplicated version on modrinth
|
||||
const val PACKETEVENTS = "2.14.0"
|
||||
|
||||
//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.6.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.6.0-SNAPSHOT"
|
||||
version = "2.5.2-SNAPSHOT"
|
||||
|
||||
java {
|
||||
withSourcesJar()
|
||||
|
||||
@@ -13,11 +13,10 @@ tasks {
|
||||
languageVersion.set(JavaLanguageVersion.of(25))
|
||||
})
|
||||
|
||||
minecraftVersion("26.3")
|
||||
downloadPlugins.modrinth("WorldEdit", "7.4.6")
|
||||
downloadPlugins.modrinth("PacketEvents", "2.14.0+spigot")
|
||||
downloadPlugins.modrinth("WorldGuard", "7.0.20")
|
||||
downloadPlugins.modrinth("LuckPerms", "v5.5.53-bukkit")
|
||||
downloadPlugins.modrinth("GriefPrevention", Versions.GRIEF_PREVENTION)
|
||||
minecraftVersion("26.1.2")
|
||||
downloadPlugins.modrinth("FastAsyncWorldEdit", Versions.FAST_ASYNC_WORLD_EDIT)
|
||||
downloadPlugins.modrinth("PacketEvents", Versions.PACKETEVENTS)
|
||||
downloadPlugins.modrinth("WorldGuard", Versions.WORLDGUARD)
|
||||
downloadPlugins.modrinth("LuckPerms", Versions.LUCKPERMS)
|
||||
}
|
||||
}
|
||||
@@ -34,11 +34,6 @@ public enum CauseOfTag {
|
||||
ENDER_PEARL,
|
||||
|
||||
|
||||
/**
|
||||
* The player's combat tag was kept active because they were below the void combat height.
|
||||
*/
|
||||
VOID,
|
||||
|
||||
/**
|
||||
* A custom cause, typically defined by external plugins or systems, applied the combat tag.
|
||||
*/
|
||||
|
||||
@@ -5,9 +5,7 @@ import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
|
||||
import java.util.UUID;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* This event is triggered when a player is tagged in a fight.
|
||||
@@ -17,25 +15,14 @@ public class FightTagEvent extends Event implements Cancellable {
|
||||
private static final HandlerList HANDLER_LIST = new HandlerList();
|
||||
private final UUID player;
|
||||
private final CauseOfTag cause;
|
||||
private final UUID tagger;
|
||||
private boolean cancelled = false;
|
||||
private CancelTagReason cancelReason;
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #FightTagEvent(UUID, CauseOfTag, UUID)} instead, which also carries the tagger.
|
||||
*/
|
||||
@Deprecated
|
||||
@ApiStatus.ScheduledForRemoval
|
||||
public FightTagEvent(UUID player, CauseOfTag cause) {
|
||||
this(player, cause, null);
|
||||
}
|
||||
|
||||
public FightTagEvent(UUID player, CauseOfTag cause, @Nullable UUID tagger) {
|
||||
super(false);
|
||||
|
||||
this.player = player;
|
||||
this.cause = cause;
|
||||
this.tagger = tagger;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,16 +43,6 @@ public class FightTagEvent extends Event implements Cancellable {
|
||||
return this.cause;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the UUID of the player who caused this tag, if any.
|
||||
*
|
||||
* @return The UUID of the tagger, or {@code null} if the tag was not caused by another player.
|
||||
*/
|
||||
@Nullable
|
||||
public UUID getTagger() {
|
||||
return this.tagger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return this.cancelled;
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
package com.eternalcode.combat.fight.spear;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface SpearService {
|
||||
boolean isOnCooldown(UUID player);
|
||||
void saveCooldown(UUID player);
|
||||
Duration getRemainingCooldown(UUID player);
|
||||
void removeCooldown(UUID player);
|
||||
}
|
||||
@@ -62,11 +62,6 @@ dependencies {
|
||||
// Lands
|
||||
compileOnly("com.github.angeschossen:LandsAPI:${Versions.LANDS_API}")
|
||||
|
||||
// GriefPrevention
|
||||
compileOnly("com.github.GriefPrevention:GriefPrevention:${Versions.GRIEF_PREVENTION}") {
|
||||
isTransitive = false
|
||||
}
|
||||
|
||||
// Multification
|
||||
implementation("com.eternalcode:multification-paper:${Versions.MULTIFICATION}")
|
||||
implementation("com.eternalcode:multification-okaeri:${Versions.MULTIFICATION}")
|
||||
@@ -101,11 +96,6 @@ paper {
|
||||
load = PaperPluginDescription.RelativeLoadOrder.BEFORE
|
||||
}
|
||||
|
||||
register("GriefPrevention") {
|
||||
required = false
|
||||
load = PaperPluginDescription.RelativeLoadOrder.BEFORE
|
||||
}
|
||||
|
||||
register("PlaceholderAPI") {
|
||||
required = false
|
||||
load = PaperPluginDescription.RelativeLoadOrder.BEFORE
|
||||
@@ -117,12 +107,6 @@ tasks {
|
||||
named("generatePaperPluginDescription") {
|
||||
notCompatibleWithConfigurationCache("The plugin-yml paper generator reads Task.project during execution.")
|
||||
}
|
||||
|
||||
runPaper.folia.registerTask {
|
||||
minecraftVersion("26.3")
|
||||
|
||||
downloadPlugins.modrinth("PacketEvents", "${Versions.PACKETEVENTS}+spigot")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.shadowJar {
|
||||
|
||||
@@ -8,8 +8,8 @@ import com.eternalcode.combat.border.animation.particle.ParticleController;
|
||||
import com.eternalcode.combat.bridge.BridgeService;
|
||||
import com.eternalcode.combat.config.ConfigService;
|
||||
import com.eternalcode.combat.config.implementation.PluginConfig;
|
||||
import com.eternalcode.combat.fight.crystalpvp.EndCrystalController;
|
||||
import com.eternalcode.combat.fight.crystalpvp.RespawnAnchorController;
|
||||
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;
|
||||
@@ -36,12 +36,10 @@ 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.blocker.SignEditingBlocker;
|
||||
import com.eternalcode.combat.fight.effect.FightEffectController;
|
||||
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.KnockbackMountController;
|
||||
import com.eternalcode.combat.fight.knockback.KnockbackRegionController;
|
||||
import com.eternalcode.combat.fight.knockback.KnockbackService;
|
||||
import com.eternalcode.combat.fight.logout.LogoutController;
|
||||
@@ -49,9 +47,6 @@ import com.eternalcode.combat.fight.logout.LogoutService;
|
||||
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;
|
||||
@@ -154,8 +149,6 @@ public final class CombatPlugin extends JavaPlugin implements EternalCombatApi {
|
||||
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)
|
||||
@@ -207,11 +200,10 @@ public final class CombatPlugin extends JavaPlugin implements EternalCombatApi {
|
||||
new BorderTriggerController(borderService, () -> pluginConfig.border, fightManager, server, scheduler),
|
||||
new ParticleController(borderService, () -> pluginConfig.border.particle, scheduler, server),
|
||||
new BorderBlockController(borderService, () -> pluginConfig.border.block, scheduler, server),
|
||||
new EndCrystalController(this, this.fightManager, pluginConfig),
|
||||
new RespawnAnchorController(this, this.fightManager, pluginConfig),
|
||||
new EndCrystalListener(this, this.fightManager, pluginConfig),
|
||||
new RespawnAnchorListener(this, this.fightManager, pluginConfig),
|
||||
new FireworkController(this.fightManager, pluginConfig, noticeService),
|
||||
new InventoryContainersBlocker(this.fightManager, pluginConfig, noticeService),
|
||||
new SignEditingBlocker(this.fightManager, pluginConfig),
|
||||
new CommandsBlocker(this.fightManager, noticeService, pluginConfig),
|
||||
new ElytraBlocker(this.fightManager, pluginConfig),
|
||||
new ElytraEquipBlocker(this.fightManager, noticeService, pluginConfig, server),
|
||||
@@ -219,10 +211,6 @@ public final class CombatPlugin extends JavaPlugin implements EternalCombatApi {
|
||||
new PlaceBlockBlocker(this.fightManager, noticeService, pluginConfig)
|
||||
);
|
||||
|
||||
new KnockbackMountController(noticeService, this.regionProvider, this.fightManager).register(this);
|
||||
|
||||
new SpearLungeController(this, fightManager, spearService, pluginConfig, noticeService);
|
||||
|
||||
eventManager.subscribe(
|
||||
PlayerDeathEvent.class,
|
||||
pluginConfig.drop.dropEventPriority,
|
||||
|
||||
+8
-4
@@ -8,6 +8,8 @@ import org.bukkit.plugin.Plugin;
|
||||
|
||||
public final class CombatSchedulerAdapter {
|
||||
|
||||
private static final String FOLIA_CLASS = "io.papermc.paper.threadedregions.RegionizedServer";
|
||||
|
||||
private CombatSchedulerAdapter() {
|
||||
throw new UnsupportedOperationException("This is an utility class and cannot be instantiated");
|
||||
}
|
||||
@@ -15,12 +17,14 @@ public final class CombatSchedulerAdapter {
|
||||
public static MinecraftScheduler getAdaptiveScheduler(Plugin plugin) {
|
||||
Logger logger = plugin.getLogger();
|
||||
|
||||
if (FoliaChecker.isFolia()) {
|
||||
try {
|
||||
Class.forName(FOLIA_CLASS);
|
||||
logger.info("» Detected Folia environment. Using FoliaScheduler.");
|
||||
return new FoliaSchedulerImpl(plugin);
|
||||
}
|
||||
|
||||
logger.info("» Detected Bukkit/Paper environment. Using BukkitScheduler.");
|
||||
return new BukkitSchedulerImpl(plugin);
|
||||
catch (ClassNotFoundException exception) {
|
||||
logger.info("» Detected Bukkit/Paper environment. Using BukkitScheduler.");
|
||||
return new BukkitSchedulerImpl(plugin);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package com.eternalcode.combat;
|
||||
|
||||
public final class FoliaChecker {
|
||||
|
||||
private static final String FOLIA_CLASS = "io.papermc.paper.threadedregions.RegionizedServer";
|
||||
private static final boolean IS_FOLIA = detectFoliaEnvironment();
|
||||
|
||||
private FoliaChecker() {
|
||||
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
|
||||
}
|
||||
|
||||
public static boolean isFolia() {
|
||||
return IS_FOLIA;
|
||||
}
|
||||
|
||||
private static boolean detectFoliaEnvironment() {
|
||||
try {
|
||||
Class.forName(FOLIA_CLASS);
|
||||
return true;
|
||||
}
|
||||
catch (ClassNotFoundException exception) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import com.eternalcode.combat.fight.FightManager;
|
||||
import com.eternalcode.combat.region.CompositeRegionProvider;
|
||||
import com.eternalcode.combat.region.bukkit.DefaultRegionProvider;
|
||||
import com.eternalcode.combat.region.RegionProvider;
|
||||
import com.eternalcode.combat.region.griefprevention.GriefPreventionRegionProvider;
|
||||
import com.eternalcode.combat.region.worldguard.WorldGuardRegionProvider;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -56,12 +55,6 @@ public class BridgeService {
|
||||
() -> this.logger.warning("WorldGuard not found; skipping WorldGuardRegionProvider.")
|
||||
);
|
||||
|
||||
this.initialize(
|
||||
"GriefPrevention",
|
||||
() -> providers.add(new GriefPreventionRegionProvider(this.config)),
|
||||
() -> this.logger.warning("GriefPrevention not found; skipping GriefPreventionRegionProvider.")
|
||||
);
|
||||
|
||||
if (providers.isEmpty()) {
|
||||
providers.add(new DefaultRegionProvider(this.config.regions.restrictedRegionRadius));
|
||||
this.logger.warning("No region plugin found; using DefaultRegionProvider.");
|
||||
|
||||
-13
@@ -90,19 +90,6 @@ public class CombatSettings extends OkaeriConfig {
|
||||
})
|
||||
public EventPriority quitPunishmentEventPriority = EventPriority.NORMAL;
|
||||
|
||||
@Comment({
|
||||
"# Keep a player's combat tag continuously active while they are below 'voidCombatHeight'.",
|
||||
"# This prevents players from evading combat by falling into the void (e.g. in the End).",
|
||||
"# Set to 'true' to keep resetting the combat timer to its full duration while below that height."
|
||||
})
|
||||
public boolean keepCombatActiveInVoid = false;
|
||||
|
||||
@Comment({
|
||||
"# The Y height below which the combat timer is kept active (see 'keepCombatActiveInVoid').",
|
||||
"# Default: 0, matching the void threshold in worlds such as the End."
|
||||
})
|
||||
public double voidCombatHeight = 0;
|
||||
|
||||
@Comment({
|
||||
"# List of kick reasons where players will NOT be punished for combat logging.",
|
||||
"# If this list is empty, players are ALWAYS punished when kicked during combat.",
|
||||
|
||||
+2
-4
@@ -50,12 +50,10 @@ public class MessagesSettings extends OkaeriConfig {
|
||||
|
||||
@Comment({
|
||||
"# Message displayed when a player enters combat.",
|
||||
"# This message warns the player not to leave the server while in combat.",
|
||||
"# The {OPPONENT} placeholder is replaced with the name of the player who caused the tag,",
|
||||
"# or the 'unknownPlayerPlaceholder' variable if the tag was not caused by a player."
|
||||
"# This message warns the player not to leave the server while in combat."
|
||||
})
|
||||
public Notice playerTagged = Notice.chat(
|
||||
"<gradient:red:yellow>⚠ <white>You are in combat with</white> <yellow>{OPPONENT}</yellow>! <u>Do not leave the server!</gradient>");
|
||||
"<gradient:red:yellow>⚠ <white>You are in combat!</white> <u>Do not leave the server!</gradient>");
|
||||
|
||||
@Comment({
|
||||
"# Message displayed when a player leaves combat.",
|
||||
|
||||
-15
@@ -6,7 +6,6 @@ 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.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;
|
||||
@@ -45,13 +44,6 @@ 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.",
|
||||
@@ -135,13 +127,6 @@ public class PluginConfig extends OkaeriConfig {
|
||||
})
|
||||
public InventorySettings inventory = new InventorySettings();
|
||||
|
||||
@Comment({
|
||||
" ",
|
||||
"# Settings related to sign editing during combat.",
|
||||
"# Created for sign traps where opening the sign editor can block ender pearl throws."
|
||||
})
|
||||
public SignEditingSettings signEditing = new SignEditingSettings();
|
||||
|
||||
@Comment({
|
||||
" ",
|
||||
"# Settings related to placeholders used in the plugin.",
|
||||
|
||||
+1
-2
@@ -13,8 +13,7 @@ public class RegionSettings extends OkaeriConfig {
|
||||
public List<String> blockedRegions = Collections.singletonList("your_region");
|
||||
|
||||
@Comment({
|
||||
"# Prevent players from entering regions where PVP is disabled by the region plugin",
|
||||
"# (WorldGuard PVP flag, GriefPrevention no-combat-in-claims settings).",
|
||||
"# Prevent players from entering regions where PVP is disabled by WorldGuard.",
|
||||
"# Set to 'true' to enforce this restriction, or 'false' to allow PVP in all regions."
|
||||
})
|
||||
public boolean preventPvpInRegions = true;
|
||||
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
package com.eternalcode.combat.config.implementation;
|
||||
|
||||
import eu.okaeri.configs.OkaeriConfig;
|
||||
import eu.okaeri.configs.annotation.Comment;
|
||||
|
||||
public class SignEditingSettings extends OkaeriConfig {
|
||||
|
||||
@Comment({
|
||||
"# Prevent players from editing signs during combat.",
|
||||
"# Disabled by default; created for sign traps where sign editing blocks ender pearl throws."
|
||||
})
|
||||
public boolean disableSignEditingDuringCombat = false;
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.eternalcode.combat.fight.crystalpvp;
|
||||
package com.eternalcode.combat.crystalpvp;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
+3
-5
@@ -1,4 +1,4 @@
|
||||
package com.eternalcode.combat.fight.crystalpvp;
|
||||
package com.eternalcode.combat.crystalpvp;
|
||||
|
||||
import com.eternalcode.combat.config.implementation.PluginConfig;
|
||||
import com.eternalcode.combat.fight.FightManager;
|
||||
@@ -66,14 +66,12 @@ public class CrystalPvpConstants {
|
||||
fightManager.tag(
|
||||
damagerUniqueId,
|
||||
pluginConfig.settings.combatTimerDuration,
|
||||
CauseOfTag.CRYSTAL,
|
||||
victimUniqueId
|
||||
CauseOfTag.CRYSTAL
|
||||
);
|
||||
fightManager.tag(
|
||||
victimUniqueId,
|
||||
pluginConfig.settings.combatTimerDuration,
|
||||
CauseOfTag.CRYSTAL,
|
||||
damagerUniqueId
|
||||
CauseOfTag.CRYSTAL
|
||||
);
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -1,4 +1,4 @@
|
||||
package com.eternalcode.combat.fight.crystalpvp;
|
||||
package com.eternalcode.combat.crystalpvp;
|
||||
|
||||
import com.eternalcode.combat.config.implementation.PluginConfig;
|
||||
import com.eternalcode.combat.fight.FightManager;
|
||||
@@ -12,15 +12,15 @@ import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import static com.eternalcode.combat.fight.crystalpvp.CrystalPvpConstants.CRYSTAL_METADATA;
|
||||
import static com.eternalcode.combat.crystalpvp.CrystalPvpConstants.CRYSTAL_METADATA;
|
||||
|
||||
public class EndCrystalController implements Listener {
|
||||
public class EndCrystalListener implements Listener {
|
||||
|
||||
private final Plugin plugin;
|
||||
private final FightManager fightManager;
|
||||
private final PluginConfig pluginConfig;
|
||||
|
||||
public EndCrystalController(Plugin plugin, FightManager fightManager, PluginConfig pluginConfig) {
|
||||
public EndCrystalListener(Plugin plugin, FightManager fightManager, PluginConfig pluginConfig) {
|
||||
this.plugin = plugin;
|
||||
this.fightManager = fightManager;
|
||||
this.pluginConfig = pluginConfig;
|
||||
+4
-4
@@ -1,4 +1,4 @@
|
||||
package com.eternalcode.combat.fight.crystalpvp;
|
||||
package com.eternalcode.combat.crystalpvp;
|
||||
|
||||
import com.eternalcode.combat.config.implementation.PluginConfig;
|
||||
import com.eternalcode.combat.fight.FightManager;
|
||||
@@ -16,16 +16,16 @@ import org.bukkit.event.entity.EntityDamageByBlockEvent;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import static com.eternalcode.combat.fight.crystalpvp.CrystalPvpConstants.ANCHOR_METADATA;
|
||||
import static com.eternalcode.combat.crystalpvp.CrystalPvpConstants.ANCHOR_METADATA;
|
||||
|
||||
public class RespawnAnchorController implements Listener {
|
||||
public class RespawnAnchorListener implements Listener {
|
||||
|
||||
private final Plugin plugin;
|
||||
private final FightManager fightManager;
|
||||
private final PluginConfig pluginConfig;
|
||||
|
||||
|
||||
public RespawnAnchorController(Plugin plugin, FightManager fightManager, PluginConfig pluginConfig) {
|
||||
public RespawnAnchorListener(Plugin plugin, FightManager fightManager, PluginConfig pluginConfig) {
|
||||
this.plugin = plugin;
|
||||
this.fightManager = fightManager;
|
||||
this.pluginConfig = pluginConfig;
|
||||
+1
-1
@@ -55,7 +55,7 @@ public class FightManagerImpl implements FightManager {
|
||||
@ApiStatus.Experimental
|
||||
@Override
|
||||
public FightTagEvent tag(UUID target, Duration delay, CauseOfTag causeOfTag, @Nullable UUID tagger) {
|
||||
FightTagEvent event = this.eventManager.publishEvent(new FightTagEvent(target, causeOfTag, tagger));
|
||||
FightTagEvent event = this.eventManager.publishEvent(new FightTagEvent(target, causeOfTag));
|
||||
|
||||
if (event.isCancelled()) {
|
||||
return event;
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package com.eternalcode.combat.fight;
|
||||
|
||||
import com.eternalcode.combat.config.implementation.PluginConfig;
|
||||
import com.eternalcode.combat.fight.event.CauseOfTag;
|
||||
import com.eternalcode.combat.fight.event.CauseOfUnTag;
|
||||
import com.eternalcode.combat.fight.event.FightTagEvent;
|
||||
import com.eternalcode.combat.notification.NoticeService;
|
||||
import com.eternalcode.combat.util.DurationUtil;
|
||||
import java.util.Optional;
|
||||
@@ -43,16 +41,6 @@ public class FightTask implements Runnable {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.config.combat.keepCombatActiveInVoid
|
||||
&& player.getLocation().getY() < this.config.combat.voidCombatHeight
|
||||
&& !this.config.settings.ignoredWorlds.contains(player.getWorld().getName())) {
|
||||
FightTagEvent tagEvent = this.fightManager.tag(playerUniqueId, this.config.settings.combatTimerDuration, CauseOfTag.VOID, fightTag.getTagger());
|
||||
|
||||
if (!tagEvent.isCancelled()) {
|
||||
fightTag = this.fightManager.getTag(playerUniqueId);
|
||||
}
|
||||
}
|
||||
|
||||
Duration remaining = fightTag.getRemainingDuration();
|
||||
|
||||
String opponent = Optional.ofNullable(fightTag.getTagger())
|
||||
|
||||
-63
@@ -1,63 +0,0 @@
|
||||
package com.eternalcode.combat.fight.blocker;
|
||||
|
||||
import com.eternalcode.combat.config.implementation.PluginConfig;
|
||||
import com.eternalcode.combat.fight.FightManager;
|
||||
import java.util.UUID;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Event.Result;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public class SignEditingBlocker implements Listener {
|
||||
|
||||
private final FightManager fightManager;
|
||||
private final PluginConfig config;
|
||||
|
||||
public SignEditingBlocker(FightManager fightManager, PluginConfig config) {
|
||||
this.fightManager = fightManager;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
void onInteract(PlayerInteractEvent event) {
|
||||
if (!this.config.signEditing.disableSignEditingDuringCombat) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.getAction() != Action.RIGHT_CLICK_BLOCK) {
|
||||
return;
|
||||
}
|
||||
|
||||
Block clickedBlock = event.getClickedBlock();
|
||||
if (clickedBlock == null || !isSign(clickedBlock.getType())) {
|
||||
return;
|
||||
}
|
||||
|
||||
Player player = event.getPlayer();
|
||||
UUID uniqueId = player.getUniqueId();
|
||||
|
||||
if (!this.fightManager.isInCombat(uniqueId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.setUseInteractedBlock(Result.DENY);
|
||||
|
||||
ItemStack item = event.getItem();
|
||||
if (item != null && isEnderPearl(item.getType()) && event.useItemInHand() != Result.DENY) {
|
||||
event.setUseItemInHand(Result.ALLOW);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isSign(Material material) {
|
||||
return material.name().endsWith("_SIGN");
|
||||
}
|
||||
|
||||
static boolean isEnderPearl(Material material) {
|
||||
return material == Material.ENDER_PEARL;
|
||||
}
|
||||
}
|
||||
-13
@@ -5,8 +5,6 @@ import com.eternalcode.combat.fight.FightManager;
|
||||
import com.eternalcode.combat.fight.event.FightTagEvent;
|
||||
import com.eternalcode.combat.fight.event.FightUntagEvent;
|
||||
import com.eternalcode.combat.notification.NoticeService;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
@@ -39,23 +37,12 @@ public class FightMessageController implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
String opponent = this.resolveOpponentName(event.getTagger(), player.getUniqueId());
|
||||
|
||||
this.noticeService.create()
|
||||
.player(player.getUniqueId())
|
||||
.notice(this.config.messagesSettings.playerTagged)
|
||||
.placeholder("{OPPONENT}", opponent)
|
||||
.send();
|
||||
}
|
||||
|
||||
private String resolveOpponentName(UUID tagger, UUID taggedPlayer) {
|
||||
return Optional.ofNullable(tagger)
|
||||
.filter(uuid -> !uuid.equals(taggedPlayer))
|
||||
.map(this.server::getPlayer)
|
||||
.map(Player::getName)
|
||||
.orElse(this.config.messagesSettings.unknownPlayerPlaceholder);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
void onUnTag(FightUntagEvent event) {
|
||||
Player player = this.server.getPlayer(event.getPlayer());
|
||||
|
||||
+4
-13
@@ -1,6 +1,5 @@
|
||||
package com.eternalcode.combat.fight.controller;
|
||||
|
||||
import com.eternalcode.combat.FoliaChecker;
|
||||
import com.eternalcode.combat.WhitelistBlacklistMode;
|
||||
import com.eternalcode.combat.config.implementation.PluginConfig;
|
||||
import com.eternalcode.combat.fight.FightManager;
|
||||
@@ -56,16 +55,6 @@ public class FightTagController implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
UUID targetId = attackedPlayerByPerson.getUniqueId();
|
||||
UUID attackerId = attacker.getUniqueId();
|
||||
|
||||
if (
|
||||
targetId.equals(attackerId)
|
||||
&& FoliaChecker.isFolia()
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.config.combat.disableFlying) {
|
||||
if (attackedPlayerByPerson.isFlying()) {
|
||||
attackedPlayerByPerson.setFlying(false);
|
||||
@@ -79,9 +68,11 @@ public class FightTagController implements Listener {
|
||||
}
|
||||
|
||||
Duration combatTime = this.config.settings.combatTimerDuration;
|
||||
UUID attackedUniqueId = attackedPlayerByPerson.getUniqueId();
|
||||
UUID attackerUniqueId = attacker.getUniqueId();
|
||||
|
||||
this.fightManager.tag(targetId, combatTime, CauseOfTag.PLAYER, attackerId);
|
||||
this.fightManager.tag(attackerId, combatTime, CauseOfTag.PLAYER, targetId);
|
||||
this.fightManager.tag(attackedUniqueId, combatTime, CauseOfTag.PLAYER, attackerUniqueId);
|
||||
this.fightManager.tag(attackerUniqueId, combatTime, CauseOfTag.PLAYER, attackedUniqueId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
package com.eternalcode.combat.fight.controller;
|
||||
|
||||
import com.eternalcode.combat.fight.crystalpvp.CrystalPvpConstants;
|
||||
import com.eternalcode.combat.crystalpvp.CrystalPvpConstants;
|
||||
import com.eternalcode.combat.fight.FightManager;
|
||||
import com.eternalcode.combat.fight.event.CauseOfUnTag;
|
||||
import com.eternalcode.combat.config.implementation.PluginConfig;
|
||||
|
||||
-129
@@ -1,129 +0,0 @@
|
||||
package com.eternalcode.combat.fight.knockback;
|
||||
|
||||
import com.eternalcode.combat.fight.FightManager;
|
||||
import com.eternalcode.combat.notification.NoticeService;
|
||||
import com.eternalcode.combat.region.RegionProvider;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.logging.Level;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.EntityEvent;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
// EntityMountEvent moved between org.spigotmc, io.papermc.paper and org.bukkit across versions,
|
||||
// so we resolve it at runtime to stay compatible without bumping the compile API.
|
||||
public final class KnockbackMountController implements Listener {
|
||||
|
||||
private static final String[] EVENT_CLASS_CANDIDATES = {
|
||||
"org.bukkit.event.entity.EntityMountEvent",
|
||||
"io.papermc.paper.event.entity.EntityMountEvent",
|
||||
"org.spigotmc.event.entity.EntityMountEvent",
|
||||
};
|
||||
|
||||
private final NoticeService noticeService;
|
||||
private final RegionProvider regionProvider;
|
||||
private final FightManager fightManager;
|
||||
private final AtomicBoolean mountFailureLogged = new AtomicBoolean();
|
||||
|
||||
public KnockbackMountController(NoticeService noticeService, RegionProvider regionProvider, FightManager fightManager) {
|
||||
this.noticeService = noticeService;
|
||||
this.regionProvider = regionProvider;
|
||||
this.fightManager = fightManager;
|
||||
}
|
||||
|
||||
public void register(Plugin plugin) {
|
||||
Class<? extends Event> eventClass = null;
|
||||
for (String candidate : EVENT_CLASS_CANDIDATES) {
|
||||
try {
|
||||
eventClass = Class.forName(candidate).asSubclass(Event.class);
|
||||
break;
|
||||
} catch (ClassNotFoundException | ClassCastException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
if (eventClass == null) {
|
||||
plugin.getLogger().warning("Could not find any EntityMountEvent class; mount protection in regions is disabled.");
|
||||
return;
|
||||
}
|
||||
|
||||
MethodHandle getMountHandle;
|
||||
try {
|
||||
getMountHandle = this.resolveGetMountHandle(eventClass);
|
||||
} catch (NoSuchMethodException | IllegalAccessException exception) {
|
||||
plugin.getLogger().log(Level.WARNING, "EntityMountEvent has incompatible getMount() method; mount protection in regions is disabled.", exception);
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.getServer().getPluginManager().registerEvent(
|
||||
eventClass,
|
||||
this,
|
||||
EventPriority.HIGHEST,
|
||||
(listener, event) -> this.handle(event, getMountHandle, plugin),
|
||||
plugin,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
private MethodHandle resolveGetMountHandle(Class<? extends Event> eventClass) throws NoSuchMethodException, IllegalAccessException {
|
||||
if (!EntityEvent.class.isAssignableFrom(eventClass) || !Cancellable.class.isAssignableFrom(eventClass)) {
|
||||
throw new NoSuchMethodException(eventClass.getName() + " is not a cancellable entity event");
|
||||
}
|
||||
|
||||
Method getMount = eventClass.getMethod("getMount");
|
||||
if (!Entity.class.isAssignableFrom(getMount.getReturnType())) {
|
||||
throw new NoSuchMethodException(eventClass.getName() + "#getMount() does not return Entity");
|
||||
}
|
||||
|
||||
return MethodHandles.publicLookup()
|
||||
.unreflect(getMount)
|
||||
.asType(MethodType.methodType(Entity.class, Event.class));
|
||||
}
|
||||
|
||||
private void handle(Event event, MethodHandle getMountHandle, Plugin plugin) {
|
||||
if (!(event instanceof EntityEvent entityEvent) || !(event instanceof Cancellable cancellable)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(entityEvent.getEntity() instanceof Player player)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.fightManager.isInCombat(player.getUniqueId())) {
|
||||
return;
|
||||
}
|
||||
|
||||
Entity mount;
|
||||
try {
|
||||
mount = (Entity) getMountHandle.invokeExact(event);
|
||||
} catch (Throwable exception) {
|
||||
this.logMountFailure(plugin, exception);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mount == null || !this.regionProvider.isInRegion(mount.getLocation())) {
|
||||
return;
|
||||
}
|
||||
|
||||
cancellable.setCancelled(true);
|
||||
this.noticeService.create()
|
||||
.player(player.getUniqueId())
|
||||
.notice(config -> config.messagesSettings.cantEnterOnRegion)
|
||||
.send();
|
||||
}
|
||||
|
||||
private void logMountFailure(Plugin plugin, Throwable exception) {
|
||||
if (!this.mountFailureLogged.compareAndSet(false, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.getLogger().log(Level.WARNING, "Could not read EntityMountEvent mount; mount protection in regions is skipped.", exception);
|
||||
}
|
||||
}
|
||||
+22
@@ -17,6 +17,7 @@ import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerMoveEvent;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||
import org.bukkit.event.vehicle.VehicleMoveEvent;
|
||||
import org.spigotmc.event.entity.EntityMountEvent;
|
||||
|
||||
public class KnockbackRegionController implements Listener {
|
||||
|
||||
@@ -131,6 +132,27 @@ public class KnockbackRegionController implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
|
||||
void onEntityMount(EntityMountEvent event) {
|
||||
if (!(event.getEntity() instanceof Player player)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.fightManager.isInCombat(player.getUniqueId())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.regionProvider.isInRegion(event.getMount().getLocation())) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.setCancelled(true);
|
||||
this.noticeService.create()
|
||||
.player(player.getUniqueId())
|
||||
.notice(config -> config.messagesSettings.cantEnterOnRegion)
|
||||
.send();
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
|
||||
void onTag(FightTagEvent event) {
|
||||
Player player = this.server.getPlayer(event.getPlayer());
|
||||
|
||||
-93
@@ -1,93 +0,0 @@
|
||||
package com.eternalcode.combat.fight.spear;
|
||||
|
||||
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 org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Entity;
|
||||
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.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.UUID;
|
||||
|
||||
public class SpearLungeController implements Listener {
|
||||
|
||||
private final SpearService spearService;
|
||||
|
||||
public SpearLungeController(
|
||||
Plugin plugin,
|
||||
FightManager fightManager,
|
||||
SpearService spearService,
|
||||
PluginConfig config,
|
||||
NoticeService noticeService
|
||||
) {
|
||||
this.spearService = spearService;
|
||||
|
||||
try {
|
||||
Class<? extends Event> lungeEventClass = Class.forName("io.papermc.paper.event.entity.EntityLungeEvent").asSubclass(Event.class);
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
|
||||
MethodHandle getEntityHandle = lookup.findVirtual(lungeEventClass, "getEntity", MethodType.methodType(Entity.class));
|
||||
MethodHandle setCancelledHandle = lookup.findVirtual(lungeEventClass, "setCancelled", MethodType.methodType(void.class, boolean.class));
|
||||
|
||||
Bukkit.getPluginManager().registerEvent(
|
||||
lungeEventClass,
|
||||
this,
|
||||
EventPriority.NORMAL,
|
||||
(listener, event) -> {
|
||||
if (!config.spear.lungeCooldown || !lungeEventClass.isInstance(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Entity entity = (Entity) getEntityHandle.invoke(event);
|
||||
|
||||
if (!(entity instanceof Player player)) {
|
||||
return;
|
||||
}
|
||||
|
||||
UUID playerId = player.getUniqueId();
|
||||
|
||||
if (config.spear.onlyForFight && !fightManager.isInCombat(playerId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.spearService.isOnCooldown(playerId)) {
|
||||
setCancelledHandle.invoke(event, true);
|
||||
|
||||
noticeService.create()
|
||||
.player(playerId)
|
||||
.notice(config.spear.lungeOnCooldown)
|
||||
.placeholder("{TIME}", DurationUtil.format(spearService.getRemainingCooldown(playerId), !config.spear.useMillis))
|
||||
.send();
|
||||
|
||||
return;
|
||||
}
|
||||
spearService.saveCooldown(playerId);
|
||||
} catch (Throwable e) {
|
||||
plugin.getLogger().warning("Failed to handle EntityLungeEvent: " + e.getMessage());
|
||||
}
|
||||
},
|
||||
plugin
|
||||
);
|
||||
} catch (ClassNotFoundException e) {
|
||||
// Silently ignore: Server version is too old to have EntityLungeEvent
|
||||
} catch (Throwable e) {
|
||||
plugin.getLogger().warning("Failed to hook EntityLungeEvent: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||
this.spearService.removeCooldown(event.getPlayer().getUniqueId());
|
||||
}
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
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()
|
||||
.chat("<dark_red>Spear cannot be used for next <red>{TIME}</red></dark_red>")
|
||||
.build();
|
||||
|
||||
}
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
package com.eternalcode.combat.region.griefprevention;
|
||||
|
||||
import com.eternalcode.combat.region.Point;
|
||||
import com.eternalcode.combat.region.Region;
|
||||
import me.ryanhamshire.GriefPrevention.Claim;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
|
||||
record GriefPreventionRegion(World world, Claim claim) implements Region {
|
||||
|
||||
@Override
|
||||
public Point getCenter() {
|
||||
Location min = this.claim.getLesserBoundaryCorner();
|
||||
Location max = this.claim.getGreaterBoundaryCorner();
|
||||
|
||||
double x = (min.getX() + max.getX()) / 2.0;
|
||||
double z = (min.getZ() + max.getZ()) / 2.0;
|
||||
|
||||
return new Point(this.world, x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getMin() {
|
||||
Location min = this.claim.getLesserBoundaryCorner();
|
||||
return new Location(this.world, min.getBlockX(), this.world.getMinHeight(), min.getBlockZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getMax() {
|
||||
Location max = this.claim.getGreaterBoundaryCorner();
|
||||
return new Location(this.world, max.getBlockX(), this.world.getMaxHeight() - 1, max.getBlockZ());
|
||||
}
|
||||
|
||||
}
|
||||
-93
@@ -1,93 +0,0 @@
|
||||
package com.eternalcode.combat.region.griefprevention;
|
||||
|
||||
import com.eternalcode.combat.config.implementation.PluginConfig;
|
||||
import com.eternalcode.combat.region.Region;
|
||||
import com.eternalcode.combat.region.RegionProvider;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.TreeSet;
|
||||
import me.ryanhamshire.GriefPrevention.Claim;
|
||||
import me.ryanhamshire.GriefPrevention.GriefPrevention;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
|
||||
public class GriefPreventionRegionProvider implements RegionProvider {
|
||||
|
||||
private final TreeSet<String> claims = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
|
||||
private final PluginConfig pluginConfig;
|
||||
|
||||
public GriefPreventionRegionProvider(PluginConfig pluginConfig) {
|
||||
this.claims.addAll(pluginConfig.regions.blockedRegions);
|
||||
this.pluginConfig = pluginConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Region> getRegion(Location location) {
|
||||
Claim claim = GriefPrevention.instance.dataStore.getClaimAt(location, true, null);
|
||||
|
||||
if (claim == null || !this.isCombatClaim(claim)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.of(new GriefPreventionRegion(location.getWorld(), claim));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Region> getRegions(World world) {
|
||||
List<Region> regions = new ArrayList<>();
|
||||
|
||||
for (Claim claim : this.snapshotClaims()) {
|
||||
if (!world.equals(claim.getLesserBoundaryCorner().getWorld())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.isCombatClaim(claim)) {
|
||||
regions.add(new GriefPreventionRegion(world, claim));
|
||||
}
|
||||
}
|
||||
|
||||
return regions;
|
||||
}
|
||||
|
||||
private boolean isCombatClaim(Claim claim) {
|
||||
if (this.claims.contains(String.valueOf(claim.getID()))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!this.pluginConfig.regions.preventPvpInRegions) {
|
||||
return false;
|
||||
}
|
||||
|
||||
World world = claim.getLesserBoundaryCorner().getWorld();
|
||||
GriefPrevention griefPrevention = GriefPrevention.instance;
|
||||
|
||||
if (world == null || !griefPrevention.pvpRulesApply(world)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!claim.isAdminClaim()) {
|
||||
return griefPrevention.config_pvp_noCombatInPlayerLandClaims;
|
||||
}
|
||||
|
||||
if (claim.parent == null) {
|
||||
return griefPrevention.config_pvp_noCombatInAdminLandClaims;
|
||||
}
|
||||
|
||||
return griefPrevention.config_pvp_noCombatInAdminSubdivisions;
|
||||
}
|
||||
|
||||
// GriefPrevention returns a live view of its claim list, and the border index reads regions asynchronously.
|
||||
// A failed snapshot is retried by the next border index refresh, so an empty result is safe here.
|
||||
private List<Claim> snapshotClaims() {
|
||||
try {
|
||||
return List.copyOf(GriefPrevention.instance.dataStore.getClaims());
|
||||
}
|
||||
catch (ConcurrentModificationException exception) {
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
package com.eternalcode.combat.config.implementation;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class SignEditingSettingsTest {
|
||||
|
||||
@Test
|
||||
void shouldDisableSignEditingBlockerByDefault() {
|
||||
SignEditingSettings signEditingSettings = new SignEditingSettings();
|
||||
|
||||
assertFalse(signEditingSettings.disableSignEditingDuringCombat);
|
||||
}
|
||||
}
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
package com.eternalcode.combat.fight.blocker;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class SignEditingBlockerTest {
|
||||
|
||||
@Test
|
||||
void shouldRecognizeStandingSignMaterials() {
|
||||
assertTrue(SignEditingBlocker.isSign(Material.OAK_SIGN));
|
||||
assertTrue(SignEditingBlocker.isSign(Material.CRIMSON_SIGN));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRecognizeWallSignMaterials() {
|
||||
assertTrue(SignEditingBlocker.isSign(Material.OAK_WALL_SIGN));
|
||||
assertTrue(SignEditingBlocker.isSign(Material.WARPED_WALL_SIGN));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldIgnoreNonSignMaterials() {
|
||||
assertFalse(SignEditingBlocker.isSign(Material.CHEST));
|
||||
assertFalse(SignEditingBlocker.isSign(Material.ENDER_PEARL));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRecognizeEnderPearlMaterial() {
|
||||
assertTrue(SignEditingBlocker.isEnderPearl(Material.ENDER_PEARL));
|
||||
assertFalse(SignEditingBlocker.isEnderPearl(Material.OAK_SIGN));
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -4,4 +4,4 @@ org.gradle.configuration-cache=true
|
||||
org.gradle.configuration-cache.parallel=true
|
||||
|
||||
# Platforms
|
||||
paperVersion=1.19.3,1.19.4,1.20,1.20.1,1.20.2,1.20.4,1.20.5,1.20.6,1.21,1.21.1,1.21.3,1.21.4,1.21.5,1.21.6,1.21.7,1.21.8,1.21.9,1.21.10,1.21.11,26.1.1,26.1.2,26.2
|
||||
paperVersion=1.19.3,1.19.4,1.20,1.20.1,1.20.2,1.20.4,1.20.5,1.20.6,1.21,1.21.1,1.21.3,1.21.4,1.21.5,1.21.6,1.21.7,1.21.8,1.21.9,1.21.10,1.21.11,26.1.1,26.1.2
|
||||
Vendored
BIN
Binary file not shown.
+1
-1
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.8.0-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.0-bin.zip
|
||||
networkTimeout=10000
|
||||
retries=0
|
||||
retryBackOffMs=500
|
||||
|
||||
Reference in new issue
Block a user