Skip to content

[Perf] Consolidate scheduled tasks into single global tick#317

Closed
Mere-Solace wants to merge 73 commits into
mainfrom
perf/consolidate-task-scheduling-299
Closed

[Perf] Consolidate scheduled tasks into single global tick#317
Mere-Solace wants to merge 73 commits into
mainfrom
perf/consolidate-task-scheduling-299

Conversation

@Mere-Solace

Copy link
Copy Markdown
Owner

Summary

  • Replaced per-task ScheduledExecutorService futures in TimeArbiter with a single global BukkitRunnable tick loop (runTaskTimer 0, 1)
  • All TimeArbiter registrations now add a TaskHandle to a List; each tick fires tasks whose nextFireTimeMs has elapsed and removes cancelled ones via removeIf
  • Eliminates ~210-280 concurrent ScheduledFutures at runtime
  • Time-bound tasks apply GLOBAL_TIME_SCALE dynamically on each fire (no rescheduling needed on scale change)
  • Public API unchanged (runTimeBoundBukkitTaskOnTimer, runTimeIndependentBukkitTaskOnTimer, runFixedIterationTaskTimer, TaskHandle.cancel/pause/resume)
  • TimeArbiter.startGlobalTick() added; called from Sword.onEnable()
  • SwordScheduler's one-shot delays still use the ScheduledExecutorService

Related Issues

Closes #299
Relates to #298

Mere-Solace and others added 30 commits March 3, 2026 23:38
* Implemented ActionContextPair, which allows multiple actions under different contexts for the same input path

* Hide successor nodes for shared prefixes when terminals have restrictive hidden predicates

* Fixed broken input activations and other glitches (+ fixed umbral blade lunge glitched movement)

* spotlessApply
* Add: NonMovable item class hierarchy (SpecialItem / NonMovableItem / SlotAnchoredItem)

Closes #199

- Add `SpecialItem`, `NonMovableItem`, `SlotAnchoredItem`, `SoulLinkItem` in `btm.sword.system.item.special`
- Add `SPECIAL_ITEM_KEY`, `NON_MOVABLE_KEY`, `SHIELD_KEY`, `CHESTPLATE_KEY` to `KeyRegistry`
- Convert Soul Link (`SoulLinkItem`), menu button, shield, and netherite chestplate to `SlotAnchoredItem`
- Replace ad-hoc `inventoryUpkeep()` slot checks with `isSatisfied()` / `restore()` calls
- Replace hardcoded slot and key guards in `handleInventoryInput()` and `onPlayerDropEvent()` with `NonMovableItem.isNonMovable()`

* Docs: Update UmbralBlade LodgedState documentation for interactable registration
* Finishes #147. Fixed a few extra umbral state transition issues + umbral blade impalement logic.

* Chore: Apply spotless formatting
…ting

- ChatInputCapture: intercepts one async chat message per player, dispatches callback on main thread
- DevMenu: op-only debug flag toggles (verbose, combat/movement/config logging, special item checks)
- ConfigMenu: section browser grouping all Config.ENTRIES by top-level YAML key
- ConfigSectionMenu: paged editor for entries within a single section
- ConfigEntryItem: in-place editing for Boolean/Integer/Long/Double/Float; chat-prompt for numerics (Shift+L); EnumSelectionMenu for all enum types; typed r,g,b for Color/TextColor
- EnumSelectionMenu: paged enum browser; SoundType supports right-click preview; Material uses itself as display item
- Config.forceInitializeAll(): reflection-based eager initialization of all nested Config classes at startup so all sections appear in the menu immediately
- ConfigManager: setValue handles Float→double, Color/TextColor→hex string, Enum→name() for correct YAML round-trip
…materials

- saveToProject: use getAbsoluteFile() before walking up to build.gradle so relative data folder paths resolve correctly on Windows
- EnumSelectionMenu: SoundType shows prefix category browser first (AMBIENT/BLOCK/ENTITY/etc with themed materials), then filtered value list per prefix; back button returns to prefix page
- Config.Combat: 20 new ConfigEntry fields for defaultMobHit, grabHit, umbralItemDisplayAttack, punch hit packets; fix duplicate path bug on ATTACK_CLASS_HIT_SOULFIRE
- Prefab.Attacks: all four hardcoded HitValuePackets now read from Config.Combat
- ConfigMenu: replace rotating SECTION_MATERIALS array with explicit Map<String,Material> keyed by section name
- CombatProfile.reloadFromConfig(): re-reads Config.Entity fields at runtime
- EntityAspects.reloadFromProfile(): applies updated profile to live Resources/Aspects, restarts regen tasks
- DevMenu: Reload Combat Profile button (RECOVERY_COMPASS) applies both instantly
- DevStatEditorMenu: op-only menu to edit all 12 AspectType base values / regen params live via chat input; opened from CharacterMenu dev button
- EnumSelectionMenu: ITEM_ sounds now match item materials; BLOCK_ sounds require isItem() to avoid non-obtainable materials (fixes BAMBOO_SAPLING crash); ITEM_ and all other prefixes get smart material resolution; window titles now prepend entry.path so you always know what you're editing
#196, #197, #202) (#217)

