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.


