codeAPI Reference

PlayerData Component for Stats and Respawn

calendar_today May 3, 2026 schedule ~1 min read person patrickjr verified 50

PlayerData Component

Persistent player data component tracking stats (kills, deaths), respawn state, and providing connection lookup.

Key Properties

CSHARP
[Property] public Guid PlayerId { get; set; }
[Property] public long SteamId { get; set; } = -1L;
[Property] public string DisplayName { get; set; }

[Sync] public int Kills { get; set; }
[Sync] public int Deaths { get; set; }
[Sync] public bool IsGodMode { get; set; }

Connection Lookup

CSHARP
public Connection Connection => Connection.Find(PlayerId);
public bool IsMe => PlayerId == Connection.Local.Id;
public float Ping => Connection?.Ping ?? 0;

/// <summary>
/// Data for all players.
/// </summary>
public static IEnumerable<PlayerData> All => Game.ActiveScene.GetAll<PlayerData>();

/// <summary>
/// Get player data for a connection.
/// </summary>
public static PlayerData For(Connection connection) => 
    connection == null ? default : For(connection.Id);

/// <summary>
/// Get player data for a player ID.
/// </summary>
public static PlayerData For(Guid playerId) => 
    All.FirstOrDefault(x => x.PlayerId == playerId);

Respawn System

CSHARP
private bool _needsRespawn;
private RealTimeSince _timeSinceDied;

/// <summary>
/// Called on host when player dies. Starts respawn countdown.
/// </summary>
public void MarkForRespawn()
{
    _needsRespawn = true;
    _timeSinceDied = 0;
}

/// <summary>
/// Single entry point for all respawn logic. Called by PlayerObserver or timeout.
/// </summary>
[Rpc.Host(NetFlags.OwnerOnly | NetFlags.Reliable)]
public void RequestRespawn()
{
    _needsRespawn = false;

    // Clean up any lingering observer for this connection.
    foreach (var observer in Scene.GetAllComponents<PlayerObserver>()
        .Where(x => x.Network.Owner?.Id == PlayerId).ToArray())
    {
        observer.GameObject.Destroy();
    }

    GameManager.Current?.SpawnPlayer(this);
}

protected override void OnUpdate()
{
    if (!Networking.IsHost) return;
    if (!_needsRespawn) return;
    if (_timeSinceDied < 4f) return;

    RequestRespawn(); // Auto-respawn after 4 seconds
}

Stats System

CSHARP
/// <summary>
/// Called on host, calls RPC on player to increment stats.
/// </summary>
public void AddStat(string identifier, int amount = 1)
{
    if (Application.CheatsEnabled) return;
    Assert.True(Networking.IsHost, "PlayerData.AddStat is host-only!");

    using (Rpc.FilterInclude(Connection))
    {
        RpcAddStat(identifier, amount);
    }
}

[Rpc.Broadcast]
private void RpcAddStat(string identifier, int amount = 1)
{
    Sandbox.Services.Stats.Increment(identifier, amount);
}

Save/Load Events

CSHARP
void Global.ISaveEvents.AfterLoad(string filename)
{
    var connection = Connection;
    if (connection == null)
    {
        // Get new PlayerId from SteamId if this is a new session
        PlayerId = Connection.All.FirstOrDefault(x => x.SteamId == SteamId)?.Id ?? Guid.Empty;
    }
}

Usage Examples

CSHARP
// Get all player data ordered by kills
var topPlayers = PlayerData.All.OrderByDescending(x => x.Kills);

// Find player data for connection
var data = PlayerData.For(connection);

// Check if this is local player
if (data.IsMe) { /* ... */ }

// Get ping
var ping = data.Ping;

// Add stats (host only)
data.AddStat("deaths");
data.AddStat("kills.players", 1);
Was this helpful?