Перейти к содержимому
Mineforgian

Combat Toggle

Combat Toggle is a lightweight server-side PvP control mod that prevents accidental or unwanted player combat by requiring mutual opt-in before PvP damage is allowed. Combat only activates when both players are in Combat mode.

Загрузки
117
Подписчики
0
Обновлён
15 мая 2026 г.
Лицензия
GPL-3.0-or-later

Опубликован 15 мая 2026 г.

Combat Toggle

A server-installable PvP opt-in system with combat tagging, smart cooldowns, and a full admin toolkit — for Minecraft 1.19.2 Forge.

Combat Toggle lets every player on your server choose whether they can be attacked. It's a ship-it-on-a-modpack solution for the "some players want PvP, some don't" problem — no world-border workarounds, no region plugins, no third-party dependencies. Install it on your dedicated server and vanilla clients can join and use /ct without touching their game.


What it does

  • Peace mode — PvP is off. Other players' arrows, swords, potions, and projectiles pass harmlessly.
  • Combat mode — PvP is on. You can be hit, and you can hit back.
  • Each player decides independently. The default is Peace — new players must opt in.
  • Server-installable. Vanilla clients can join and use /ct to flip modes with no client install. Modded clients additionally get a rebindable V keybind and an on-screen HUD indicator.

Features

Core

  • Universal commands. /ct, /combat, /peace, and /combattoggle status work from any client — modded or vanilla.
  • One-key toggle. Default keybind: V (modded clients only). Fully rebindable per-client.
  • HUD overlay. 51×19 pixel-art icon (shield for Peace, crossed swords for Combat). Configurable position (9-anchor + custom XY), scale, and per-mode visibility. Hides automatically in inventories and when the HUD is toggled off with F1.
  • Combat tagging. Any PvP engagement tags both attacker and victim for a configurable duration (default 30s). While tagged, a player cannot switch to Peace mode — no combat-logging via the toggle.
  • Smart cooldowns. By default, the cooldown starts only when PvP actually happens, not when players toggle. Players can freely switch modes while farming, building, or exploring.
  • Persistent state. Mode, tag timer, and cooldown survive logouts, respawns, dimension changes, and server restarts. Stored on a Forge capability serialized to player data.
  • Dual-event PvP enforcement. Primary cancel at LivingAttackEvent (HIGH priority) — knockback, hurt sound, and the PLAYER_HURT_ENTITY advancement criterion never fire on Peace targets. LivingHurtEvent runs as defence-in-depth. Attacker resolution walks projectile owners, primed-TNT owners, and tamed pets, so wolves, snow golems, and ignited TNT are correctly attributed.

Social

  • Nameplate coloring. Players in Combat mode get red nameplates; Peace players get blue. Disable if you already use scoreboard teams for something else.
  • Emoji/text prefixes. Configurable glyphs (defaults: crossed swords and a coffee mug) appear before the player name so the state is unmistakable from any angle.
  • Throttled feedback. Attackers who hit a peace-mode target get a one-line chat message explaining why nothing happened (throttled so it never spams).

Player commands

No permissions required.

Command Purpose
/ct Toggle Combat ↔ Peace.
/combat Set yourself to Combat mode.
/peace Set yourself to Peace mode.
/combattoggle status Show your own mode, tag, and cooldown.
/combattoggle help List every command you can run (admin entries hidden for non-OP).

Admin commands

Require OP level 2.

Command Purpose
/combattoggle get <player> Inspect another player's state.
/combattoggle set <player> <peace|combat> [bypass] Force a mode. bypass=true overrides cooldown + tag guard.
/combattoggle tag <player> [seconds] Apply a combat tag manually (1–3600s).
/combattoggle untag <player> Clear a combat tag.
/combattoggle resetcooldown <player> Clear both toggle and PvP cooldowns.
/combattoggle resync Refresh scoreboard teams and resync HUD state for everyone online (/combattoggle reload is an alias).

