
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
/ctto flip modes with no client install. Modded clients additionally get a rebindableVkeybind and an on-screen HUD indicator.
Features
Core
- Universal commands.
/ct,/combat,/peace, and/combattoggle statuswork 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 thePLAYER_HURT_ENTITYadvancement criterion never fire on Peace targets.LivingHurtEventruns 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
- Install Minecraft Forge 43.3.0+ for Minecraft 1.19.2.
- Drop
combattoggle-1.2.0.jarinto your server's (or client's)mods/folder. - Launch. On first run,
combattoggle-server.tomlis generated in your world'sserverconfig/directory andcombattoggle-client.tomlin 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
showHudserver config key is gone. HUD visibility is purely client-side now viahudEnabledincombattoggle-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=falseresolves 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) andLivingHurtEvent(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.
/combatand/peaceare common literal namespaces. If another mod or plugin registers the same root, behavior depends on registration order — fall back to/ctand/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
- License: GPL-3.0
- Source & issues: https://github.com/otectus/combat-toggle
- Modpack home: Built for the Runecraft 1.19.2 Forge modpack. Drop it into anything else — it's standalone.
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
LivingAttackEventinstead ofLivingHurtEvent. Cancelling at the old event happened after knockback, hurt sound, andPLAYER_HURT_ENTITYhad already fired — letting attackers chain free knockback shots on Peace targets. The new path stops the entire hurt pipeline before any side effects.LivingHurtEventremains 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 anyOwnableEntity(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, andlast_pvp_msbecame*_tickfields driven bylevel.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> peacenow respectsforceCombatWhileTagged. 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 unlessbypass=trueis supplied. (REVIEW 1.4) /combattoggle statusno 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.
ClientCombatStatezeros itself onClientPlayerNetworkEvent.LoggingInandLoggingOutso the HUD never carries stale tag/cooldown state across servers. (REVIEW 1.6) /combattoggle tag <player> 0rejected at the parser. Brigadier now usesinteger(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.
CombatToggleDatais now a Forge capability attached to everyPlayerentity.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) cooldownAppliesToPeaceOnlyboolean replaced withcooldownScopeenum (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 thePEACE_ONLYdefault. (REVIEW 1.9, with related rename ofisCooldownActive(boolean)→isCooldownActiveForDirection(ToggleDirection), REVIEW 3.8.)/combattoggle reloadrenamed to/combattoggle resync. The actual operation is a team refresh + HUD resync — Forge auto-reloads the config on file save independently. The old/reloadliteral 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
blockedDamageTypesconfig (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), andshowHudTimers(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_notifiedpersisted 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
forceCombatWhileTaggedflips 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, noCombatToggleDataallocations. (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)
isPvpAllowedcentralised inCombatToggleData. Single source of truth used by both the API and enforcement; eliminates duplicatedrequireBothconfig 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.javaregisters the capability and attaches a provider to everyPlayer. The clone hook replaces the oldPlayerEvent.Clone-based persistent-data copy. - New
data/ToggleDirection.javaanddata/CooldownScope.javaenums replace boolean direction flags. - One-shot legacy NBT migration (
PlayerLifecycleEvents.migrateLegacyData) portsenabledfrom any pre-1.2.0combat_togglepersistent-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 theToggleDirectionoverload. - 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
reloadkey 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 helpcommand added in this release covers the same discoverability gap with no extra dependencies.
Комментарии
Загружаем…