Skip to content

Changelog#

All notable changes to the Xenon Plugin SDK will be documented in this file.

Format follows Keep a Changelog.

[Unreleased]#

Fixed#

  • Ability cooldown slots now follow physical inputs and failed reads no longer look ready. Skill1 is Shift and Skill2 is E (including Kiriko: Swift Step / Protection Suzu); Bastion's Tactical Grenade is secondary fire. Samples now report Unsupported, Unavailable, or Available, root-variable definitions with allocation zero resolve correctly, GUID misses fail closed, and held remote Entity objects query current cooldowns by stable entity ID. Ultimate charge is no longer exported as a fake cooldown timer.
  • Button pulses no longer lose their make edge under scheduler or backend delay. The holdMs window now starts after the host successfully emits the press, not when the request is merely applied. Make and break are kept in distinct command phases, and the GameMemory backend waits for dispatcher consumption when observable. Multi-stage placement abilities must still wait for their preview-ready game state before submitting the confirm input.
  • Entity/view direction contracts are now reliable. Remote Entity::GetForward() now derives normalized model/body facing from the validated ModelBase + 0x3B0 quaternion instead of reading the local-only connection component. Local command-direction reads are normalized and use StableInstance across the update rebuild gap. GetCameraForward() (also exposed as GetViewDirection()) now works from both on_frame() and on_render(). GetYaw() remains a legacy alias for the newly explicit GetModelYaw() and is documented as body/model yaw, not view yaw.
  • Plugins no longer hang on default-constructed PluginEntity / Entity / Entity::Local(). Recent clang versions enable the WASM bulk-memory-opt feature by default, which lowers MyStruct{} zero-init for structs larger than ~32 bytes to a memory.fill instruction. WAMR's fast interpreter does not implement bulk-memory ops and silently traps on the opcode — every default-init of PluginEntity (and therefore every Entity constructor, LocalPlayer() call, etc.) appeared to hang the on_frame call. build.bat now passes both -mno-bulk-memory and -mno-bulk-memory-opt, forcing clang to emit explicit i32.store loops instead. Rebuild any prebuilt plugins. If you maintain a custom build pipeline, mirror these flags.
  • Entity and aim queries now work from on_frame(). Entity queries (IsEnemy, IsAlly, IsVisible, GetDistance, FindBestTarget, PredictPosition) and aim synchronization queries (AimHitsHitbox, AimAtPositionEx, AimAtBoneEx, AimHitTest) are phase-agnostic. They use the locked update snapshot in on_frame() and the lockless render snapshot in on_render(). Remaining render-only natives (get_fov_to_entity, get_screen_offset_to_bone, get_closest_bone_in_fov, find_best_target_fov, get_entity_hitboxes / get_entity_hitbox, get_lerp_history) emit the one-shot phase warning outside on_render().
  • Library plugin functions are now reachable from dependent plugins. When every call site to a library wrapper (e.g. math::Lerp) was foldable to a constant or otherwise eliminated by -O2, wasm-ld's --gc-sections would drop the underlying lib_* import declarations from the dependent plugin's imports section. The host bridge then had nothing to resolve, and calls silently failed with no diagnostic. Library import headers in include/xenon/libs/*.hpp now include a __attribute__((used)) keepalive stub that roots every import declaration. Library authors writing new headers must follow the pattern shown in the Dependencies guide — see include/xenon/libs/math_helpers.hpp for the canonical template.
  • GetUltCharge() and Entity::GetUltCharge() now return real values. Both were reading GetLookUpSkill(0x00F8), a skill-list slot that isn't populated reliably across heroes — so the result was always 0.0 for every hero. They now mirror the canonical source used by the in-game cooldown UI: Entity::UltCd.current (a 0-100 percentage where 100 = ready). IsUltReady() becomes accurate as a side effect. As a bonus, Entity::GetUltCharge() is now populated for all hero entities, not just the local player.
  • Remaining render-phase APIs now fail loudly instead of silently. Calling WorldToScreen, WorldToScreenUnclamped, GetCameraPosition, GetViewMatrix, or any Draw::* function from on_frame() returns zeros with a one-shot warning per plugin. GetCameraForward() is now phase-safe and is no longer part of this restriction.
  • fmt::float_to_str (and therefore TextBuilder::putFloat) no longer traps WASM when given NaN, ±Inf, or values ≥ 2³². The trapping i32.trunc_f32_u instruction would silently kill on_render and disable the plugin after 10 consecutive frames; non-finite values now produce "nan" instead. Common trigger: putFloat(entity.GetPosition().x, ...) on a frame where the entity wasn't fully populated.
  • Entity::Local() (used by LocalPlayer()) now sets m_valid based on alive || health > 0, mirroring the index constructor. Previously it set m_valid = true unconditionally, so IsValid() always returned true and never gated reads — callers reading GetPosition() etc. on an unpopulated snapshot could pick up non-finite floats.

Removed#

  • GetUltDuration() (free function and Entity method) and PluginEntity::ultDuration field. The 0xD7 duration variable at root varbag is generic and not reliably ult-specific; ult active/duration state should be derived from IsUltActive() + hero-specific variables instead. Breaking ABI change — plugins must be rebuilt against the updated PluginEntity layout.

Added#

  • GetAbilityCooldown(input), GetSecondaryFireCooldown(), SkillCooldown::IsAvailable(), IsSupported(), and IsReady() for explicit input-based cooldown queries.
  • GetDvaSelfDestructState(out) exposes the spawned Self-Destruct mech's active state, position, and team/enemy classification. Remote D.Va ult charge is not a bomb timer.
  • Entity::Refresh() updates a held entity snapshot and repairs moved entity-array indices by stable host ID.
  • Versioned aim synchronization API - AimAtPositionEx / AimAtBoneEx return AimStepResultV1 with Invalid / Turning / Settled state, observed angular error/velocity, command tick, and consecutive settled ticks. AimHitTest returns detailed sphere/capsule geometry, visibility, safety-margin fireReady, explicit projectile padding, predicted/backtracked hitbox translation, and bodySlotFilter selection.
  • Tracked input receipts - GetGameCommandTick, PressGameButtonTracked, PulseGameButtonTracked, and GetInputReceipt expose queued/applied/edge-written/input-consumed/released/rejected/superseded command stages plus InputBackendV1. Superseded is terminal but may have emitted or still affect a live held/timed action, so it is not a safe retry signal. Tracked calls accept one action bit; invalid/multi-action values return 0, while queue-full rejection returns a nonzero ID with Rejected state. GameMemory can observe InputConsumed; KernelDriver cannot and advances from EdgeWritten to Released. Milestone ticks are stable-bracket observations and can remain 0 when the event crosses a command boundary. Input consumption is not proof that a shot fired or projectile spawned; keep aiming through cast/startup confirmation.
  • GetMouseDelta() / GetMouseDelta(out) — reads the game's current accumulated mouse delta pair from InputManager as a Vector2.
  • SetSensitivity(value) — writes the player's in-game mouse sensitivity setting.
  • HeroId::Shion — new hero constant (0x02E0000000000420). GetHeroName(HeroId::Shion) returns "Shion".
  • HeroId::Sierra — new hero constant (0x02E0000000000410). GetHeroName(HeroId::Sierra) returns "Sierra".
  • PulseGameButton(bit, holdMs = 50) — presses a game button and schedules an automatic release. Required for edge-triggered abilities (Skill1, Skill2, Ult, Reload, Interact, Melee): PressGameButton alone holds the bit indefinitely and blocks the rising edge that fires the ability, which made spell-activating plugins appear broken. Auto-release is driven by the host input spin thread (no plugin-side timer needed).
  • Per-entity cooldowns and durations — cooldown/duration data is now available on all hero entities, not just the local player:
  • Entity::GetSecondaryFireCooldown() / GetSkill1Cooldown() / GetSkill2Cooldown() — per-entity cooldown state by physical input
  • Entity::GetSkill1Duration() / GetSkill2Duration() / GetSkill3Duration() — per-entity duration state
  • PluginEntity struct now includes skill1Cd, skill1Duration, skill2Cd, skill2Duration, skill3Cd, skill3Duration, ultCd fields (PluginCooldown struct)
  • GetSkill3Duration() — Skill 3 duration for the local player (was missing from the global API)
  • SkillCooldown struct moved to Types.hpp (was in Core.hpp) for use by both Core.hpp and Entity.hpp
  • Hotkey struct (Hotkey.hpp) — convenience type wrapping key binding, edge detection, toggle, and config persistence:
  • Update() — call once per frame for edge detection
  • IsDown() / Pressed() / Released() — level and edge-triggered key state
  • Toggle(bool&) — flip a bool on press
  • Render(label) — keybind menu widget
  • Load(key) / Save(key) — config persistence
  • Ability Duration API — track remaining time on scripted abilities (e.g. Genji Dragonblade, Soldier Visor):
  • GetSkill1Duration() / GetSkill2Duration() — returns SkillCooldown{current, max, enabled} where current = time remaining, max = total duration, enabled = ability actively running
  • Hero-specific: duration variables are resolved per-hero using the same allocIdx as cooldowns with varKey 0xD7
  • WorldToScreenUnclamped(worldPos, screenPos) — projects a 3D position to screen coordinates without rejecting off-screen results. Use for AABB bounding box projection where corners may extend beyond screen edges.
  • ImGui::ColorPicker(label, color) — color swatch button that opens a full color picker popup (uses host's XenonColorButton widget). Alpha is preserved but not editable in the picker.
  • Skill Cooldown API — per-input cooldown state for the local player:
  • GetSecondaryFireCooldown() / GetSkill1Cooldown() / GetSkill2Cooldown() — returns an availability-aware SkillCooldown with IsOnCooldown(), IsReady(), and GetPercent() helpers
  • Hero-specific: cooldown variables are resolved per-hero automatically
  • GetWidowCharge() — Widowmaker sniper scope charge level
  • Aim Control API — full aim system for plugins, matching the internal aimbot's mechanics:
  • AimSetDirection(dir) — instantly set the command/firing direction vector
  • AimGetDirection() — read the current command/firing direction vector
  • AimSetAngles(pitch, yaw) — instantly set command angles in radians
  • AimGetAngles() — read current command angles (pitch, yaw) in radians
  • AimAtPosition(target, stiffness) — aim at a world position with critically-damped spring smoothing
  • AimAtBone(entityIndex, boneId, stiffness) — aim at an entity's bone with spring smoothing
  • AimResetSmoothing() — reset spring velocity (call on target switch)
  • AimHitsHitbox(entityIndex, hitboxScale) — check if current aim intersects entity hitboxes (returns BodySlot or -1)
  • atan2f(y, x) — math helper for angle calculations (was missing from freestanding WASM)

Changed#

  • aim_to / aim_to_bone host bindings are now functional (were stubs). Third arg is spring stiffness (0 = instant).
  • Breaking: Renamed skill fields and functions for accuracy:
  • PluginEntity.skill1Cd / skill2Cd / skillECdskill1Active / skill2Active / skill3Active (these are active-state flags, not cooldowns)
  • GetSkill1Cooldown() / GetSkill2Cooldown() / GetSkillECooldown()IsSkill1Active() / IsSkill2Active() / IsSkill3Active() (now return bool)
  • WASM imports renamed: get_skill1_cooldownis_skill1_active, get_skill2_cooldownis_skill2_active, get_skill_e_cooldownis_skill3_active

Added (previous)#

  • Entity::GetTotalHealth() — alias for GetHealth() (total HP including all pools)
  • Entity::IsFullHealth() — returns true if health >= maxHealth
  • Entity::GetOverhealth() — returns current overhealth amount
  • Entity::GetArmor() — returns current armor
  • Entity::GetBarrier() — returns current barrier
  • Entity::GetForward() — returns the entity's forward-facing direction vector
  • Entity::IsTargetable() — returns true if entity can be targeted (not invulnerable/phased)
  • Entity::IsReloading() — returns true if the entity is currently reloading
  • Entity::GetHitboxCount() — returns the number of hitboxes for this entity
  • Entity::GetHitbox(index, out) — retrieves a single hitbox by index
  • Entity::GetHitboxes(out, maxCount) — retrieves all hitboxes into a buffer, returns count
  • Entity::GetLerpHistory(out, maxCount) — retrieves server tick position history (newest first)
  • Hitbox struct — hitbox data with boneIndex, radius, bodySlot, isCapsule, worldPos, capsuleEnd
  • LerpEntry struct — tick + position pair for lerp history
  • BodySlot namespace — body region classification constants (Head, Neck, Body, Chest, etc.)
  • PluginEntity.overhealth — overhealth field in raw entity data
  • PluginEntity.hitboxCount — hitbox count in raw entity data
  • PluginEntity.isTargetable — targetability flag in raw entity data
  • PluginEntity.isReloading — reload state flag in raw entity data
  • PluginEntity.forward — forward direction vector in raw entity data
  • WeaponInfo.reloading — explicit reload state in weapon info
  • WeaponInfo.skillBlocked — ability-blocked state in weapon info

Added (previous)#

  • GetCameraPosition() — returns world-space camera position (extracted from VP matrix)
  • GetCameraForward() — returns normalized camera forward direction (derived from VP matrix)
  • GetViewMatrix(float* out16) — copies the raw 4x4 view-projection matrix (row-major)
  • GetUltCharge() — returns local player ult charge percentage (0-100)
  • IsUltReady() — returns true if ult charge >= 100%
  • IsUltActive() — returns true if ultimate is currently active
  • IsSkill1Active() / IsSkill2Active() / IsSkill3Active() — returns true if skill is active (renamed from GetSkill*Cooldown)
  • GetHeroState() — returns bitmask with HeroState::Ulting (0x1) and HeroState::Reloading (0x2)
  • GetRailgunCharge() / GetIllariCharge() / GetHanzoCharge() — hero-specific charge values
  • GetLookupSkill(lookupId) — generic skill value lookup by ID
  • SkillId namespace with known lookup IDs (UltCharge, SojournCharge, IllariCharge, HanzoCharge, SombraAmmo)
  • HeroState namespace with bitmask flags (Ulting, Reloading)
  • Entity::GetUltCharge(), IsUltActive(), IsSkill1Active(), IsSkill2Active(), IsSkill3Active() accessors
  • GetWeaponInfo(inputFlags, out) — query local player weapon state (valid, useable, shootable, hasGravity, projectile speed)
  • WeaponInfo struct with hasGravity field and InputFlag constants (PrimaryFire, SecondaryFire, ScopedShoot, Skill1, Skill2, Ultimate, PrimaryRelease)
  • Color::Lerp(a, b, t) — linear interpolation between colors
  • Color::HealthGradient(percent, alpha) — red→yellow→green health color
  • Vector3::RotatedY(yaw) — rotate vector around Y axis
  • Draw::TextShadow() — text with 1px shadow for readability
  • Draw::TextCentered() — roughly centered text with shadow
  • Draw::RectCorners() — tactical corner bracket rectangle
  • ImGui::ColorSliders(label, color) — 4-slider RGBA color picker
  • Config::GetColor(key, default) / Config::SetColor(key, color) — load/save Color as 4 int keys
  • IsIngame() — check if currently in a match
  • GetMapId() — current map identifier
  • GetSensitivity() — in-game mouse sensitivity
  • LogDebug(msg), LogWarning(msg), LogError(msg) — log level wrappers in Core.hpp
  • GetCurrentHero() — returns the local player's current hero pool ID
  • Entity::GetBoundsMin() / Entity::GetBoundsMax() — AABB accessor aliases (same as GetDelta1/GetDelta2)
  • Raycast(from, to) — world-space line trace, returns RaycastResult with hit position, fraction, and hit flag
  • IsPointVisible(from, to) — quick LOS check between two world points (fraction > 0.98 = visible)
  • IsRaycastReady() — check if the native raycast system is initialized
  • RaycastResult struct with IsHit() and IsVisible() helpers
  • GameButton namespace — game-level button constants (LMouse, RMouse, Jump, Crouch, Skill1, Skill2, Ult, Reload, Melee, etc.)
  • GetClientPing() / GetServerPing() — query current network latency in ms
  • PressGameButton(bit) / ReleaseGameButton(bit) — inject game-level button inputs

Changed#

  • PluginEntity.ultCharge now returns real ult charge for the local player (was always 0)
  • PluginEntity.ultActive now returns real ult active state (was always 0)
  • PluginEntity.skill1Active/skill2Active/skill3Active .x now returns 1.0 when skill is active (was always 0; renamed from skill1Cd/skill2Cd/skillECd)
  • GetCrosshairPosition() now returns screen center instead of (0, 0)
  • IsAimKeyDown() now checks right mouse button instead of returning false
  • enemy_esp.cpp — refactored to use SDK drawing helpers (removed ~55 lines of local boilerplate)
  • enemy_outlines.cpp — refactored to use Color, ImGui::ColorSliders, Config::GetColor/SetColor
  • enemy_glow_outlines.cpp — refactored to use Color, Config::GetColor/SetColor
  • Clarified SetOutlineGlow comment — it is equivalent to SetOutline, uses the glow import path internally

[1.0.0] — 2026-02-28#

Initial documented release. SDK headers, build system, and examples.

Core#

  • Log, GetTime, IsKeyDown, ScreenSize, ScreenCenter, WorldToScreen

Entity#

  • Entity class with full state queries (health, position, bones, visibility, team)
  • Players() range iterator, LocalPlayer(), GetPlayer(), GetPlayerCount()
  • FindBestTarget(flags), FindBestTargetInFov(fov, bone, flags)
  • CalcAngle, GetHeroName, PredictPosition, GetFovTo, GetScreenOffsetTo, GetClosestBoneInFov
  • SetOutline, SetOutlineGlow + Visible/Occluded shorthands
  • TargetFlags namespace (Enemy, Team, Visible, LowHP)

Drawing#

  • Draw::Line, Circle, CircleFilled, Rect, RectFilled, Text, HealthBar

ImGui#

  • Checkbox, SliderFloat, SliderInt, Combo, CollapsingHeader, Separator, Hotkey

Config#

  • GetBool, GetFloat, GetInt, SetBool, SetFloat, SetInt, Save

Types#

  • Vector2, Vector3, Color, Team, EntityType
  • Bone namespace (actual game skeleton indices)
  • HeroId namespace (all heroes through Jetpack Cat)
  • VK key constants, OutlineType, PluginFlags

Build#

  • build.bat — single-file and batch compilation
  • --library flag for library plugins
  • Dependency system with XENON_PLUGIN_INFO_DEPS