Server/Modpack Info

  • Capability-backed state. Player data lives on a Forge capability (CombatToggleData); same in-memory instance for the player's whole session, no per-call NBT churn. There's no public API today, but a future minor can expose one without re-architecting.
  • Structured SLF4J logging at DEBUG/INFO for every PvP decision, toggle request, and command invocation. Plays nicely with ops pipelines.
  • Translation-driven messages. Every player-facing string is a translation key (combattoggle.msg.*). Ship translations without code patches.
  • Vanilla-tolerant network handshake. The channel uses NetworkRegistry.acceptMissingOr(PROTOCOL::equals); vanilla clients pass through, sync packets are dropped silently to peers without the channel.

Installation

  1. Install Minecraft Forge 43.3.0+ for Minecraft 1.19.2.
  2. Drop combattoggle-1.2.0.jar into your server's (or client's) mods/ folder.
  3. Launch. On first run, combattoggle-server.toml is generated in your world's serverconfig/ directory and combattoggle-client.toml in the client config folder.

Server-installable? Yes. Install on the dedicated server only and vanilla clients can join and use /ct, /combat, /peace, and /combattoggle status with no client-side install. Players who additionally install the mod client-side get the V keybind and the HUD indicator on top — eye candy over the same server-authoritative logic.


Configuration

  • combattoggle-server.toml
Key Default What it does
cooldownSeconds 600 Cooldown duration (10 min). Set to 0 to disable cooldown entirely.
combatTagSeconds 30 How long a combat tag lasts after a PvP event.
requireBothCombatEnabled true If true, PvP only works when both attacker and target are in Combat.
forceCombatWhileTagged true Force tagged players into Combat mode.
allowToggleWhileTagged false If false, Combat-tagged players cannot switch to Peace.
cooldownTriggersOnPvp true Cooldown starts when a player deals or takes PvP damage.
cooldownTriggersOnToggle false Cooldown starts when a player toggles.
cooldownAppliesToPeaceOnly true Cooldown only blocks Combat→Peace; Peace→Combat is always instant.
allowAdminBypassCooldown true bypass=true on /combattoggle set actually works.
useScoreboardTeams true Disable to keep Combat Toggle out of the scoreboard entirely.
useEmojiPrefixes / useNameplateColors true / true Independent toggles for the two visual effects.
combatEmoji / peaceEmoji / Any BMP Unicode.
combatTeamName / peaceTeamName ct_combat / ct_peace Rename to avoid clashes.
  • combattoggle-client.toml
Key Default What it does
hudEnabled true Master switch for the HUD icon.
hudShowInCombatMode true Show the icon while in Combat mode.
hudShowInPeaceMode true Show the icon while in Peace mode.
hudAnchor TOP_CENTER One of TOP_LEFT, TOP_CENTER, TOP_RIGHT, CENTER_LEFT, CENTER, CENTER_RIGHT, BOTTOM_LEFT, BOTTOM_CENTER, BOTTOM_RIGHT, CUSTOM.
hudOffsetX 0 Inward pixel offset from the anchor. Absolute screen X if hudAnchor=CUSTOM.
hudOffsetY 6 Inward pixel offset from the anchor. Absolute screen Y if hudAnchor=CUSTOM.
hudScale 1.0 Range 0.5–3.0.
hudShowTimers true Show tag/cooldown countdown text below the icon.

Upgrading

  • Mode is preserved. Whatever mode (Combat or Peace) each player was last in carries through on first 1.2.0 login.
  • In-flight tags and cooldowns reset on first 1.2.0 login. The 1.1.x persistent timers were wall-clock milliseconds with no clock-source guarantee; converting them to the new game-tick clock isn't safe, so they're dropped. New tags/cooldowns from 1.2.0 onward use a monotonic, restart-safe game-tick deadline.
  • The showHud server config key is gone. HUD visibility is purely client-side now via hudEnabled in combattoggle-client.toml. Existing operator configs keep the orphan key on first read; Forge logs an unused-key warning. Harmless.
  • Network protocol is "1" for the 1.2.x line. 1.1.x modded clients cannot connect to a 1.2.0 server (handshake rejects); they need to upgrade. Vanilla clients are unaffected.

