Upstream replaced RayTraceApi/RayTraceImpl with CounterStrikeSharp's own Trace API
(CSS 1.0.375), so RayTrace-MM and RayTrace-CSS-API are no longer needed. Also brings the
CS2 1.41.8.3 gamedata fixes and the No Recoil, Long Zeus, Long Knife, Fragile Bomb and
Tripwire fixes.
Kept from this fork: Panorama skill card and target menu (PanoramaManager), css_setoverride,
WASDMenuAPI removed, no tracked "Server Files" build output. Version 1.2.4.b3-panorama1.
#### General
- **Updated markdown** files.
- Switched from `RayTrace.WorldOnlyMask` to CounterStrikeSharp's built-in `Masks.SolidBrushOnly` mask.
- Added tracking and automatic removal of beams created by traces with `drawBeam` enabled.
#### Compatibility
* **CounterStrikeSharp v1.0.375** - after the CS2 update, RayTraceImpl stopped working. All traces now use CounterStrikeSharp's native `Trace` API, so **RayTrace-CSS-API and RayTrace-MM are no longer required**. `RayTraceApi.dll` was removed from the project and the Ray-Trace step was removed from the README install guide.
* **Gamedata** - CS2 1.41.8.3 broke the Windows `HEGrenadeProjectile_CreateFunc`, `CMolotovProjectile_CreateFunc` and `SnapViewAngles` signatures, and the Linux `SmokeGrenadeProjectile_CreateFunc` signature. `INetworkGameServer_Slots` moved from 584 to 616 on both platforms; the old value read a garbage size and pointer without throwing. All signatures now match exactly one function on both platforms, and the offsets were confirmed at runtime.
#### Skills
* **No Recoil** - the client received `weapon_accuracy_nospread`, but the server still applied spread, so the crosshair looked straight while bullets spread. The weapon's spread and inaccuracy values are now also zeroed on the server for that tick. Weapon data is shared per weapon type, so a weapon type is only patched while no other living player holds the same type; the values are restored on the next tick, at map end and on unload.
* **Long Zeus** - kills went through suicide, so they did not count as Zeus kills and the ragdoll was marked `world`. The suicide damage is now rewritten to the attacker's Zeus with shock damage, so the engine credits the kill natively (killfeed, kill sound, scoreboard, ragdoll). The victim's body also gets the electric shock effect, which the engine only plays on real Taser hits.
* **Long Knife** - long-range slashes now draw a tracer to where they hit.
* **Fragile Bomb** - the bomb position was only stored by the `bomb_planted` event, which is sent only to skills active at that moment, so receiving the skill after the plant never detected hits. The planted bomb is now looked up when needed. Hits were also measured from the impact point, so shots passing through the bomb and landing behind it were missed; the bullet's path is now checked instead.
* **Tripwire** - wires stayed visible after the skill changed. Beams were hidden from clients in the same tick they were cleared, so the "hide" update never reached the client. Beams are no longer hidden from clients before removal.
#### Reliability
* **Skill distribution** - an exception thrown during skill assignment aborted the round's distribution silently. It is now caught, logged and retried. Spectators without a pawn no longer count as connected players, which had caused unnecessary retries.
Drop the csproj Copy* targets and the directory's .gitignore entry. release.sh now assembles a
single tarball laid out like game/csgo/ (plugin, gamedata, compiled Panorama layouts) straight from
the build output and sources; updater.json points at it.
"jRandomSkills - Server Files" is now gitignored build output; releases carry the built plugin.
The GeoLite2 country database moves to "jRandomSkills - SRC Files/packages" and a new
CopyPackagesToServer target copies it into the build output. The shipped configs are dropped:
the plugin writes config.json and skillsInfo.json itself when they are missing.
- ModuleVersion 1.2.4.b2-panorama1; the startup version check and its download link now use
git.zio.sh/cs2/jRandomSkills releases instead of upstream's GitHub
- updater.json lists the release
- Remove the unused WASDMenuAPI project from the solution and repo, and its README credit
- Skill card and W/S/E target menu drawn as CCSCustomHudLayout panels via PanoramaManager
(panorama/ sources), replacing PrintToCenterHtml and WASDMenuAPI; WASDMenuAPI.dll dropped
- Custom status lines (cooldowns, charges) overwrite the description on the card; Medic keeps its
description with the charge count below
- css_setoverride toggles a per-player cooldown bypass
- YourSkillChatInfo also gates the round-start skill description in chat
- Language: "[css_useSkill]" becomes the configured button name, "Press ... key" wording,
welcome message trimmed to its first line
- Build against CounterStrikeSharp.API 1.0.375
# v1.2.4.b2
#### Crashes
* **Ninja, Ghost, C4 Camouflage** - a client could crash with `CopyExistingEntity: missing client entity` when a hidden player died nearby. Hidden players are now revealed in the `player_death` pre-hook, inside the same tick as the death, so the client always holds the entity. Cleanup previously ran on a 2-tick timer and left a gap.
#### Skills
* **Falcon Eye** - weapons stayed locked into the next round if the camera was active when the round ended. Restoring the default view no longer depends on the camera entity still existing.
* **Jump Ban** - the target could still jump. Upward velocity is now zeroed on the jump input, the downward pull was raised, and the block lasts longer.
* **Jump Ban** - the "you can jump again" message never arrived. It was gated behind a freeze-time check that is always true when skills are cleared.
* **Blast Shot** - grenades could be thrown during freeze time.
* **Sound Maker** - phantom screams came from world origin `0 0 0`, and the sound was sent to the emitter's own team. Only living enemies now receive it.
* **Careful Bullets, Darkness, Deaf, Expensive Ammo, Giant, Glitch, Jammer, Jet Kick, Jump Curse, Magnifier, Nightmare, Poison, Primary Ban, Wild Throw** - the message telling the target the effect had ended never arrived, for the same freeze-time reason.
#### Freeze time
* **DisableOnFreezeTime** - the setting only applied to actively triggered skills; passive damage handlers ignored it entirely. Cypher, Exploding Barrel, Explosive Shot, Fortnite, Iana, Illusionist and Replicator now honour it.
#### Reliability
* **Skill distribution** - a round could start with nobody receiving a skill. A permission lookup for a player who was disconnecting threw and aborted the whole selection loop; those lookups are now isolated, and a failure on one player no longer stops the rest.
* **Skill distribution** - if a round still ends with no skills assigned while players are connected, distribution retries up to 6 times at 0.5s intervals and writes the reason to the debug log.
- #### Fixes
- **Pyro** - Fire damage killed the holder at low health. The regeneration now runs before the damage is applied instead of after.
- **Iana** - The clone did not absorb a lethal hit, same cause as Pyro.
- **Dracula** - Lifesteal used the raw damage, so an AWP headshot healed for 400. It is now capped to the health the victim actually had, works on the killing blow, and can no longer leave the attacker alive at 0 HP.
- **RobinHood** - Stolen money used the raw damage and a hardcoded 16000 cap. It is now capped to the victim's real health, works on the killing blow, and respects `mp_maxmoney`. The victim loses exactly what the attacker gains.
- **Voodoo** - Reflected damage used the raw damage, now capped to the victim's real health.
- **Config** - The `Rifle`, `Pistol` and `Grenade` weapon pools grew every time the config was loaded, so removed weapons came back on the next start.
- **Use button** - Skills no longer trigger when a CT presses `Use` while looking at a planted C4, so the skill key no longer competes with the defuse key.
- #### Other
- Built against CounterStrikeSharp 1.0.373.
- #### Debug
- **PerfLog** - `p50`, `p95` and `p99` returned the upper bound of the histogram bucket instead of a real sample, so they could report a value higher than `max`. It happened in 74% of the windows in a 6.5 hour session (e.g. `avg=0.06ms p99=8.00ms max=4.68ms`). Percentiles are now taken from the actual samples of the window, and the bucket estimate is only used as a fallback for windows that overflow the buffer - interpolated inside the bucket and clamped to `max`.
- **EntityManager** - An entity that was already tracked logged a second `+` line, so creates and destroys stopped matching and the entity log read like a leak. Re-registering now logs `~ oldType -> newType` when the type changes and nothing when it does not.
- **Ghost, Ninja, C4Camouflage** - The hiding prop was registered twice, first as `prop_dynamic` and then as `empty_prop`. It is now tracked under the right name from creation through the new `trackAs` parameter of `CreateTrackedDynamicProp`.
- #### Fixes
- **Startup crash** - Servers with `PerfMode` or `DebugMode` enabled crashed during boot at the first skill. `PerfLog.Sample` called `IsServerIdle()` on every `SkillAction`, which scanned the player list while the server was still booting. Our own regression, introduced with the idle-logging gate in b8.
- **PlayerManager** - Added a `serverActive` flag set on map start; `IsServerIdle`, `GetTickPlayers` and `GetTickBomb` no longer touch the game before the map is up.
- **Commands** - `zmienmape` and one Chinese alias were listed twice, registering the same command name natively two times. Cleaned up the defaults and added a duplicate guard so user configs are covered too.
- #### Chat
- **Skill announcement** - Teammate skills are now shown first and your own skill description last, so it stays at the bottom of the chat instead of scrolling away.
- **SetRandomSkill** - No longer leaves the chat box unclosed when the player has teammates.
- #### Shared Systems
- **PlayerManager.FillSkillHolders** - Skill holders are resolved from the player index instead of scanning every controller each tick, replacing the per-skill full player loop used by ten skills.
- **PlayerManager.GetControllerByPawn** - Tick-cached pawn to controller map, replacing the linear observer-target scan.
- **PlayerManager.IsServerIdle** - Tick-cached idle check shared by the debug and perf writers.
- **SkillUtils.IsFriendlyFireBlocked** - Now takes the skill, so the new per-skill `FriendlyFire` key and the server convars are resolved in one place. The convar-only overload remains for victim-side skills.
- **SkillUtils.TerminateRound** - Accepts the player who ended the round and pays out the round-end money that the engine skips on plugin-terminated rounds.
- #### Performance
- **OnTick(hud)** - Average cost down 17% against the previous build.
- **CheckTransmit** - Average cost down 25%, peak down 39%.
- **OnTick(skills)** - Peak cost down 62%.
- **Host stalls** - Real freezes measured with players connected dropped from six to two across 785 rounds.
- **DecoyRing** - Beam creation is spread across four frames instead of spawning the whole ring in one tick.
- **Fourteen skills** - `OnTick` now returns immediately when the skill holds no state.
- **Berserker, BladeMaster, BunnyHop, DemonEye, Flash, PawelJumper, Pilot, Poison, QuickShot, RadarHack, Regeneration** - Switched to the shared skill-holder list; config reads hoisted out of per-player loops.
- #### Fixes
- **GravityDecoy** - Restored a hardcoded gravity of 1.0 when leaving the field, permanently breaking Astronaut. Each pawn's own scale is now captured and restored.
- **PsychicDefusing** - The bomb location stayed unset when the skill was assigned after the plant, the stored position pointed into the entity and dangled once the bomb was killed, and setup was silently skipped when the pawn was not ready.
- **PsychicDefusing, FragileBomb** - Winning teams were paid nothing when the plugin ended the round. Winners are now paid from the matching `cash_*` convars, with the defuser bonus on top.
- **ToxicSmoke, HealingSmoke** - Smoke kept damaging and healing after its owner lost the skill, and expiry left behind smokes whose owner had already lost it. HealingSmoke never tracked an owner at all.
- **EmpGrenade** - Crosshair and radar stayed jammed forever, because `OnTick` stops being dispatched once nobody holds the skill and the jam timer never expired.
- **ThrowingKnife** - Losing the skill while the knife was thrown destroyed it without giving one back.
- **OnlyHead, ReactiveArmor** - Both ignored fall and world damage, which has no player attacker.
- **Gambler** - Refreshing consumed the one-shot instead of rolling two new skills, and the refresh row lost its colour prefix to the text-direction wrapper.
- **Berserker, Flash** - The falling-speed clamp fired while noclipping.
- **ExplodingBarrel** - The prop spawned without a model and received it a frame later; the model is now passed through `CEntityKeyValues` at spawn.
- **FastReload** - Now has a cooldown, five seconds by default, with a HUD countdown. The cooldown only starts when a magazine is actually refilled.
- **Config** - Missing-key migration now descends into nested sections, so keys under `HtmlHudCustomisation` are detected.
- #### Damage Hook
- **OnEntityTakeDamagePre / OnEntityTakeDamagePost** - Replaced the deprecated `CBaseEntity_TakeDamageOldFunc` hook across 27 files.
- **Teammate damage** - The new hook runs before the engine zeroes friendly fire, so attacker-side modifiers need explicit guards. Added to OneShot, Cutter, Assassin, Soldier, Berserker, Nemesis and Punisher.
- **FriendlyFire** - New per-skill key on twelve skills, defaulting to `true`. Setting it to `false` stops teammates being affected, the way LongKnife and KillerFlash already allow. Covers both the damage amplifiers and the explosives that previously only reduced teammate damage.
- #### Menu
- **WSADMenuVisibleItems** - New setting replacing the hardcoded three visible menu rows, clamped between one and ten.
- **WSAD menu** - The final menu row keeps its `#RRGGBB` colour prefix instead of losing it to the text-direction wrapper.
- #### Config Migration
- **skillsInfo.json** - Missing keys are written back to the file on load instead of only being defaulted in memory. Existing values are preserved.
- **playersLanguage.json** - Now grouped by language instead of one row per player, which cuts a ten thousand player file by 20%. Old files are converted on load and invalid SteamIDs are dropped.
- #### Debug
- **Perf samples** - Report `p50`, `p95` and `p99` alongside the average and maximum.
- **Per-skill sampling** - Every skill is now sampled, instead of only logging the calls that exceeded two milliseconds. The measurement was already running; only the reporting threshold discarded it.
- **Load context** - Aggregate lines carry the player, alive, bot, active-skill and round counts, so sessions can be compared under different loads.
- **ENTITIES** - Tracked entities are broken down by type.
- **STALL** - Lines carry the tick, tracked entity count and load context, which separates map-load pauses from genuine freezes.
- **DMGHOOK** - Reports how often the attacker and ability resolve on the damage hook.
- **Idle server** - Nothing is written while the server sits empty, apart from lifecycle lines and player connects and disconnects.
- #### Cleanup
- **Compiler warnings** - All twelve resolved; the build is clean.
- **SkillUtils.SetPlayerCollisions** - Removed, its body started with an unconditional `return`.
- **Damage hook aliases** - Dropped the `param` and `param2` locals the migration left behind across 27 files.
## Shared Systems
* **DecoyTracker** - owner-aware decoy tracking shared by FrozenDecoy, GravityDecoy and MagneticDecoy, replacing the per-skill position dictionaries.
* **DebugCategory** - new flag enum backing the numeric `DebugMode`, with automatic migration from the old boolean value.
* **SkillsInfo.GetSkillConfig** - indexed skill config lookup replacing the linear scan used by draws and HUD rendering.
* **SkillNames** - cached enum name table, removing repeated `ToString()` calls from the draw path.
* **SkillUtils.HideCarriedEntities** - shared carried-weapon resolution for C4Camouflage, Ghost and Ninja.
## Performance
* **Skill draw** - replaced the O(n²) config lookup; rounds exceeding the perf threshold dropped from 89% to 0.3%.
* **CheckTransmit** - the weapon chain is now resolved once per hidden pawn instead of once per receiver; Ghost cost down 61%.
* **Pilot** - the exhaust trail is paused and resumed instead of being destroyed and recreated on every jump release.
* **MagneticDecoy** - config reads and the pawn list are hoisted out of the per-decoy loop.
* **FrozenDecoy** - config reads are hoisted out of the per-tick loop.
* **Debug** - the damage hook is only installed when damage logging is enabled.
## Fixes
* **PsychicDefusing** - can no longer finish a defuse and award an extra round after the round has already ended.
* **Spectator** - now picks a different enemy on each activation and no longer creates an unused camera prop every time the skill is switched off.
* **Random** - replaced the shared `Random` instance with `Random.Shared`, preventing degenerate results from concurrent use.
* **Baseball** - decoys are removed when their owner dies or loses the skill.
* **FrozenDecoy, GravityDecoy, MagneticDecoy** - decoys and their ground rings are removed when the skill is taken away.
* **ThirdEye** - fixed the camera check dereferencing a missing `CameraServices`, and the door toggle now verifies the entity class before casting.
* **Round start** - fixed `[gamerules unavailable]` appearing on the first freeze-time end after every map change.
* **Config** - added migration for keys missing from existing configuration files.
## Menu
* **WASDMenuAPI** - fixed a malformed `<font>` tag that wrapped every non-hovered menu row in an unclosed attribute.
* **WSAD menu** - the controls line is no longer dropped when the header line is hidden.
* **HtmlHudCustomisation** - line size options left empty now hide their line instead of emitting an invalid font class.
## Debug
* **DebugMode** - changed from a boolean to a number with concatenated category digits: `1` Skill, `2` Round, `3` Entity, `4` Damage. `123` enables Skill, Round and Entity. `0` disables logging. Existing `true` values migrate automatically.
* **EntityManager** - entity creation, destruction and failures are now logged under the Entity category instead of the server console.
* Startup now reports which debug categories are active.
New skills (9)
- Bounty: put a cash price on an enemy's head, the killer collects it.
- EmpGrenade: grenade victims lose radar and crosshair for a while.
- GravityDecoy: the decoy alters gravity for everyone in its radius.
- Heavyweight: immune to push and slow effects from other skills.
- Nemesis: the marked player takes extra damage from everyone.
- Punisher: banks part of the damage you take and releases it on your next hit.
- Rewind: drop a marker and teleport back to it shortly after.
- TrueArmor: armor absorbs part of incoming damage instead of health.
- Voodoo: the marked enemy shares the damage you take.
Shared systems
- DecoyRing: reusable ground ring so decoy skills show their real area of effect. Adopted by FireRain, FrozenDecoy, GravityDecoy and MagneticDecoy.
- WeaponPool: single cached weapon classification (rifle/pistol/grenade) with a normalizing lookup, backed by the new config Weapons section. Replaces the duplicated hardcoded arrays in Assassin, Chicken, Ghost, NoNades, PrimaryBan, RandomWeapon, SniperElite and WeaponsSwap. [because of compatibility with WeaponRestrict]
- HUD suppression registry in SkillUtils: crosshair and radar hiding is now refcounted per owner, so overlapping skills (Glitch, Jammer, EmpGrenade) cannot restore each other's state early.
- SkillUtils.ResolveHiddenPawns: shared invisible-pawn resolution for C4Camouflage, Ghost and Ninja.
- Config gained a Weapons section with automatic migration for existing installs, plus a per-skill DisableOnPistolRound switch honoured by both the round draw and css_setskill.
Sound
- Volume rebalance
- Add SkillUtils.EmitSoundToPlayer with a private RecipientFilter. A null filter had been broadcasting every skill sound to all clients. Migrated 16 call sites: Poison, DemonEye, ToxicSmoke, CarefulBullets, Thorns, HotBomb, LastGasp, LongKnife, Nightmare, Jester, Tripwire, Medic, HealingSmoke, Rewind, Watchmaker.
- New effects: LongKnife knife-hit feedback (Weapon_Knife.Hit.Heavy/Light), Grapple hook impact, Fortnite barricade placement, Ricochet bounce, Nightmare cursed tone, Replicator/Illusionist spawn, PsychicDefusing defuse cue, TakeAmmo pickup, Tripwire and Jammer trigger tones.
- 29 skills gained a configurable SoundVolume, LongKnife also HitSoundVolume.
- Grapple: emit the hook impact at the hook entity instead of at the owner so the enemy team can locate it.
- PawelJumper: the engine already plays the first jump, only extra jumps emit.
Models and visuals
- Grapple: real hook model with configurable hookScale and hookEmbed so the hook sits in the surface it hit.
- Pilot: glowing exhaust trail parented to the pawn, active only while airborne.
Performance
- SkillsInfo.GetValue now resolves through a ConcurrentDictionary cache instead of reflecting on every call; invalidated on config reload.
- HUD render cache keyed on player, config, description flag and the three source lines; identical frames skip the rebuild.
- Glaz.CheckTransmit: hoist smoke index resolution out of the per-viewer loop and build the pawn-owner map lazily.
- playersSkills switched from ConcurrentBag to HashSet<Skills>, turning the NoRepeat filter from an O(n) scan into a set lookup.
Fixes
- NoRepeat could hand a player two or three skills in one round because the history bag was never deduplicated; the HashSet rewrite closes it.
- SniperElite: restore the saved weapon after the halftime team swap instead of dropping it on the pistol round.
- Illiterate: the holder alert now reaches chat and is exempt from its own scrambler via PrintToChat(ignoreIlliterate).
- EntityManager.DestroyBeam: zero out width and render colour before the kill so beams cannot linger visibly, with a next-frame Remove fallback.
- Ricochet: drop all 18 diagnostic Trace calls and the Trace helper; the skill measured stable over 48h and the tracing dominated the debug log.
Debug
- Tag round start, round end and freeze-time-end lines with [WARMUP] so warmup load is separable from live rounds.
## GENERAL
- The HUD loop now runs on the shared 16 Hz frame gate (SkillUtils.IsHudFrame) instead of keeping its own every-other-tick schedule, and fifteen skills that were refreshing their HUD on every single tick were moved onto that same gate.
- Added SkillData.GetSnapshot(), a cached array of the registered skills, so the HUD no longer copies the whole skill bag every frame. The cache is invalidated together with the existing lookup map, so a reload still picks up new skills.
- Added SkillUtils.ForceFullUpdateToAllChunked(), which spreads a full network update across frames (two clients per frame) instead of pushing one for every connected client in a single tick. The original ForceFullUpdateToAll is untouched, so the six skills that already call it keep their current behaviour.
- BlastShot, Cypher and Iana now clear their HUD line in DisableSkill, so a stale line can no longer survive the skill being taken away.
## FIXES
- SoundMaker threw a divide-by-zero on every tick when its cooldown was configured as 0, because the tick gate divides by that value. The cooldown is now clamped to at least 1.
- Regeneration and RadarHack were calling the wrong SkillsInfo.DefaultSkillInfo overload, which silently dropped disableOnFreezeTime, requiredPermission, hudDuration, descriptionHudDuration, maxPerServer and rarity. Every configured field now reaches the base class, so those two skills finally honour their own config.
- Falcon Eye blocked the trigger on the wrong side of the check: picking up a weapon while back in the normal view locked primary and secondary fire, because the pickup handler tested for the player being in their own view instead of in the skill camera.
- Spectator locked primary and secondary fire when its camera could not be created at all, which happens when no enemy is alive to spectate. The player stayed in the normal view with a weapon that would not fire until the round ended.
## SKILL IMPROVEMENTS
- Wallhack:
-- The glow build and the full network update now start when freeze time ends and are spread across frames, instead of running in a single tick the moment the skill is handed out. On a busy server this used to cost roughly 13 ms in one frame, close to a dropped tick at 64 tick; across four grants in an eight-hour session it no longer reaches the 2 ms profiler threshold at all.
- Jackal:
-- CheckTransmit resolves the trail relays once per frame rather than once per viewer, so the same entity lookups are no longer repeated for every client on the server.
-- Removed an opponent list that was gathered in EnableSkill and never used.
- ThrowingKnife:
-- CheckTransmit builds the knife glow list once per frame, and the viewer index is resolved once per client instead of once per knife.
- Glaz:
-- The observer target is only resolved for players who do not own the skill themselves, and the lookup now bails out early when there is no observer target at all.
- RadarHack and QuickShot:
-- The skill check runs before the validity check, so players without the skill are skipped immediately instead of being fully validated first.
- Distancer, HealingChicken and SoundMaker:
-- These now return straight away while nobody holds the skill, rather than walking the player list every tick.
## DEBUG
- Removed the [HITGROUP] mismatch check. Every recorded case was accounted for: ten were Aimbot rewriting the hit group as designed, and thirteen were non-bullet skill damage, which reports a generic hit group by nature. Dropping the check also removes a dictionary write from every damage event.
- Removed the identity-split tag from the damage log. It was dead by construction, because the victim controller had already been routed before the comparison ran, so it could never report a difference.
- Removed the player-shoot handler, which produced no output on this build.
- [DMG] lines now print the routing fields only when the routed index actually differs from the original one, so the check remains as a tripwire without adding noise to every line.
## TRANSLATIONS
- Turkish: reworded sixty-two entries that padded their sentences with second-person verb endings, dropped the English glosses left in parentheses, and settled every [css_useSkill] line into one shape. Also fixed genuine typos in Dracula, Giant, Thorns and Impostor.
- All eight languages received the strings for the four new skills. (AI)
## NEW SKILLS:
- Chameleon: The first player you kill gives you their skill.
- Grapple Hook: Press [css_useSkill] to fire a hook at the point you are aiming at and pull yourself there.
- Inheritance: When a teammate dies you can take over their skill.
- Ricochet: Your bullets bounce off walls and can still hit enemies.