* Add: Mob ability system with mobile PreAttack wind-up (#196, #197, #202)

- MobAbility interface + AbilityCategory enum (MELEE/RANGED) for typed
  ability dispatch; cooldown map tracked per-ability on Hostile
- MobSlashAbility: picks SLASH1/2/3 randomly, runs MobSweepAttack with
  defaultMobHit (not basicAttack)
- MobThrowAbility: launches off-hand item as DroppedItem with parabolic arc
- MobSweepAttack: extends SweepAttack, overrides hit() to use defaultMobHit
- PreAttackRetreatGoal: RANGED wind-up movement, recomputes backoff each tick
- PreAttackState: selects ability on enter, registers ApproachGoal (MELEE)
  or PreAttackRetreatGoal (RANGED) so mob moves during the telegraph instead
  of stopping; removes all MOVE+LOOK goals on exit
- AttackState: fires pendingAbility immediately on entry — no charge, no
  distance gate; incapacitation guard prevents execution while grabbed
- HostileStateMachine: transition #8 adds !isIncapacitated() guard;
  transition #13 (target escape mid-charge) removed as obsolete
- SwordEntity: onGrabbed()/onReleased() lifecycle hooks (public)
- Combatant: wires onGrabbed/onReleased in onGrab, onGrabLetGo, onGrabThrow
- Hostile: overrides onGrabbed (setAware false, remove MOVE goals) and
  onReleased (setAware true) for grab incapacitation
- Config: MOB_SLASH_COOLDOWN_TICKS (20), MOB_THROW_COOLDOWN_TICKS (60),
  MOB_THROW_ARC_HEIGHT (0.4) + matching config.yaml entries
- README: updated PreAttack/Attack sections, removed #13, added MobAbility
  system docs, grab incapacitation section, updated goal table

* Add: Mob ability system with VisualProjectile refactor and throw fixes (#196, #197, #202)

- Extract VisualProjectile from ThrownItem: pure physics + display base class with event hooks
- Add Projectile (headless standalone physics) and ProjectileManager (single tick loop)
- ThrownItem now extends VisualProjectile; hook methods resolve origin/basis/dir from thrower
- ThrowAction: add throwReady(Combatant, ItemStack) overload for mob AI item injection
- MobThrowAbility: fix Bug 1 (wrong hand), Bug 2 (arc direction ignored), guard double-execute
- PreAttackState: retreat for ranged abilities, approach for melee; play pre-attack sound
- Add WorldListener to cancel block placement during testing
- Add PRE_ATTACK sound prefab and config entries
…gle, search, menu click-open

- CreativeInventoryMenu: paged browser of all modern materials with chat-based search filter
- DevMenu: wooden axe + wither skeleton egg give buttons, block placing toggle, creative inv button
- Config.World.BLOCK_INTERACTION_ALLOW_BLOCK_PLACING: block placing and item consume no longer deplete items
- Clicking the main menu button inside the inventory now opens the menu
- Shift-right drop in inventory now guards against non-movable/empty items
…ers README

- Add ProtocolLib 5.3.0 as compileOnly dep and hard server dependency in paper-plugin.yml (reserved for future packet-level work; see listeners/README.md and issue #219)
- Add Config.Movement.DASH_DOWNWARD_FLAT_CHECK_MULTIPLIER (default 3.0) for tuning the downward flat-check threshold in Dash
- Fix: opening any menu via PlayerMenuManager now clears the cursor item first, preventing the menu button from getting stuck on the player's cursor
- Fix: clicking the main menu button while inside an inventory now opens the menu correctly (check reordered before null-guard in SwordPlayer)
- Add listeners/README.md documenting the ProtocolLib block-place attempt and implementation notes for revisiting
* Add: Umbral Blade expansion — bugs #205 #179, features #181 #174 #176

Stage 1 (#205): InactiveState hides display (viewRange=0) on enter, restores
on exit, fixing spectator-mode head glitch.

Stage 2 (#179): RecallingState maxIterations reduced 500→80 (~8s max recall);
added onTick fallback STANDBY request for early task cancellation; added
LungingState→RecallingState on STANDBY for mid-lunge dash-grab.

Stage 3 (#181): WaitingState rewritten with entry-time tracking and
distance/timeout auto-recall; add WAITING to BladeRequest; new
StandbyState→WaitingState transition; full WaitingState transition set
(RECALL, STANDBY, LUNGE, ATTACK_QUICK, ATTACK_HEAVY); hoverBlade() action
bound to DROP+SWAP (soulLinkState); config: WAITING_TIMEOUT_MS=8000,
WAITING_MAX_DISTANCE=20.

Stage 4 (#174): ReclaimType enum (NONE/OVERHEAD_SLAM/AERIAL_SPIKE) on
UmbralBlade; onGrab branches on isDashing()+isOnGround() to set reclaimType
and request appropriate attack; AttackingQuickState dispatches OVERHEAD_SLAM
with SF-gain-on-hit callback; AttackingHeavyState dispatches AERIAL_SPIKE
similarly; both reset reclaimType on exit; OVERHEAD_SLAM/AERIAL_SPIKE added
to AttackType enum. NOTE: spinning-slash SWAP+SWAP+LEFT variant pending user
decision (see plan tradeoff note).

Stage 5 (#176): CHANNELING added to ActivationContext; channelInterrupted flag
on SwordPlayer; SwordEntity.hit() sets channelInterrupted when target is
channeling; channel() in UmbralBladeAction gates on WieldState+SF≥50, locks
velocity for 2s, heals on completion or cancels on damage; RIGHT+RIGHT_HOLD
(umbralState) binds channel; config: CHANNEL_SOULFIRE_COST=50,
CHANNEL_HEAL_AMOUNT=1, CHANNEL_DURATION_MS=2000.

Stage 6 (#143) deferred — requires stages 1-3 tested and stable first.

* Fix: Fully functional healing system

* Add: Per-state blade glow color #229

Glow color is now set centrally in UmbralStateMachine.setState() via a
state class -> Config.SwordColor lookup, replacing scattered setGlowing()
calls in each state's onEnter/onExit. Six new SwordColor config entries
added (standby, attack_quick, lunge, lodged, grab_impale, recall).

* Fix: Umbral blade systems

* Refactor: Dash logic cleanup + game feel improvements

* Fix: Umbral blade & dashing logic fixes

* Add: Reclaim attack system — circular slash, DrawUtil.circle(), wield-state input routing

- Add ReclaimType.CIRCULAR_SLASH / FORWARD_RUSH enum variants replacing OVERHEAD_SLAM / AERIAL_SPIKE
- Add setReclaimType(type, durationMs) with auto-reset timer on UmbralBlade
- Trigger CIRCULAR_SLASH reclaim when grabbing blade from WaitingState
- Add wieldedUmbralBladeAttack() dispatching by reclaim type, wired into combo input tree
- Add DrawUtil.circle() for oriented ring particle patterns using Basis
- Add ParticleWrapper.displayAll() batch helper
- Add SwordEntity.getCurrentEyeDirectionBasis() / getCurrentBodyDirectionBasis() convenience accessors

* Fix: gradle build fixes for GitHUb CI/CD tasks
* Refactor: Generalize AttackProfile — decouple sweep path from Bézier via AttackShape #237

- Add AttackShape interface: resolve(Basis, double) → Function<Double, Vector>
- Add BezierShape: local-space and world-space factory methods, moves adjustToBasis logic out of Attack
- AttackProfile.controlVectors() replaced by shape()
- AttackType: stores AttackShape, accepts any shape impl via new constructors, keeps controlVectors() helper for lunge states
- GeneratedAttackProfile: wraps ControlVectors in BezierShape.worldSpace(), removes instanceof coupling in Attack
- Attack: drops controlVectors field, generateBezierFunction() → generatePathFunction() delegates to shape
- README updated to reflect new extension model

* Add: ArcShape and ConeShape AttackShape implementations #237

- ArcShape: sweeps a circular arc in the attacker's horizontal plane.
  Parameters: radius, startAngle, endAngle (radians, 0=forward), height.
  Full 360 circle when endAngle - startAngle = 2pi. Matches DrawUtil.circle convention.
- ConeShape: sweeps along the rim of a cone aligned to the attacker's forward axis.
  Parameters: halfAngle (polar offset), range, startSweepAngle, endSweepAngle.
  ConeShape.symmetric() factory for left/right symmetric fan attacks.
- README: document ArcShape, ConeShape, and AOE/sphere extension pattern

* Refactor: Move shape classes to style/shape package, wire ArcShape into AttackType #237

* Add: Config entries for hardcoded UmbralBlade/combat values; relabel TODOs to #240/#241 #237

* Refactor: Wire hardcoded values to Config entries in UmbralBlade, AttackType, UmbralBladeAction #237

* Add: Config entries for Dash, Attack, InputRegistrar hardcoded values; wire all to Config #237

* Add: Track AttackType control vector config work as #242 #237
Introduces ImpactType enum (IMPALE/KNOCKBACK) stored as a PDC key on
ItemStacks. ThrownItem.onHit() now reads the tag instead of checking
material name suffixes. The prefab sword is tagged with IMPALE.
Aligns static final HitValuePacket field names with Java constants
convention. Updates all call sites across attack, mob, and listener classes.
Adds three anchored storage buttons (Currency/Material/Quest) in the
column above the main menu hotbar slot. Each button has a config-backed
material supplier, TEXT_COOL display name, and a per-player lore supplier
showing live stats (Steel Credits balance, material slots filled/total).
Adds Config.Materials section, loadMaterial helper, STORAGE_BUTTON_KEY,
steelCredits/materialSlotsUsed fields on SwordPlayer, and a dev menu
button to hot-reload all anchored inventory buttons.
* Fix: Config-drive AttackingHeavyState TODOs and extend CreativeInventoryMenu row

* Feat: Make AttackType control vectors config-driven via Supplier<Vector> (#242)

- ControlVectors: converted from record to class; stores Supplier<Vector>
  for each of the four control points. Two constructors accept plain
  Vectors (eagerly captured) or Suppliers (resolved lazily at attack time).
- Config: added loadVector() utility (reads {x,y,z} sub-section) and new
  Config.AttackCurves class with 76 Vector entries for all 19 AttackType
  curves, all loaded from the new attack_curves YAML section.
- AttackType: replaced every hardcoded Vector literal with a supplier
  referencing Config.AttackCurves, so /sword reload propagates curve
  changes without a restart. DEFAULT uses Config.Direction method refs.
- config.yaml: added attack_curves section with all 76 default vectors.

* Feat: Add Vector support to ConfigEntryItem and ConfigManager (#242)

- ConfigEntryItem: Vector entries now display x/y/z in the config menu
  and accept chat input in "x y z" format via ChatInputCapture.
- ConfigManager.setValue: stores Vector as an {x,y,z} YAML sub-section
  matching the format expected by Config.loadVector().

* Update: Tweak Vector item icon and note ConfigMenu section materials
- ConfigMenu: global search via ChatInputCapture opens filtered ConfigSectionMenu
- ConfigSectionMenu: per-section filter with active state indicator and shift-click to clear
- EnumSelectionMenu: filter support on both prefix list and value list; use Registry.MATERIAL to exclude legacy/non-item Materials
- Menu: add WALL separator item (light blue pane)
- DevMenu: replace '#' column separator with WALL item
- PlayerListener: guard drop event to only fire when default inventory is open
- config.yaml: update a few default values
…, trash clear (#254)

* Add: Main menu scene and camera animation pipeline (#233)

Introduces the btm.sword.system.scene package with CameraController
abstract base (single-owner enforcement via SwordPlayer field),
GentleDriftCameraController for the main menu scene (sinusoidal bob +
yaw drift, HudDisplayGroup displays), BezierCameraController for
cutscenes (follows CameraPath over configurable tick duration),
SceneManager (music loop + scene-viewer tracking), and supporting
utilities. Hooks into SwordPlayer.onSpawn, InputListener Shift gate,
and PlayerListener forced-spectator-exit. Config.Scene added with all
defaults. AvatarDisplay stub included.

* Add: Packet-driven camera system (ClientboundSetCameraPacket)

Introduces PacketAdapter (NMS reflection layer), CameraSession (active
attachment state), and CameraService (static attach/detach API).
Refactors BezierCameraController and GentleDriftCameraController to use
CameraService.attach() instead of spectator-mode targeting — player
stays in their original game mode. Both controllers retain a spectator
fallback if PacketAdapter reflection fails at runtime.

* Fix: Switch PacketAdapter to ProtocolLib; pin Gradle daemon to Java 21

Replaces NMS reflection in PacketAdapter with ProtocolLib's
PacketContainer API (PacketType.Play.Server.CAMERA, getIntegers().write).
Swaps compileOnly fileTree for net.dmulloy2:ProtocolLib:5.4.0 from
Maven Central. Pins org.gradle.java.home to JDK 21 in gradle.properties
so Gradle 8.8 runs under Java 21 regardless of the system JAVA_HOME.

* Refactor: Make ParticleWrapper fields supplier-based for runtime configurability (#234)

Replace fixed-value fields with Supplier<T> equivalents so values are resolved
at display time, enabling config hot-reload and runtime-driven particle behavior.
Introduces fluent with*() methods (withSpeed, withOptions, withTransition,
withBlockData) to avoid Java type-erasure clashes with generic overloads.
All existing call sites updated mechanically — no behavior change.

* Add: Config.Particles section and particles menu for hot-reloadable particle tuning

Adds Config.Particles with type (Particle enum), count, and speed entries for
all 18 gameplay Prefab.Particles constants. Wires Prefab suppliers to config
fields so values resolve at runtime and respond to /sword reload.

Adds EnumSelectionMenu support for Particle enum with keyword-based icon
resolution (FLAME → flint_and_steel, CRIT → diamond_sword, etc.).

Adds Config.Menu.PARTICLES_ICON and registers the "particles" section in
ConfigMenu so it appears as a browsable section in the config GUI.

* Refactor: Extract debug toggles from DevMenu into TogglesMenu submenu

Move all 10 boolean toggle items out of DevMenu into a dedicated TogglesMenu,
and lift the shared toggle() helper up to the Menu base class.

* Add: DEU/BDE animation system and scene refactor

Introduces DEUAnimationController, StaticSceneController, and a full animation registry backed by animations.yml. Adds AnimationBrowserMenu, DEUBDEMenu, and DeuGroupBrowserMenu for in-game animation browsing. Removes legacy SceneManager, AvatarDisplay, HudDisplayGroup, and GentleDriftCameraController in favour of the new controller model. Renames SoundType → SwordSoundType to avoid collision with the Bukkit API. Adds packet-level MovementListener for cutscene input handling.

* Add: Scene overlay system, animation config picker, and ChatInputCapture fix

- Add enterSceneOverlay/exitSceneOverlay to SwordPlayer: clears inventory, fills
  with non-movable glass panes, keeps menu button at slot 22, clears hotbar and
  offhand, sets player invisible, deactivates umbral blade
- DEUAnimationController and StaticSceneController call enter/exitSceneOverlay
- DevMenu static scene button now uses Config'd DEUAnimationController instead of
  StaticSceneController; animation key resolved from AnimationRegistry by anim key
- Add Config.Animation.STATIC_MENU_ANIMATION_KEY (default: gentle_v2_default)
- ConfigMenu animation section now opens ConfigSectionMenu (not AnimationBrowserMenu)
- Add AnimationPickerMenu: paged GUI for selecting animations from AnimationRegistry;
  used when clicking a String-typed config entry in ConfigEntryItem
- Fix ChatInputCapture NoClassDefFoundError: replace PlainTextComponentSerializer
  with TextComponent instance-check extraction to avoid runtime class resolution failure

* Add: Animation stay-fix, blade deactivation API, recovery conditions, trash clear

* Fix: NPE guard on gameMode display removal, double .get() call, redundant slot0 branch

* Fix: Remove local Windows JDK path from gradle.properties

* Fix: Faulty Import

* Fix: SpotlessApply
Mere-Solace and others added 29 commits March 28, 2026 00:55
- Add class-level and method Javadoc to ~34 files across listeners, action classes, skill hierarchy, attack shapes, AI states, and utilities
- Fix all actionable Javadoc warnings: missing @param/@return, empty comments, no-main-description, and undocumented default constructors
- Add private BezierUtil() constructor (also suppresses FinalClass Checkstyle hint)
- Javadoc warnings reduced from ~890 to 100 (remaining are undocumented fields in Config/Prefab — out of scope)
* Add: Roguelike wave-run prototype (#268)

- RoguelikeRun gamemode: 3-wave PvE (pillagers → wither skeletons → mixed),
  configurable spawn center and radius, TextDisplay reward chest on completion
- Config.Roguelike section: spawn world/X/Y/Z, radius, per-wave enemy counts
- QueueManager: register roguelike queue, start solo run immediately on enqueue
- MainMenu: Wither Skeleton Skull queue button paired with CTF button
- TODO #285: distribute MiscItems once consumable item system is built

* Chore: move comms dir from .claude/comms to comms/ at project root

* Feat: 5-minute roguelike timeout and per-mob damage log

- Reduce RoguelikeRun safety cap from 15 min to 5 min (300 s)
- Add DamageEntry record to Hostile tracking attacker UUID, name,
  weapon (SwordItemType), shard damage, and toughness damage
- Override hit() in Hostile to append entries only when the hit
  actually lands (guarded by invulnerability-window check)

* Feat: team-based friendly fire suppression via scoreboard tags

Add SwordTeam enum (RED/BLUE/GREEN/YELLOW) backed by Bukkit scoreboard
tags. SwordEntity.hit() skips damage when attacker and defender share
a team tag. Assignments: Hostile → RED, SwordPlayer → BLUE,
Passive → GREEN. joinTeam() / getTeam() exposed on SwordEntity.

* Fix: Gamemode timer period 20ms → 1000ms (decrement once per real second)

* Fix: restore umbralBladeState and soulLinkState predicates in combo registration

* Feat: ErrorListener with safeRemove + try-catch around display spawning/removal

* Feat: CTF full infrastructure — flag entity, team management, respawn, scoring, banner-on-head (#280)

- Config.Ctf: spawn coords, flag return timer, respawn delay, capture threshold, carrier slow config
- CtfTeam enum: RED/BLUE with banner factory (patterned banners), spawn locations from config
- FlagEntity: IDLE/CARRIED/DROPPED state machine, ArmorStand visual at base, banner-on-head carrier effect, auto-return timer
- CaptureTheFlag1v1 rewrite: proper lifecycle, 5s respawn with team-spawn return, first-to-N or timer win, score messaging
- QueueManager: CTF 2-player queue wired, activeMatches map for death routing, startCtfDebug() bypass
- PlayerListener: routes death events to active CTF match for flag drop + respawn scheduling
- CommonConstraints: NOT_CARRYING_FLAG stub (pending global context constraint refactor)
- Prefab.PotionEffects: FLAG_CARRIER_SLOW (config-backed Slowness II applied to flag carriers)
- DevMenu: "Start CTF (Solo Debug)" button — bypasses queue, instant launch for one player

* Chore: wire issue #297 reference into NOT_CARRYING_FLAG stub

* Fix: null scores in getTitle() called before subclass field init

* Fix: defer display group unregister 1 tick to avoid Paper chunk-lock warning

* Feat: dev button to stop active roguelike run from main menu

* Fix: NaN boss bar progress — override getMaxDuration() with constant in CTF

* Chore: move menu items for menu refactor into dev menus folder
- Checkstyle config: allow SCREAMING_SNAKE_CASE for static fields, permit
  empty catch blocks named 'ignored', allow empty {} blocks by excluding
  LCURLY/RCURLY from WhitespaceAround tokens, processJavadoc for UnusedImports,
  allowLineBreaks for NoWhitespaceBefore
- Utility classes: add final + private constructor to ~30 utility/static classes
- Static field renames: camelCase → SCREAMING_SNAKE_CASE across TimeArbiter,
  QueueManager, SwordEntityArbiter, SwordAction, ActionCaster, MovementListener,
  SkillRegistry, CharacterMenu, Misc, and others
- Method renames: Config.Direction UP/DOWN/etc → up/down/etc; WeaponAttackStyle
  fDash/bDash/rStrafe/lStrafe accessor names normalized
- Parameter renames: fade_in/fade_out → fadeIn/fadeOut in SwordPlayer; r_diff
  etc in InputActionExecutor
- Whitespace fixes: operator spacing (arithmetic, string concat, casts) across
  ~60 files; comma spacing in method calls throughout
- Empty block style: revert { } → {} for switch arrow cases and catch blocks
  now that checkstyle is configured to allow them
- Mark knife projectile as unbreakable so handleItemDamageAndCheckIfBroken
  does not destroy itemStack before the ImpactType check runs (root cause of
  the always-knockback bug: fresh items reported max damage due to
  hasDamageValue() returning false, triggering the break path on first hit)
- Switch ImpactType.fromItem check from display.getItemStack() to itemStack
  to avoid PDC loss through the ItemDisplay entity round-trip
- Add Prefab.Attacks.KNIFE_THROW: 1 shard, 15 toughness, 0 soulfire
- Add hitPacket / knockbackFunction overrides to ThrownItem so ability
  throws can supply custom hit resolution without subclassing
- Knife knockback: velocity * 0.2 added to the target's current velocity
- Ability projectiles (tagged ABILITY_ID_KEY) no longer catchable by
  thrower, not registered in InteractiveItemArbiter, and dispose silently
  on entity death instead of dropping a world item
ItemStack API:
- Replace new ItemStack(Material.X) with ItemStack.of() across the codebase
- Replace ItemMeta.displayName() / .displayName() with .customName()

Input system:
- Rename InputExecutionTree.hasChildren() -> isTerminal() (clearer semantics)
- Add getPlainTextInputSequence() debug helper to InputExecutionTree
- Reset input tree on charge-action release in InputRegistrar
- Reset input tree in MovementAction when player is holding an ability item

Ability slot / SwordPlayer:
- Add AbilitySlotManager.restoreSlot() and syncSlotItemFromState() so the
  ability slot can be restored after the gunpowder placeholder replaces it
- Fix endHoldingRight() to call restoreSlot() when releasing an ability slot,
  rather than blindly restoring the pre-hold item stack
- Fix onHoldRight() to null-check and clone hand snapshots
- Remove stale isAbilityItem() overloads from SwordPlayer (replaced by
  notHoldingAbilityItem() / AbilitySlotManager)
- Remove unused punchingWithLink, commandingWithLink, targetIndicatorTask fields

AbilitySlotItem:
- Merge EQUIPPED and EMPTY placeholder visual (both now show gray pane)
- Change DEPLETED placeholder material to GRAY_DYE

CtfTeam:
- Replace manual getDyeColor/getTextColor with Lombok @Getter
- Modernize world null-check and ItemStack construction

Misc:
- Add throwDirect(float) overload to ThrowAction delegating to Vector3f form
- Add non-uniform displayScaleVector support to VisualProjectile
Replaces the previous flat suppression approach with a 20s sequence
in SwordPlayer.testHudSequence(): wither hearts (5s), poisoned hearts
(5s), empty food bar via HUD override (5s), and a bubble animation
full-to-empty-to-full via TimeArbiter (5s). Removes the redundant
enableTimedTestOverride/disableTestOverride/TEST_OVERRIDE_KEY API from
HudOverrideManager.
Ability throws (ABILITY_ID_KEY tagged items) were skipping
InteractiveItemArbiter.put() after the 10c6f57 cleanup, making them
invisible to pickup and dash-to interactions. Remove the guard so they
are tracked like any other thrown item; the onGrab refund path and the
disposeWithNewInteractiveItem silent-dispose override continue to handle
ability-specific behavior correctly.

Also adds the custom smithing interaction system (CustomInteractionManager,
CustomInventoryInteraction, HUD override infrastructure) and the ROTATE
throw style used by smithing refits.
#303, #304, #305)

#305 MovementListener: guard Debug.listener() string concat behind
  Config.Debug.LOGGING_VERBOSE_LISTENER — eliminates 100+ String
  allocations/sec per player from a no-op debug path.

#304 HostileStateMachine: gate broadcastMessage() calls in
  onAnyTransition/afterAnyTransition behind LOGGING_VERBOSE_HOSTILE —
  eliminates O(n) getNearbyEntities world scan on every mob AI transition.

#303 Hostile/HostileStateMachine: add cachedDistSqToTarget field,
  refreshTargetDistanceCache() called once at tick() start; all 13
  transition conditions replaced with getCachedDistSqToTarget() —
  eliminates 3-5 Location allocations per mob per tick.

#301 SwordEntity: cache SwordTeam in cachedTeam field set by joinTeam();
  hit() now compares getCachedTeam() fields directly instead of calling
  SwordTeam.fromEntity() twice; joinTeam() drops the values() loop and
  removes only the previously held tag — eliminates 4 allocations per hit.
Add effectiveTransitionCache: Map<Class<?>, List<Transition>> to
StateMachine. tick() now calls effectiveTransitionsFor(currentState.getClass())
which lazily computes and caches the filtered subset of transitions whose
from-class is assignable from the current state class.

Result: O(k) per-tick evaluation where k = transitions for the current state,
instead of O(N) across all transitions with isAssignableFrom on every entry.
For HostileStateMachine (~20 transitions, 10 distinct from-classes): each tick
evaluates 3-7 conditions instead of 20. Cache is invalidated on addTransition()
for correctness.

Flyweight state instance caching skipped: several state classes (RecallingState,
GrabImpaleState, StandbyState, RecoverState) hold task handle fields that are
set in onEnter and cancelled in onExit — caching would require auditing every
onExit for full cleanup, adding fragility risk for marginal gain on a code path
that only runs on transitions.
* Fix ability-slot item swapping and refresh behavior (#292)

* Fix: Knife throw impact type, add knife hit packet, ability item cleanup

- Mark knife projectile as unbreakable so handleItemDamageAndCheckIfBroken
  does not destroy itemStack before the ImpactType check runs (root cause of
  the always-knockback bug: fresh items reported max damage due to
  hasDamageValue() returning false, triggering the break path on first hit)
- Switch ImpactType.fromItem check from display.getItemStack() to itemStack
  to avoid PDC loss through the ItemDisplay entity round-trip
- Add Prefab.Attacks.KNIFE_THROW: 1 shard, 15 toughness, 0 soulfire
- Add hitPacket / knockbackFunction overrides to ThrownItem so ability
  throws can supply custom hit resolution without subclassing
- Knife knockback: velocity * 0.2 added to the target's current velocity
- Ability projectiles (tagged ABILITY_ID_KEY) no longer catchable by
  thrower, not registered in InteractiveItemArbiter, and dispose silently
  on entity death instead of dropping a world item

* Update: ItemStack API modernization, input fixes, ability slot restore

ItemStack API:
- Replace new ItemStack(Material.X) with ItemStack.of() across the codebase
- Replace ItemMeta.displayName() / .displayName() with .customName()

Input system:
- Rename InputExecutionTree.hasChildren() -> isTerminal() (clearer semantics)
- Add getPlainTextInputSequence() debug helper to InputExecutionTree
- Reset input tree on charge-action release in InputRegistrar
- Reset input tree in MovementAction when player is holding an ability item

Ability slot / SwordPlayer:
- Add AbilitySlotManager.restoreSlot() and syncSlotItemFromState() so the
  ability slot can be restored after the gunpowder placeholder replaces it
- Fix endHoldingRight() to call restoreSlot() when releasing an ability slot,
  rather than blindly restoring the pre-hold item stack
- Fix onHoldRight() to null-check and clone hand snapshots
- Remove stale isAbilityItem() overloads from SwordPlayer (replaced by
  notHoldingAbilityItem() / AbilitySlotManager)
- Remove unused punchingWithLink, commandingWithLink, targetIndicatorTask fields

AbilitySlotItem:
- Merge EQUIPPED and EMPTY placeholder visual (both now show gray pane)
- Change DEPLETED placeholder material to GRAY_DYE

CtfTeam:
- Replace manual getDyeColor/getTextColor with Lombok @Getter
- Modernize world null-check and ItemStack construction

Misc:
- Add throwDirect(float) overload to ThrowAction delegating to Vector3f form
- Add non-uniform displayScaleVector support to VisualProjectile

* [Perf] Apply low-risk mob and state machine optimizations

* Fix: Redo HUD effect test to cycle wither/poison/hunger/bubbles

Replaces the previous flat suppression approach with a 20s sequence
in SwordPlayer.testHudSequence(): wither hearts (5s), poisoned hearts
(5s), empty food bar via HUD override (5s), and a bubble animation
full-to-empty-to-full via TimeArbiter (5s). Removes the redundant
enableTimedTestOverride/disableTestOverride/TEST_OVERRIDE_KEY API from
HudOverrideManager.

* Fix: Re-register ability projectiles in InteractiveItemArbiter

Ability throws (ABILITY_ID_KEY tagged items) were skipping
InteractiveItemArbiter.put() after the 10c6f57 cleanup, making them
invisible to pickup and dash-to interactions. Remove the guard so they
are tracked like any other thrown item; the onGrab refund path and the
disposeWithNewInteractiveItem silent-dispose override continue to handle
ability-specific behavior correctly.

Also adds the custom smithing interaction system (CustomInteractionManager,
CustomInventoryInteraction, HUD override infrastructure) and the ROTATE
throw style used by smithing refits.

* Fix: WhitespaceAround checkstyle warnings in SwordPlayer debug log

* [Perf] Gate debug allocations, cache team and distance-to-target (#301, #303, #304, #305)

#305 MovementListener: guard Debug.listener() string concat behind
  Config.Debug.LOGGING_VERBOSE_LISTENER — eliminates 100+ String
  allocations/sec per player from a no-op debug path.

#304 HostileStateMachine: gate broadcastMessage() calls in
  onAnyTransition/afterAnyTransition behind LOGGING_VERBOSE_HOSTILE —
  eliminates O(n) getNearbyEntities world scan on every mob AI transition.

#303 Hostile/HostileStateMachine: add cachedDistSqToTarget field,
  refreshTargetDistanceCache() called once at tick() start; all 13
  transition conditions replaced with getCachedDistSqToTarget() —
  eliminates 3-5 Location allocations per mob per tick.

#301 SwordEntity: cache SwordTeam in cachedTeam field set by joinTeam();
  hit() now compares getCachedTeam() fields directly instead of calling
  SwordTeam.fromEntity() twice; joinTeam() drops the values() loop and
  removes only the previously held tag — eliminates 4 allocations per hit.

* [Perf] Partition state machine transitions by from-state (#302)

Add effectiveTransitionCache: Map<Class<?>, List<Transition>> to
StateMachine. tick() now calls effectiveTransitionsFor(currentState.getClass())
which lazily computes and caches the filtered subset of transitions whose
from-class is assignable from the current state class.

Result: O(k) per-tick evaluation where k = transitions for the current state,
instead of O(N) across all transitions with isAssignableFrom on every entry.
For HostileStateMachine (~20 transitions, 10 distinct from-classes): each tick
evaluates 3-7 conditions instead of 20. Cache is invalidated on addTransition()
for correctness.

Flyweight state instance caching skipped: several state classes (RecallingState,
GrabImpaleState, StandbyState, RecoverState) hold task handle fields that are
set in onEnter and cancelled in onExit — caching would require auditing every
onExit for full cleanup, adding fragility risk for marginal gain on a code path
that only runs on transitions.

* Refactor: ThrowPhase/InventoryMode enums; gate debug scans (#304 #305 #306) (#309)

* Fix: Gate throw-cancel log behind Debug.system()
Keep cachedDistSqToTarget field in Hostile (#303) and
ThrowPhase.THROWING in ThrowAction (#306). Take incoming
SwordPlayer which includes testHudSequence() and the
4-arg itemNameDisplay(Component, Material) overloads.
Cache prevDisplayShards and prevDisplayToughness on SwordEntity; return early from updateStatusDisplayText() when neither value has changed, eliminating ~700 Component allocations per second at 35 entities.
…isplay

- ThrownItem: re-register ability items in InteractiveItemArbiter so they
  can be dash-grabbed again (onGrab's refund logic already handles them
  correctly; the non-registration guard introduced in 10c6f57 was overly broad)
- AbilitySlotItem: simplify shouldPreserveOnReplace — any item with
  ABILITY_SLOT_KEY is a managed slot item and should never be displaced to
  the player's inventory on restore
- AbilitySlotManager: replace direct setAmount/updateDurabilityDisplay
  mutations in consumeUse() with restoreSlot(), which actually pushes the
  rebuilt item into the inventory slot so the client sees the updated amount
Replace per-task ScheduledExecutorService futures with a single
BukkitRunnable.runTaskTimer(0, 1) global tick loop. All TimeArbiter
registrations now add a TaskHandle to REGISTERED_TASKS; each tick
iterates the list, fires tasks whose nextFireTimeMs has elapsed, and
removes cancelled entries via removeIf.

- Eliminates ~210-280 concurrent ScheduledFutures at runtime
- Time-bound tasks apply GLOBAL_TIME_SCALE dynamically on each fire,
  removing the need to reschedule on setGlobalTimeScale() changes
- Public API (runTimeBoundBukkitTaskOnTimer, runTimeIndependentBukkitTaskOnTimer,
  runFixedIterationTaskTimer, TaskHandle.cancel/pause/resume) unchanged
- Removes scheduleTaskFuture(), processRestartRequest(), rescheduleFuture(),
  and markedToRestart (all internal to the old executor approach)
- SwordScheduler's one-shot delays continue using the ScheduledExecutorService
@Mere-Solace Mere-Solace closed this Apr 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Perf] Consolidate scheduled task explosion into unified global tick loops

1 participant