Compatibility

  • Minecraft: 1.19.2
  • Forge: 43.3.0 or newer
  • Java: 17
  • Dependencies: None. No Kotlin, no Architectury, no Cloth Config.
  • Other PvP mods: Should coexist with anti-cheat, region protection, and KubeJS. useScoreboardTeams=false resolves conflicts with mods that own the scoreboard.
  • Multiplayer-only? No. Single-player works; the toggle still applies to damage interactions with hostile iron golems, etc. (as you'd expect for "PvP enabled/disabled").

Known Limits

  • Mixin-based damage sources may bypass enforcement. Combat Toggle cancels at both LivingAttackEvent (primary) and LivingHurtEvent (fallback). A handful of mods route damage through custom mixins that bypass both — those will hit Peace targets. Open an issue with a reproduction if you hit this.
  • No dimension allowlist. PvP rules apply uniformly across every dimension.
  • /combat and /peace are common literal namespaces. If another mod or plugin registers the same root, behavior depends on registration order — fall back to /ct and /combattoggle <subcmd>.
  • No public mod-integration API today. Player state lives on an internal Forge capability; a future minor can expose it without re-architecting.
  • Vanilla clients have no HUD or keybind. Use /ct. By design.

License & Source

Pull requests and bug reports welcome. Please include server logs, your combattoggle-server.toml, and a minimal repro when filing an issue.

Ченджлог

1.2.1Релиз1.19.2 · 15 мая 2026 г.

1.2.1

Bug Fixes

  • PvP enforcement now cancels at LivingAttackEvent instead of LivingHurtEvent. Cancelling at the old event happened after knockback, hurt sound, and PLAYER_HURT_ENTITY had already fired — letting attackers chain free knockback shots on Peace targets. The new path stops the entire hurt pipeline before any side effects. LivingHurtEvent remains as a defence-in-depth fallback that also clears residual knockback velocity. (REVIEW 1.1, 3.4)
  • Indirect-damage PvP loopholes closed. Attacker resolution now walks the full ownership chain: direct hits, projectiles, primed TNT (PrimedTnt.getOwner()), and any OwnableEntity (vanilla tamables and modded summons). Wolves, snow golems, and ignited TNT are now correctly gated by Combat Toggle. (REVIEW 1.2, 1.10)
  • Server-side timers migrated from wall-clock to game ticks. combat_tag_until_ms, last_toggle_ms, and last_pvp_ms became *_tick fields driven by level.getGameTime() — monotonic, persistent across server restarts, and immune to NTP steps or admin clock changes. The HUD packet still carries millisecond remainders for client-side display. (REVIEW 1.3)
  • Admin /set <player> peace now respects forceCombatWhileTagged. Previously the command silently flipped a tagged player to Peace even though the next PvP hit would rubber-band them back. Now refuses with a clear error unless bypass=true is supplied. (REVIEW 1.4)
  • /combattoggle status no longer reports a misleading cooldown for Peace-mode players. Asks the cooldown question in the direction of the player's next possible toggle, not always TO_PEACE. (REVIEW 1.5)
  • Client state is reset on disconnect / reconnect. ClientCombatState zeros itself on ClientPlayerNetworkEvent.LoggingIn and LoggingOut so the HUD never carries stale tag/cooldown state across servers. (REVIEW 1.6)
  • /combattoggle tag <player> 0 rejected at the parser. Brigadier now uses integer(1, 3600); the no-args form surfaces a clearer error (tag_disabled_in_config) when the server has tagging disabled. (REVIEW 1.7)
  • Per-call NBT allocations eliminated for player state. CombatToggleData is now a Forge capability attached to every Player entity. CombatToggleData.get(player) returns the same in-memory instance for the lifetime of that player — no per-call allocation, no read-modify-save footgun, no async-mutation race. save() is preserved as a no-op for source compatibility. (REVIEW 1.8, 1.13, 5.1)
  • cooldownAppliesToPeaceOnly boolean replaced with cooldownScope enum (PEACE_ONLY / COMBAT_ONLY / BOTH / NONE). The boolean conflated two semantics and made the symmetric/none cases inexpressible. Boolean is dropped — operators on upgrade get the PEACE_ONLY default. (REVIEW 1.9, with related rename of isCooldownActive(boolean)isCooldownActiveForDirection(ToggleDirection), REVIEW 3.8.)
  • /combattoggle reload renamed to /combattoggle resync. The actual operation is a team refresh + HUD resync — Forge auto-reloads the config on file save independently. The old /reload literal is preserved as an alias. (REVIEW 1.11)
  • Mode argument is now tab-completable. /combattoggle set <player> <combat|peace> [bypass] uses literal subcommands; typos no longer reach the executor. (REVIEW 1.12)

New Features

  • /combattoggle help — permission-filtered usage listing for every subcommand. (REVIEW 3.9)
  • Damage-type denylist — new blockedDamageTypes config (list of damage-type resource locations) lets admins forbid specific PvP vectors (e.g. minecraft:magic, minecraft:trident) without disabling PvP entirely. (REVIEW 3.1)
  • Public Forge event surface for mod integrations — three new events under com.runecraft.combattoggle.api.events:
    • CombatToggleStateChangeEvent (cancellable, fires before persist; lets safe-zone mods refuse a toggle)
    • CombatTagAppliedEvent (fires when a tag is set or extended)
    • CombatTagExpiredEvent (fires when a tag elapses, including on next-login if it elapsed offline)
    • CombatLoggedOutEvent (fires on disconnect with remaining tag ticks; canonical combat-log signal for penalty mods) (REVIEW 3.2, 3.12, 3.13)
  • Configurable HUD position. New client-config keys hudAnchor (LEFT/CENTER/RIGHT), hudYOffset (pixels), and showHudTimers (boolean) so HUD-customisation users can move or hide the indicator. (REVIEW 3.5)
  • Tag-expired chat ping fires after offline-elapsed tags too. A new tag_expiry_notified persisted flag ensures the notification is sent exactly once whether the tag expired live or while the player was offline. (REVIEW 3.11)
  • Force-combat flips notify the target. When forceCombatWhileTagged flips a Peace player to Combat (PvP hit or login while tagged), the player gets a chat hint explaining why and how long the tag has left. (REVIEW 3.10)

Performance

  • Tick handler stops allocating once per tagged player per tick. Tag deadlines are cached in a Map<UUID, Long> alongside the tracker — the per-tick walk is one iterator + one > comparison, no NBT reads, no CombatToggleData allocations. (REVIEW 2.1)
  • HUD timer strings cached per-second. Replaces the per-frame string concat + font.width measurement with a one-render-per-second rebuild. (REVIEW 2.2)
  • Sync packet shrunk from 17 bytes to ~5. Two longs replaced with two varint seconds (HUD only displays mm:ss). Packet protocol bumps to "3" — clients on 1.1.x get a clean handshake reject. (REVIEW 2.3, 4.12)
  • isPvpAllowed centralised in CombatToggleData. Single source of truth used by both the API and enforcement; eliminates duplicated requireBoth config reads. (REVIEW 2.4, 2.5)
  • Team-name pair cached at config load/reload instead of resolved on every login/respawn/toggle/admin-set. (REVIEW 2.6)
  • Hot-path PvP debug logging guarded by LOGGER.isDebugEnabled() to avoid eager arg evaluation on the per-hit path. (REVIEW 2.7)

Internals

  • New data/CombatToggleCapability.java registers the capability and attaches a provider to every Player. The clone hook replaces the old PlayerEvent.Clone-based persistent-data copy.
  • New data/ToggleDirection.java and data/CooldownScope.java enums replace boolean direction flags.
  • One-shot legacy NBT migration (PlayerLifecycleEvents.migrateLegacyData) ports enabled from any pre-1.2.0 combat_toggle persistent-data compound and discards the wall-clock timestamps (which would compare as in-the-future against the new game-tick clock). The legacy compound is removed after migration.
  • API method CombatToggleAPI.isCooldownActive(ServerPlayer, boolean) deprecated for removal in favour of the ToggleDirection overload.
  • The lang file gained 12 new keys (help_*, set_blocked_tagged, tag_disabled_in_config, admin_force_flipped, pvp_blocked_damage_type, state_change_cancelled, resync) and the reload key was renamed.

Skipped / Out-of-Scope

  • JEI/EMI integration page (REVIEW 3.14) — would require adding compileOnly external Maven deps and a custom anchor item to host the info page. The /combattoggle help command added in this release covers the same discoverability gap with no extra dependencies.

Комментарии

Загружаем…