From 203c4491a0091226a993ebafb2d91e1480a479f9 Mon Sep 17 00:00:00 2001 From: retiolus Date: Mon, 19 Jan 2026 07:55:28 +0100 Subject: [PATCH 1/2] add pvp off timeout --- README.md | 4 + .../me/jack/pvptoggle/PvPTogglePlugin.java | 4 +- .../pvptoggle/commands/PvPOffCommand.java | 12 ++- .../jack/pvptoggle/commands/PvPOnCommand.java | 3 +- .../pvptoggle/commands/PvPToggleCommand.java | 14 +++- .../commands/admin/PvPAdminConfigCommand.java | 9 +- .../commands/admin/PvPAdminSetCommand.java | 12 ++- .../components/PvPToggleComponent.java | 49 +++++++++++ .../pvptoggle/config/PvPToggleConfig.java | 14 ++++ .../constants/PvPToggleConstants.java | 1 + .../systems/PvPOffTimeoutSystem.java | 82 +++++++++++++++++++ .../pvptoggle/util/PvPToggleMessageUtil.java | 19 +++++ .../Server/Languages/en-US/pvptoggle.lang | 7 +- 13 files changed, 223 insertions(+), 7 deletions(-) create mode 100644 src/main/java/me/jack/pvptoggle/systems/PvPOffTimeoutSystem.java create mode 100644 src/main/java/me/jack/pvptoggle/util/PvPToggleMessageUtil.java diff --git a/README.md b/README.md index e4f3e49..ad294ca 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ PvP Toggle gives players control over their PvP status. Players can choose to op - **Per-player PvP toggle** - Each player can independently enable or disable their PvP status - **Combat protection** - Players cannot toggle PvP while actively in combat - **Toggle cooldown** - Configurable cooldown between PvP state changes to prevent abuse +- **Off timeout** - Delay before PvP can be turned off after enabling - **Persistent state** - PvP status is saved across server restarts - **Admin controls** - Server administrators can view and modify plugin settings in-game @@ -23,6 +24,7 @@ When a player has PvP disabled: If either the attacker or the target has PvP disabled, no damage is dealt. The combat timer prevents players from disabling PvP mid-fight. After engaging in PvP combat, players must wait for the combat timer to expire before they can change their PvP status. +The off timeout (if configured) requires players to keep PvP enabled for a minimum time before turning it off, and will queue the request to disable when used early. ## Commands @@ -47,6 +49,7 @@ The combat timer prevents players from disabling PvP mid-fight. After engaging i |------------------|------|----------------------------------------------------------------| | `combattimer` | seconds | How long after PvP damage before you can toggle (0 to disable) | | `cooldown` | seconds | How long between PvP toggles (0 to disable) | +| `offtimeout` | seconds | Minimum time after enabling PvP before disabling (0 to disable) | | `default` | true/false | Default PvP state for new players | | `persist` | true/false | Save PvP state across server restarts | | `itemprotection` | true/false | Enable item protection for PvP players | @@ -59,6 +62,7 @@ Boolean values accept: `true`, `false`, `yes`, `no`, `on`, `off`, `1`, `0` |-------------------|------------| | Combat Timer | 10 seconds | | Toggle Cooldown | 5 seconds | +| Off Timeout | 0 seconds | | Default PvP State | Disabled | | Data Persistence | Enabled | | Item Protection | Enabled | diff --git a/src/main/java/me/jack/pvptoggle/PvPTogglePlugin.java b/src/main/java/me/jack/pvptoggle/PvPTogglePlugin.java index 0dbe83f..6ed2d8d 100644 --- a/src/main/java/me/jack/pvptoggle/PvPTogglePlugin.java +++ b/src/main/java/me/jack/pvptoggle/PvPTogglePlugin.java @@ -13,6 +13,7 @@ import me.jack.pvptoggle.config.PvPToggleConfig; import me.jack.pvptoggle.systems.CombatTrackingSystem; import me.jack.pvptoggle.systems.PreventDamageSystem; +import me.jack.pvptoggle.systems.PvPOffTimeoutSystem; import me.jack.pvptoggle.systems.PvPItemProtectionSystem; import javax.annotation.Nonnull; @@ -70,6 +71,7 @@ protected void setup() { this.getEntityStoreRegistry().registerSystem(new CombatTrackingSystem()); this.getEntityStoreRegistry().registerSystem(new PreventDamageSystem()); this.getEntityStoreRegistry().registerSystem(new PvPItemProtectionSystem()); + this.getEntityStoreRegistry().registerSystem(new PvPOffTimeoutSystem()); this.getCommandRegistry().registerCommand(new PvPCommand()); } @@ -85,4 +87,4 @@ private void onPlayerReady(@Nonnull PlayerReadyEvent event) { playerRef.getStore().ensureComponent(playerRef, this.pvpToggleComponentType); } } -} \ No newline at end of file +} diff --git a/src/main/java/me/jack/pvptoggle/commands/PvPOffCommand.java b/src/main/java/me/jack/pvptoggle/commands/PvPOffCommand.java index 26d7fc9..b165ead 100644 --- a/src/main/java/me/jack/pvptoggle/commands/PvPOffCommand.java +++ b/src/main/java/me/jack/pvptoggle/commands/PvPOffCommand.java @@ -10,6 +10,7 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import me.jack.pvptoggle.components.PvPToggleComponent; +import me.jack.pvptoggle.util.PvPToggleMessageUtil; import org.checkerframework.checker.nullness.compatqual.NonNullDecl; import java.time.Instant; @@ -49,6 +50,14 @@ protected void execute( return; } + long remainingOffTimeout = pvp.getRemainingOffTimeoutSeconds(); + if (remainingOffTimeout > 0) { + pvp.setPendingDisableAt(pvp.getOffTimeoutEndTime()); + pvp.setLastDisableAnnouncementSeconds(remainingOffTimeout); + commandContext.sendMessage(PvPToggleMessageUtil.buildDisableCountdownMessage(remainingOffTimeout)); + return; + } + if (pvp.isOnCooldown()) { commandContext.sendMessage(Message.translation("pvptoggle.toggle_cooldown").param("timeLeft", pvp.getRemainingCooldown())); return; @@ -56,6 +65,7 @@ protected void execute( pvp.setPvPEnabled(false); pvp.setLastToggleTime(Instant.now()); + pvp.clearPendingDisable(); commandContext.sendMessage(Message.translation("pvptoggle.off")); } -} \ No newline at end of file +} diff --git a/src/main/java/me/jack/pvptoggle/commands/PvPOnCommand.java b/src/main/java/me/jack/pvptoggle/commands/PvPOnCommand.java index b477098..8f647ce 100644 --- a/src/main/java/me/jack/pvptoggle/commands/PvPOnCommand.java +++ b/src/main/java/me/jack/pvptoggle/commands/PvPOnCommand.java @@ -56,6 +56,7 @@ protected void execute( pvp.setPvPEnabled(true); pvp.setLastToggleTime(Instant.now()); + pvp.clearPendingDisable(); commandContext.sendMessage(Message.translation("pvptoggle.on")); } -} \ No newline at end of file +} diff --git a/src/main/java/me/jack/pvptoggle/commands/PvPToggleCommand.java b/src/main/java/me/jack/pvptoggle/commands/PvPToggleCommand.java index 9456aa6..b2c07ac 100644 --- a/src/main/java/me/jack/pvptoggle/commands/PvPToggleCommand.java +++ b/src/main/java/me/jack/pvptoggle/commands/PvPToggleCommand.java @@ -10,6 +10,7 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import me.jack.pvptoggle.components.PvPToggleComponent; +import me.jack.pvptoggle.util.PvPToggleMessageUtil; import org.checkerframework.checker.nullness.compatqual.NonNullDecl; import java.time.Instant; @@ -44,6 +45,16 @@ protected void execute( return; } + if (pvp.isPvPEnabled()) { + long remainingOffTimeout = pvp.getRemainingOffTimeoutSeconds(); + if (remainingOffTimeout > 0) { + pvp.setPendingDisableAt(pvp.getOffTimeoutEndTime()); + pvp.setLastDisableAnnouncementSeconds(remainingOffTimeout); + commandContext.sendMessage(PvPToggleMessageUtil.buildDisableCountdownMessage(remainingOffTimeout)); + return; + } + } + if (pvp.isOnCooldown()) { commandContext.sendMessage(Message.translation("pvptoggle.toggle_cooldown").param("timeLeft", pvp.getRemainingCooldown())); return; @@ -53,6 +64,7 @@ protected void execute( pvp.setPvPEnabled(!pvp.isPvPEnabled()); pvp.setLastToggleTime(Instant.now()); + pvp.clearPendingDisable(); commandContext.sendMessage(Message.translation(messageKey)); } -} \ No newline at end of file +} diff --git a/src/main/java/me/jack/pvptoggle/commands/admin/PvPAdminConfigCommand.java b/src/main/java/me/jack/pvptoggle/commands/admin/PvPAdminConfigCommand.java index b1a2429..bc65dd9 100644 --- a/src/main/java/me/jack/pvptoggle/commands/admin/PvPAdminConfigCommand.java +++ b/src/main/java/me/jack/pvptoggle/commands/admin/PvPAdminConfigCommand.java @@ -33,6 +33,12 @@ protected CompletableFuture execute(@Nonnull CommandContext context) { : Message.translation("pvptoggle.common.disabled"); context.sendMessage(Message.translation("pvptoggle.config.toggle_cooldown").param("value", cooldownValue)); + long offTimeout = config.getOffTimeoutSeconds(); + Message offTimeoutValue = offTimeout > 0 + ? Message.translation("pvptoggle.common.seconds").param("count", offTimeout) + : Message.translation("pvptoggle.common.disabled"); + context.sendMessage(Message.translation("pvptoggle.config.off_timeout").param("value", offTimeoutValue)); + context.sendMessage(Message.translation("pvptoggle.config.default_state") .param("state", Message.translation(config.isDefaultPvPEnabled() ? "pvptoggle.common.enabled" : "pvptoggle.common.disabled"))); @@ -49,6 +55,7 @@ protected CompletableFuture execute(@Nonnull CommandContext context) { context.sendMessage(Message.translation("pvptoggle.config.help.title")); context.sendMessage(Message.translation("pvptoggle.config.help.combat")); context.sendMessage(Message.translation("pvptoggle.config.help.cooldown")); + context.sendMessage(Message.translation("pvptoggle.config.help.offtimeout")); context.sendMessage(Message.translation("pvptoggle.config.help.default")); context.sendMessage(Message.translation("pvptoggle.config.help.persist")); context.sendMessage(Message.translation("pvptoggle.config.help.itemprotection")); @@ -56,4 +63,4 @@ protected CompletableFuture execute(@Nonnull CommandContext context) { return CompletableFuture.completedFuture(null); } -} \ No newline at end of file +} diff --git a/src/main/java/me/jack/pvptoggle/commands/admin/PvPAdminSetCommand.java b/src/main/java/me/jack/pvptoggle/commands/admin/PvPAdminSetCommand.java index de2fe90..4e3f288 100644 --- a/src/main/java/me/jack/pvptoggle/commands/admin/PvPAdminSetCommand.java +++ b/src/main/java/me/jack/pvptoggle/commands/admin/PvPAdminSetCommand.java @@ -25,7 +25,7 @@ public class PvPAdminSetCommand extends AbstractCommand { public PvPAdminSetCommand() { super("set", "Set a config value"); - this.keyArg = this.withRequiredArg("key", "Config key (combattimer, cooldown, default, persist, itemprotection)", ArgTypes.STRING); + this.keyArg = this.withRequiredArg("key", "Config key (combattimer, cooldown, offtimeout, default, persist, itemprotection, knockback)", ArgTypes.STRING); this.valueArg = this.withRequiredArg("value", "New value", ArgTypes.STRING); } @@ -57,6 +57,16 @@ protected CompletableFuture execute(@Nonnull CommandContext context) { Message timerMsg = Message.translation(seconds == 0 ? "pvptoggle.common.seconds_disabled" : "pvptoggle.common.seconds").param("count", seconds); context.sendMessage(Message.translation("pvptoggle.admin.set.toggle_cooldown").param("value", timerMsg)); } + case "offtimeout" -> { + Long seconds = parseLong(value); + if (seconds == null) { + context.sendMessage(MSG_INVALID_VALUE); + return CompletableFuture.completedFuture(null); + } + config.setOffTimeoutSeconds(seconds); + Message timerMsg = Message.translation(seconds == 0 ? "pvptoggle.common.seconds_disabled" : "pvptoggle.common.seconds").param("count", seconds); + context.sendMessage(Message.translation("pvptoggle.admin.set.off_timeout").param("value", timerMsg)); + } case "default" -> { Boolean enabled = parseBoolean(value); if (enabled == null) { diff --git a/src/main/java/me/jack/pvptoggle/components/PvPToggleComponent.java b/src/main/java/me/jack/pvptoggle/components/PvPToggleComponent.java index 9fea5df..000f157 100644 --- a/src/main/java/me/jack/pvptoggle/components/PvPToggleComponent.java +++ b/src/main/java/me/jack/pvptoggle/components/PvPToggleComponent.java @@ -13,6 +13,8 @@ public class PvPToggleComponent implements Component { private boolean pvpEnabled; private Instant lastToggleTime = Instant.EPOCH; private Instant lastCombatTime = Instant.EPOCH; + private Instant pendingDisableAt = Instant.EPOCH; + private long lastDisableAnnouncementSeconds = -1; public PvPToggleComponent() { this.pvpEnabled = PvPTogglePlugin.CONFIG.get().isDefaultPvPEnabled(); @@ -57,6 +59,51 @@ public void setLastCombatTime(Instant lastCombatTime) { this.lastCombatTime = lastCombatTime; } + public boolean hasPendingDisable() { + return !Instant.EPOCH.equals(this.pendingDisableAt); + } + + public Instant getPendingDisableAt() { + return pendingDisableAt; + } + + public void setPendingDisableAt(Instant pendingDisableAt) { + this.pendingDisableAt = pendingDisableAt; + } + + public void clearPendingDisable() { + this.pendingDisableAt = Instant.EPOCH; + this.lastDisableAnnouncementSeconds = -1; + } + + public long getRemainingPendingDisableSeconds() { + if (!hasPendingDisable()) return 0; + return Math.max(0, pendingDisableAt.getEpochSecond() - Instant.now().getEpochSecond()); + } + + public long getRemainingOffTimeoutSeconds() { + if (!pvpEnabled) return 0; + long timeout = PvPTogglePlugin.CONFIG.get().getOffTimeoutSeconds(); + if (timeout <= 0) return 0; + Instant disableAt = this.lastToggleTime.plusSeconds(timeout); + return Math.max(0, disableAt.getEpochSecond() - Instant.now().getEpochSecond()); + } + + public Instant getOffTimeoutEndTime() { + if (!pvpEnabled) return Instant.EPOCH; + long timeout = PvPTogglePlugin.CONFIG.get().getOffTimeoutSeconds(); + if (timeout <= 0) return Instant.EPOCH; + return this.lastToggleTime.plusSeconds(timeout); + } + + public long getLastDisableAnnouncementSeconds() { + return lastDisableAnnouncementSeconds; + } + + public void setLastDisableAnnouncementSeconds(long lastDisableAnnouncementSeconds) { + this.lastDisableAnnouncementSeconds = lastDisableAnnouncementSeconds; + } + public boolean isInCombat() { long duration = PvPTogglePlugin.CONFIG.get().getCombatTimerSeconds(); if (duration <= 0) return false; @@ -89,6 +136,8 @@ public Component clone() { clone.lastCombatTime = this.lastCombatTime; clone.lastToggleTime = this.lastToggleTime; + clone.pendingDisableAt = this.pendingDisableAt; + clone.lastDisableAnnouncementSeconds = this.lastDisableAnnouncementSeconds; return clone; } diff --git a/src/main/java/me/jack/pvptoggle/config/PvPToggleConfig.java b/src/main/java/me/jack/pvptoggle/config/PvPToggleConfig.java index 93e3da1..47372b3 100644 --- a/src/main/java/me/jack/pvptoggle/config/PvPToggleConfig.java +++ b/src/main/java/me/jack/pvptoggle/config/PvPToggleConfig.java @@ -13,6 +13,7 @@ public class PvPToggleConfig { private long combatTimerSeconds = PvPToggleConstants.COMBAT_TIMER_SECONDS; private long toggleCooldownSeconds = PvPToggleConstants.TOGGLE_COOLDOWN_SECONDS; + private long offTimeoutSeconds = PvPToggleConstants.OFF_TIMEOUT_SECONDS; public static final BuilderCodec CODEC = BuilderCodec .builder(PvPToggleConfig.class, PvPToggleConfig::new) @@ -28,6 +29,9 @@ public class PvPToggleConfig { .append(new KeyedCodec<>("ToggleCooldownSeconds", Codec.LONG), (config, value) -> config.toggleCooldownSeconds = value, (config) -> config.toggleCooldownSeconds).add() + .append(new KeyedCodec<>("OffTimeoutSeconds", Codec.LONG), + (config, value) -> config.offTimeoutSeconds = value, + (config) -> config.offTimeoutSeconds).add() .append(new KeyedCodec<>("ItemProtectionEnabled", Codec.BOOLEAN), (config, value) -> config.itemProtectionEnabled = value, (config) -> config.itemProtectionEnabled).add() @@ -76,6 +80,16 @@ public PvPToggleConfig setToggleCooldownSeconds(long toggleCooldownSeconds) { return this; } + public long getOffTimeoutSeconds() { + return offTimeoutSeconds; + } + + public PvPToggleConfig setOffTimeoutSeconds(long offTimeoutSeconds) { + this.offTimeoutSeconds = offTimeoutSeconds; + + return this; + } + public boolean isItemProtectionEnabled() { return itemProtectionEnabled; } diff --git a/src/main/java/me/jack/pvptoggle/constants/PvPToggleConstants.java b/src/main/java/me/jack/pvptoggle/constants/PvPToggleConstants.java index 26e0d2a..26cce5a 100644 --- a/src/main/java/me/jack/pvptoggle/constants/PvPToggleConstants.java +++ b/src/main/java/me/jack/pvptoggle/constants/PvPToggleConstants.java @@ -8,4 +8,5 @@ public class PvPToggleConstants { public static final long TOGGLE_COOLDOWN_SECONDS = 5; public static final long COMBAT_TIMER_SECONDS = 10; + public static final long OFF_TIMEOUT_SECONDS = 0; } diff --git a/src/main/java/me/jack/pvptoggle/systems/PvPOffTimeoutSystem.java b/src/main/java/me/jack/pvptoggle/systems/PvPOffTimeoutSystem.java new file mode 100644 index 0000000..7604050 --- /dev/null +++ b/src/main/java/me/jack/pvptoggle/systems/PvPOffTimeoutSystem.java @@ -0,0 +1,82 @@ +package me.jack.pvptoggle.systems; + +import com.hypixel.hytale.component.ArchetypeChunk; +import com.hypixel.hytale.component.CommandBuffer; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.component.query.Query; +import com.hypixel.hytale.component.system.tick.EntityTickingSystem; +import com.hypixel.hytale.server.core.Message; +import com.hypixel.hytale.server.core.universe.PlayerRef; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import me.jack.pvptoggle.components.PvPToggleComponent; +import me.jack.pvptoggle.util.PvPToggleMessageUtil; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.checkerframework.checker.nullness.compatqual.NullableDecl; + +import java.time.Instant; + +public class PvPOffTimeoutSystem extends EntityTickingSystem { + @Override + public void tick( + float dt, + int index, + @NonNullDecl ArchetypeChunk archetypeChunk, + @NonNullDecl Store store, + @NonNullDecl CommandBuffer commandBuffer + ) { + Ref ref = archetypeChunk.getReferenceTo(index); + PvPToggleComponent pvp = (PvPToggleComponent) commandBuffer.getComponent(ref, PvPToggleComponent.getComponentType()); + + if (pvp == null) { + return; + } + + if (!pvp.isPvPEnabled()) { + if (pvp.hasPendingDisable()) { + pvp.clearPendingDisable(); + } + return; + } + + if (!pvp.hasPendingDisable()) { + return; + } + + long remainingSeconds = pvp.getRemainingPendingDisableSeconds(); + + if (remainingSeconds <= 0) { + if (pvp.isInCombat()) { + return; + } + + pvp.setPvPEnabled(false); + pvp.setLastToggleTime(Instant.now()); + pvp.clearPendingDisable(); + + PlayerRef playerRef = store.getComponent(ref, PlayerRef.getComponentType()); + if (playerRef != null) { + playerRef.sendMessage(Message.translation("pvptoggle.off")); + } + return; + } + + if (remainingSeconds == pvp.getLastDisableAnnouncementSeconds()) { + return; + } + + if (remainingSeconds <= 10 || (remainingSeconds >= 60 && remainingSeconds % 60 == 0)) { + PlayerRef playerRef = store.getComponent(ref, PlayerRef.getComponentType()); + if (playerRef != null) { + playerRef.sendMessage(PvPToggleMessageUtil.buildDisableCountdownMessage(remainingSeconds)); + } + pvp.setLastDisableAnnouncementSeconds(remainingSeconds); + } + } + + @NullableDecl + @Override + public Query getQuery() { + return PlayerRef.getComponentType(); + } +} diff --git a/src/main/java/me/jack/pvptoggle/util/PvPToggleMessageUtil.java b/src/main/java/me/jack/pvptoggle/util/PvPToggleMessageUtil.java new file mode 100644 index 0000000..fc432fb --- /dev/null +++ b/src/main/java/me/jack/pvptoggle/util/PvPToggleMessageUtil.java @@ -0,0 +1,19 @@ +package me.jack.pvptoggle.util; + +import com.hypixel.hytale.server.core.Message; + +public final class PvPToggleMessageUtil { + private PvPToggleMessageUtil() { + } + + public static Message buildDisableCountdownMessage(long remainingSeconds) { + if (remainingSeconds <= 0) { + return Message.translation("pvptoggle.off"); + } + if (remainingSeconds < 60) { + return Message.translation("pvptoggle.off_countdown_seconds").param("seconds", remainingSeconds); + } + long minutes = remainingSeconds / 60; + return Message.translation("pvptoggle.off_countdown_minutes").param("minutes", minutes); + } +} diff --git a/src/main/resources/Server/Languages/en-US/pvptoggle.lang b/src/main/resources/Server/Languages/en-US/pvptoggle.lang index ce74fdc..74da1c2 100644 --- a/src/main/resources/Server/Languages/en-US/pvptoggle.lang +++ b/src/main/resources/Server/Languages/en-US/pvptoggle.lang @@ -6,17 +6,20 @@ already_disabled = PvP is already disabled. combat_cooldown = You cannot toggle PvP while in combat! Wait {timeLeft} seconds. toggle_cooldown = You are toggling too quick! Wait {timeLeft} seconds. +off_countdown_minutes = PvP will turn off in {minutes} minute(s). +off_countdown_seconds = PvP will turn off in {seconds} second(s). status.enabled = Your PvP is currently enabled. Item protection is currently {itemProtection} status.disabled = Your PvP is currently disabled. Item protection is currently {itemProtection} -errors.admin.set.invalid_key = Invalid key. Valid keys: combattimer, cooldown, default, persist, itemprotection, knockback +errors.admin.set.invalid_key = Invalid key. Valid keys: combattimer, cooldown, offtimeout, default, persist, itemprotection, knockback errors.admin.set.invalid_value = Invalid value. Use a number for timers, or true/false/yes/no/on/off for toggles. errors.admin.set.persist_restart = Note: 'persist' changes require a server restart to take effect. config.title = PvP Toggle Config: config.combat_timer = Combat timer: {value} config.toggle_cooldown = Toggle cooldown: {value} +config.off_timeout = Off timeout: {value} config.default_state = Default PvP state: {state} config.persist_data = Persist data across restarts: {state} config.item_protection = Item protection enabled: {state} @@ -24,6 +27,7 @@ config.knockback = Knockback: {state} config.help.title = To change these settings: config.help.combat = /pvp admin set combattimer config.help.cooldown = /pvp admin set cooldown +config.help.offtimeout = /pvp admin set offtimeout config.help.default = /pvp admin set default config.help.persist = /pvp admin set persist config.help.itemprotection = /pvp admin set itemprotection @@ -31,6 +35,7 @@ config.help.knockback = /pvp admin set knockback admin.set.combat_timer = Combat timer set to {value} admin.set.toggle_cooldown = Toggle cooldown set to {value} +admin.set.off_timeout = Off timeout set to {value} admin.set.default_state = Default PvP state set to {state} admin.set.persist_data = Persist PvP state set to {state} admin.set.item_protection = Loot protection set to {state} From a3f7d06b55431b5915ceb97f68d6ac8c07fec065 Mon Sep 17 00:00:00 2001 From: retiolus Date: Mon, 19 Jan 2026 08:03:43 +0100 Subject: [PATCH 2/2] add public pvp on message --- .../java/me/jack/pvptoggle/commands/PvPOnCommand.java | 2 ++ .../java/me/jack/pvptoggle/commands/PvPToggleCommand.java | 8 ++++++-- .../java/me/jack/pvptoggle/util/PvPToggleMessageUtil.java | 8 ++++++++ src/main/resources/Server/Languages/en-US/pvptoggle.lang | 1 + 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/main/java/me/jack/pvptoggle/commands/PvPOnCommand.java b/src/main/java/me/jack/pvptoggle/commands/PvPOnCommand.java index 8f647ce..1135e32 100644 --- a/src/main/java/me/jack/pvptoggle/commands/PvPOnCommand.java +++ b/src/main/java/me/jack/pvptoggle/commands/PvPOnCommand.java @@ -10,6 +10,7 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import me.jack.pvptoggle.components.PvPToggleComponent; +import me.jack.pvptoggle.util.PvPToggleMessageUtil; import org.checkerframework.checker.nullness.compatqual.NonNullDecl; import java.time.Instant; @@ -58,5 +59,6 @@ protected void execute( pvp.setLastToggleTime(Instant.now()); pvp.clearPendingDisable(); commandContext.sendMessage(Message.translation("pvptoggle.on")); + world.sendMessage(PvPToggleMessageUtil.buildPublicPvpOnMessage(playerRef.getUsername())); } } diff --git a/src/main/java/me/jack/pvptoggle/commands/PvPToggleCommand.java b/src/main/java/me/jack/pvptoggle/commands/PvPToggleCommand.java index b2c07ac..afc6301 100644 --- a/src/main/java/me/jack/pvptoggle/commands/PvPToggleCommand.java +++ b/src/main/java/me/jack/pvptoggle/commands/PvPToggleCommand.java @@ -60,11 +60,15 @@ protected void execute( return; } - String messageKey = pvp.isPvPEnabled() ? "pvptoggle.off" : "pvptoggle.on"; + boolean newState = !pvp.isPvPEnabled(); + String messageKey = newState ? "pvptoggle.on" : "pvptoggle.off"; - pvp.setPvPEnabled(!pvp.isPvPEnabled()); + pvp.setPvPEnabled(newState); pvp.setLastToggleTime(Instant.now()); pvp.clearPendingDisable(); commandContext.sendMessage(Message.translation(messageKey)); + if (newState) { + world.sendMessage(PvPToggleMessageUtil.buildPublicPvpOnMessage(playerRef.getUsername())); + } } } diff --git a/src/main/java/me/jack/pvptoggle/util/PvPToggleMessageUtil.java b/src/main/java/me/jack/pvptoggle/util/PvPToggleMessageUtil.java index fc432fb..7eb00d0 100644 --- a/src/main/java/me/jack/pvptoggle/util/PvPToggleMessageUtil.java +++ b/src/main/java/me/jack/pvptoggle/util/PvPToggleMessageUtil.java @@ -2,10 +2,18 @@ import com.hypixel.hytale.server.core.Message; +import java.awt.Color; + public final class PvPToggleMessageUtil { private PvPToggleMessageUtil() { } + public static Message buildPublicPvpOnMessage(String playerName) { + return Message.translation("pvptoggle.public_on") + .param("player", playerName) + .color(Color.RED); + } + public static Message buildDisableCountdownMessage(long remainingSeconds) { if (remainingSeconds <= 0) { return Message.translation("pvptoggle.off"); diff --git a/src/main/resources/Server/Languages/en-US/pvptoggle.lang b/src/main/resources/Server/Languages/en-US/pvptoggle.lang index 74da1c2..e3054be 100644 --- a/src/main/resources/Server/Languages/en-US/pvptoggle.lang +++ b/src/main/resources/Server/Languages/en-US/pvptoggle.lang @@ -1,5 +1,6 @@ on = PvP is now enabled. You can attack and be attacked by other players. off = PvP is now disabled. You cannot attack or be attacked by other players. +public_on = {player} has enabled PvP. already_enabled = PvP is already enabled. already_disabled = PvP is already disabled.