v1.2.3.b1
Revive skills (Second Chance / Phoenix / Re-Zombie) — unified "on death" logic: - Intercept the lethal hit before it is applied. Previously they reacted after the death was already committed, so one-shots, fall damage and skill-based kills were missed (Phoenix and Re-Zombie were rewritten from a post-death handler to pre-damage interception). Killer Flash: - Kills through real damage instead of forcing a suicide, so revive skills can intercept. Baseball: - Fixed a rare server crash from decoy grenades: a freed decoy's entity index could be reused by another entity and written to (use-after-free). Decoy tracking is now validated per tick (DesignerName gate) and cleared every round. Native crash hardening: - Aimbot: guard the hit-group native pointer before writing (avoids writing to freed/null memory). - Wallhack: detach the glow when the target dies or disconnects (stale FollowEntity on a freed pawn). - Spectator / Cypher / FalconEye / ThirdEye: null-guard the camera SceneNode walk and re-validate the target before teleport/parent (cross-frame freed-entity deref). - CServerSideClient.ForceFullUpdate now bails on a null client handle. Performance: - Added a per-tick player/bomb cache (PlayerManager.GetTickPlayers / GetTickBomb). - Migrated 82 skills from per-skill Utilities.GetPlayers() to the one shared per-tick scan, and the HUD loop reuses it. Much lower per-tick cost at high player counts (verified 10v10). RayTrace: - Prints a single notice when the module is not installed; dependent skills (Long Zeus, Long Knife, Iana, Cypher, Noclip, Shade + the skill-use aim check) no-op instead of failing silently (#43).
This commit is contained in:
parent
c7ee500957
commit
0e86f54e0a
113 changed files with 717 additions and 437 deletions
|
|
@ -28,7 +28,7 @@ namespace src.player.skills
|
|||
{
|
||||
foreach (var knifeInfo in knivesInfo.Values.ToArray())
|
||||
DisableKnifeSkill(knifeInfo);
|
||||
|
||||
|
||||
knivesInfo.Clear();
|
||||
}
|
||||
|
||||
|
|
@ -50,18 +50,19 @@ namespace src.player.skills
|
|||
float y = BitConverter.ToSingle(packedParams, 11);
|
||||
float z = BitConverter.ToSingle(packedParams, 15);
|
||||
soundPos = new Vector(x, y, z);
|
||||
} catch { }
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
if (soundPos == null) return;
|
||||
|
||||
(var knife, var knifeInfo) = GetClosetKnife(soundPos);
|
||||
|
||||
|
||||
if (knife == null || !knife.IsValid || knife.AbsRotation == null)
|
||||
return;
|
||||
|
||||
|
||||
if (knifeInfo == null || knifeInfo.InitialLook == null || knife.Collision == null)
|
||||
return;
|
||||
|
||||
|
||||
knifeInfo.IsDropped = false;
|
||||
QAngle rotation = new(knifeInfo.InitialLook?.X, knifeInfo.InitialLook?.Y, knife.AbsRotation.Z);
|
||||
Vector offset = SkillUtils.GetForwardVector(new QAngle(rotation.X - 90, rotation.Y, rotation.Z)) * 2;
|
||||
|
|
@ -74,11 +75,11 @@ namespace src.player.skills
|
|||
|
||||
knife.Collision.CollisionAttribute.InteractsWith = knifeInfo.OriginalCollisionMask;
|
||||
Utilities.SetStateChanged(knife, "CCollisionProperty", "m_collisionAttribute");
|
||||
|
||||
|
||||
var world = Utilities.GetEntityFromIndex<CBaseEntity>(0);
|
||||
if (world != null && world.IsValid)
|
||||
knife.AcceptInput("SetParent", world, world, "!activator");
|
||||
|
||||
|
||||
if (knifeInfo.Timer != null)
|
||||
knifeInfo.Timer?.Kill();
|
||||
|
||||
|
|
@ -136,6 +137,12 @@ namespace src.player.skills
|
|||
DisableKnifeSkill(knifeInfo);
|
||||
}
|
||||
|
||||
public static void PlayerDisconnect(uint playerIndex)
|
||||
{
|
||||
if (knivesInfo.TryRemove(playerIndex, out KnifeInfo? knifeInfo) && knifeInfo != null)
|
||||
DisableKnifeSkill(knifeInfo);
|
||||
}
|
||||
|
||||
public static void DisableKnifeSkill(KnifeInfo knifeInfo)
|
||||
{
|
||||
if (knifeInfo.Timer != null)
|
||||
|
|
@ -185,7 +192,8 @@ namespace src.player.skills
|
|||
if (weapon == null || !weapon.IsValid || (!weapon.DesignerName.Contains("knife") && !weapon.DesignerName.Contains("bayonet"))) return;
|
||||
|
||||
player.DropActiveWeapon();
|
||||
Server.NextFrame(() => {
|
||||
Server.NextFrame(() =>
|
||||
{
|
||||
if (player == null || !player.IsValid) return;
|
||||
if (weapon == null || !weapon.IsValid) return;
|
||||
ThrowKnife(player, weapon);
|
||||
|
|
@ -243,10 +251,10 @@ namespace src.player.skills
|
|||
{
|
||||
if (player == null || !player.IsValid) return null;
|
||||
if (knife == null || !knife.IsValid || knife.AbsOrigin == null) return null;
|
||||
|
||||
|
||||
var trigger = SkillUtils.CreateTrigger($"trowingknife", 10, knife.AbsOrigin);
|
||||
if (trigger == null || !trigger.IsValid) return null;
|
||||
|
||||
|
||||
trigger.AcceptInput("SetParent", knife, knife, "!activator");
|
||||
return trigger.Index;
|
||||
}
|
||||
|
|
@ -312,8 +320,14 @@ namespace src.player.skills
|
|||
if (victimPawn.Index == throwerPawn.Index)
|
||||
return;
|
||||
|
||||
var victimController = victimPawn.Controller.Value?.As<CCSPlayerController>();
|
||||
|
||||
bool friendlyFire = SkillsInfo.GetValue<bool>(skillName, "friendlyFire");
|
||||
if (!friendlyFire && thrower.TeamNum == victimPawn.TeamNum) return;
|
||||
if (!friendlyFire)
|
||||
{
|
||||
if (thrower.TeamNum == victimPawn.TeamNum) return;
|
||||
if (victimController != null && victimController.IsValid && thrower.Team == victimController.Team) return;
|
||||
}
|
||||
|
||||
if (CheckHasKnife(thrower!)) return;
|
||||
|
||||
|
|
@ -371,7 +385,7 @@ namespace src.player.skills
|
|||
var obs = playerPawn.ObserverServices?.ObserverTarget?.Value;
|
||||
if (obs != null && obs.IsValid)
|
||||
{
|
||||
var observed = Utilities.GetPlayers().FirstOrDefault(p =>
|
||||
var observed = PlayerManager.GetTickPlayers().FirstOrDefault(p =>
|
||||
p != null && p.IsValid &&
|
||||
p.Pawn?.Value?.Handle == obs.Handle);
|
||||
if (observed != null)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue