NetworkPawn.cs
using Sandbox;
using System;
namespace PaintballBaddies;

/// <summary>Owner movement/presentation state; host validates combat requests.</summary>
public sealed class NetworkPawn : Component
{
    [Property,Sync(SyncFlags.FromHost)] public int Seat { get; set; }
    [Property,Sync(SyncFlags.FromHost)] public Guid ConnectionId { get; set; }
    [Sync] public bool Ducking { get; set; }
    [Sync] public bool Grounded { get; set; }
    [Sync] public bool Covered { get; set; }
    [Sync] public bool Low { get; set; }
    [Sync] public bool Peek { get; set; }
    [Sync] public bool Sliding { get; set; }
    [Sync] public Vector3 SlideDirection { get; set; }
    [Sync] public Vector3 CoverNormal { get; set; }
    [Sync] public string CoverName { get; set; }
    [Sync] public bool Vaulting { get; set; }
    [Sync] public float VaultProgress { get; set; }
    [Sync] public Rotation VaultFacing { get; set; }
    [Sync] public bool Aiming { get; set; }
    [Sync] public Vector3 Motion { get; set; }
    [Sync] public Angles Look { get; set; }
    [Sync(SyncFlags.FromHost)] public int AcceptedShots { get; private set; }
    [Sync(SyncFlags.FromHost)] public int RejectedShots { get; private set; }
    [Sync(SyncFlags.FromHost)] public string ContactState { get; private set; }="";
    private PlayerController player;
    private float sinceShot=10,stateTime;
    public int RecoveryRequests {get;private set;}
    public int RecoveryAccepted {get;private set;}
    private int lastSequence;
    private bool requestedPaint;
    protected override void OnStart()
    {
        player=Components.Get<PlayerController>();
        if(IsProxy)player.UseInputControls=player.UseLookControls=player.UseCameraControls=false;
        if(MultiplayerSession.Authority)
        {
            var actor=Components.Get<PaintballCombatant>();actor.Team=Seat+10;actor.StayInMatch=true;
        }
        Components.Get<RosterSelection>()?.SelectNetwork(Seat);
    }
    protected override void OnUpdate()
    {
        sinceShot+=Time.Delta;
        if(!IsProxy && !requestedPaint && sinceShot>11){requestedPaint=true;RequestPaint();}
        if(!player.IsValid())return;
        if(IsProxy)
        {
            player.UseInputControls=player.UseLookControls=player.UseCameraControls=false;
            player.EyeAngles=Look;player.UpdateDucking(Ducking);
            Components.Get<CoverController>()?.ApplyNetwork(this);
            Components.Get<VaultController>()?.ApplyNetwork(this);
        }
        else
        {
            var cover=Components.Get<CoverController>();var vault=Components.Get<VaultController>();
            Covered=cover?.Attached==true;Low=cover?.LowCover==true;Peek=cover?.Peeking==true;
            Sliding=cover?.Sliding==true;SlideDirection=cover?.SlideDirection ?? Vector3.Zero;
            CoverNormal=cover?.Normal ?? Vector3.Zero;CoverName=cover?.ReservedSurface?.GameObject.Name ?? "";
            Vaulting=vault?.IsVaulting==true;VaultProgress=vault?.Progress ?? 0;VaultFacing=vault?.Facing ?? Rotation.Identity;
            Ducking=player.IsDucking;Grounded=player.IsOnGround;
            Aiming=Components.Get<PaintballMarker>()?.Aiming==true;Motion=player.Velocity;Look=player.EyeAngles;
        }
        if(MultiplayerSession.Authority)
        {
            stateTime-=Time.Delta;
            if(stateTime<=0){stateTime=.1f;ContactState=Components.Get<CloseCombat>()?.CaptureNetworkState() ?? "";}
        }
        else Components.Get<CloseCombat>()?.ApplyNetworkState(ContactState);
    }
    [Rpc.Host(NetFlags.OwnerOnly)]
    public async void RequestPaint()
    {
        var caller=Rpc.Caller?.Id;
        await GameTask.MainThread();
        if(!this.IsValid() || caller!=ConnectionId)return;
        var session=MultiplayerSession.Find(Scene);ReceivePaint(session.PaintSnapshot());
        if(session.Status=="TIME UP")EndRound();
    }
    [Rpc.Owner(NetFlags.HostOnly)]
    public async void ReceivePaint(string json)
    {
        await GameTask.MainThread();
        if(!this.IsValid())return;
        MultiplayerSession.Find(Scene)?.ReplayPaint(json);
    }
    [Rpc.Host(NetFlags.OwnerOnly)]
    public async void RequestShot(Vector3 origin,Vector3 aim,int sequence)
    {
        // Delayed RPCs can resume on a worker thread. Preserve caller identity
        // before yielding, then validate and touch engine objects on its main thread.
        var caller=Rpc.Caller?.Id;
        await GameTask.MainThread();
        if(!this.IsValid())return;
        var session=MultiplayerSession.Find(Scene);var state=Components.Get<PaintballCombatant>();
        bool finite=float.IsFinite(origin.x)&&float.IsFinite(origin.y)&&float.IsFinite(origin.z)
            &&float.IsFinite(aim.x)&&float.IsFinite(aim.y)&&float.IsFinite(aim.z);
        if(caller!=ConnectionId || !finite || sequence<=lastSequence || sinceShot<PaintballFlight.ShotInterval-.015f
            || !state.AcceptHits || (!session.InRound && session.Status!="LOBBY") || Components.Get<CloseCombat>()?.Incapacitated==true
            || Components.Get<CloseCombat>()?.Attacking==true || Covered&&!Peek || Vaulting || Sliding
            || (origin-WorldPosition).Length>115 || (aim-origin).Length<1)
        {RejectedShots++;return;}
        lastSequence=sequence;sinceShot=0;AcceptedShots++;
        var obstruction=PaintballFlight.Trace(Scene,player.EyePosition,origin,GameObject);
        var tint=CharacterPaintColor.For(GameObject);
        NetworkEffects.Sound(CharacterMarkerSound.For(Seat),origin,ConnectionId);
        ArenaOpponent.ReportMarkerSound(Scene,GameObject,origin);
        if(obstruction.Hit){PaintImpactSystem.Find(Scene)?.Spawn(obstruction,tint);return;}
        var target=origin+(aim-origin).Normal*MathF.Min((aim-origin).Length,PaintballFlight.Speed*PaintballFlight.Lifetime);
        NetworkEffects.SpawnBall(GameObject,state.Team,origin,PaintballFlight.LaunchVelocity(origin,target));
    }
    [Rpc.Host(NetFlags.OwnerOnly)]
    public async void RequestElbow()
    {
        var caller=Rpc.Caller?.Id;
        await GameTask.MainThread();
        if(!this.IsValid())return;
        if(caller==ConnectionId && Components.Get<PaintballCombatant>().AcceptHits)Components.Get<CloseCombat>()?.TryAttackAuthoritative();
    }
    [Rpc.Host(NetFlags.OwnerOnly)]
    public async void RequestRecovery()
    {
        var caller=Rpc.Caller?.Id;
        await GameTask.MainThread();
        if(!this.IsValid())return;
        if(caller==ConnectionId){RecoveryRequests++;if(Components.Get<CloseCombat>()?.RegisterRecoveryAuthoritative()==true)RecoveryAccepted++;}
    }
    [Rpc.Owner(NetFlags.HostOnly)]
    public async void ConfirmHit()
    {
        await GameTask.MainThread();
        if(!this.IsValid())return;
        Components.Get<PaintballMarker>()?.NetworkHit();
    }
    [Rpc.Owner(NetFlags.HostOnly)]
    public async void ResetForRound(Vector3 position,int round)
    {
        await GameTask.MainThread();
        if(!this.IsValid())return;
        Components.Get<CoverController>()?.Leave();Components.Get<VaultController>()?.Cancel();Components.Get<CloseCombat>()?.ResetState();
        player ??= Components.Get<PlayerController>();
        player.WorldPosition=position;player.Body.MotionEnabled=true;player.Body.Sleeping=false;player.Body.Velocity=Vector3.Zero;player.WishVelocity=Vector3.Zero;
        player.UseInputControls=player.UseLookControls=player.UseCameraControls=true;
        Components.Get<PaintballMarker>()?.ResetTraining();Components.Get<PaintballMarker>().AcceptInput=true;
        Network.ClearInterpolation();
    }
    [Rpc.Owner(NetFlags.HostOnly)]
    public async void EndRound()
    {
        await GameTask.MainThread();
        if(!this.IsValid())return;
        Components.Get<CoverController>()?.Leave();Components.Get<VaultController>()?.Cancel();Components.Get<CloseCombat>()?.ResetState();
        Components.Get<PaintballMarker>()?.EndRound();
        player.UseInputControls=false;player.WishVelocity=Vector3.Zero;
        if(player.Body.IsValid())player.Body.Velocity=Vector3.Zero;
        var match=Components.Get<ArenaMatch>();
        bool won=match?.Standings.FirstOrDefault()?.IsPlayer==true;
        Sound.Play(won ? "sounds/paintball/round_win.sound" : "sounds/paintball/round_loss.sound",WorldPosition);
    }
}