Entity Schemas

How to read and modify Source 2 entity schemas and network state.

Understanding Schemas

In Source 1, entities had two types of properties: send props (synchronized with clients) and data props (server-only). Source 2 simplified this by merging them into a unified schema system. Schemas define the structure and properties of all game objects, and many schema fields are networked automatically.

Schemas allow you to access and modify properties of any entity or object in Source 2, including health, position, velocity, model, team, and countless other attributes. When you modify networked schema fields with changeState set to true, the changes are automatically synchronized to clients.

Finding Schema Information

You can find schema class names and member names using:

  • Online dumps: cs2-dumper repository
  • Schema dumpers: Various community tools that extract schema definitions
  • Game resource files: Located in your game's schema folders

Common schema classes include CBaseEntity, CBasePlayerPawn, CCSPlayerController, CBaseModelEntity, and many more.

Schema Methods vs Data Methods

The s2sdk provides two approaches for working with schemas:

Use class and member names as strings. These are easier to use and more readable:

SetEntSchema(entityHandle, "CBaseEntity", "m_iHealth", 200, true, 0);
int health = GetEntSchema(entityHandle, "CBaseEntity", "m_iHealth", 0);

Data Methods (Advanced)

Use raw memory offsets. These are faster but require manual offset management:

int offset = GetSchemaOffset("CBaseEntity", "m_iHealth");
SetEntData(entityHandle, offset, 200, 4, true, 0);
int health = GetEntData(entityHandle, offset, 4);

Entity Handles vs Pointers

Most functions come in two versions:

  • Handle version (e.g., GetEntSchema): Works with entity handles - use this whenever possible
  • Pointer version (e.g., GetEntSchema2): Works with raw pointers - only use when handles cannot be generated (some schemas are not entity-based)

Always prefer entity handles unless you're working with non-entity schema objects.

Basic Example: Modifying Entity Health

Here's a simple example of reading and modifying an entity's health:

c#
c++
python
go
js
lua
using Plugify;
using static s2sdk.s2sdk;

public unsafe class Sample : Plugin
{
    public void OnPluginStart()
    {
        AddConsoleCommand("set_health", "Sets player health to 200", 
            ConVarFlag.LinkedConcommand | ConVarFlag.Release, 
            (caller, context, arguments) =>
            {
                if (caller == -1) return ResultType.Handled;
                
                // Convert player's slot to entity's handle
                int player = PlayerSlotToEntHandle(caller);
                
                // Set the caller's health to 200
                SetEntSchema(player, "CBaseEntity", "m_iHealth", 200, true, 0);
                
                // Read back the health
                int health = (int)GetEntSchema(player, "CBaseEntity", "m_iHealth", 0);
                PrintToServer($"Player health set to {health}\n");
                
                return ResultType.Handled;
            }, HookMode.Post);
    }
}

Working with Different Data Types

The schema system supports multiple data types. Here are examples for each:

Integers

// Get/Set integer values (health, armor, money, etc.)
SetEntSchema(entityHandle, "CBaseEntity", "m_iHealth", 100, true, 0);
int health = (int)GetEntSchema(entityHandle, "CBaseEntity", "m_iHealth", 0);

Floats

// Get/Set float values (speed, gravity, etc.)
SetEntSchemaFloat(entityHandle, "CCSPlayerPawn", "m_flGravityScale", 0.5, true, 0);
double gravity = GetEntSchemaFloat(entityHandle, "CCSPlayerPawn", "m_flGravityScale", 0);

Strings

// Get/Set string values (names, models, etc.)
SetEntSchemaString(entityHandle, "CBaseModelEntity", "m_ModelName", "models/player.mdl", true, 0);
string modelName = GetEntSchemaString(entityHandle, "CBaseModelEntity", "m_ModelName", 0);

Vectors

// Get/Set 3D vectors (position, velocity, angles, etc.)
using System.Numerics;

Vector3 newPos = new Vector3(100.0f, 200.0f, 300.0f);
SetEntSchemaVector3D(entityHandle, "CBaseEntity", "m_vecAbsOrigin", newPos, true, 0);
Vector3 position = GetEntSchemaVector3D(entityHandle, "CBaseEntity", "m_vecAbsOrigin", 0);

Entity Handles

// Get/Set entity references (owner, parent, weapon, etc.)
SetEntSchemaEnt(entityHandle, "CBaseEntity", "m_hOwnerEntity", ownerHandle, true, 0);
int owner = GetEntSchemaEnt(entityHandle, "CBaseEntity", "m_hOwnerEntity", 0);

Working with Arrays

Some schema fields are arrays. Use the element parameter to access specific array indices:

// Get array size
int arraySize = GetEntSchemaArraySize(entityHandle, "SomeClass", "m_arrayField");

// Access array elements (element parameter is the last one before type-specific defaults)
for (int i = 0; i < arraySize; i++)
{
    int value = (int)GetEntSchema(entityHandle, "SomeClass", "m_arrayField", i);
    PrintToServer($"Array[{i}] = {value}\n");
}

// Set array element
SetEntSchema(entityHandle, "SomeClass", "m_arrayField", 42, true, 2); // Set index 2 to 42

Understanding Parameters

changeState (bool)

When true, marks the field as changed and triggers network synchronization to clients. Set to true when modifying networked fields, false for server-only changes.

element (int)

The array index when working with array schema fields. Use 0 for non-array fields.

chainOffset (int)

Advanced parameter for chain offsets in inheritance hierarchies. Use 0 for most cases, or use GetSchemaChainOffset() for specific inheritance chains. Use -1 for non-entity classes.

Practical Examples

Example 1: God Mode Toggle

c#
using Plugify;
using static s2sdk.s2sdk;

public unsafe class GodMode : Plugin
{
    private bool[] godMode = new bool[65]; // Max players
    
    public void OnPluginStart()
    {
        AddConsoleCommand("god", "Toggle god mode", 
            ConVarFlag.LinkedConcommand | ConVarFlag.Release,
            Command_God, HookMode.Post);
    }
    
    public ResultType Command_God(int caller, int context, string[] arguments)
    {
        if (caller == -1) return ResultType.Handled;
        
        godMode[caller] = !godMode[caller];
        
        int player = PlayerSlotToEntHandle(caller);
        
        if (godMode[caller])
        {
            SetEntSchema(player, "CBaseEntity", "m_iHealth", 999, true, 0);
            SetEntSchema(player, "CBaseEntity", "m_takedamage", 0, true, 0); // DAMAGE_NO
            PrintToServer("God mode enabled\n");
        }
        else
        {
            SetEntSchema(player, "CBaseEntity", "m_iHealth", 100, true, 0);
            SetEntSchema(player, "CBaseEntity", "m_takedamage", 2, true, 0); // DAMAGE_YES
            PrintToServer("God mode disabled\n");
        }
        
        return ResultType.Handled;
    }
}

Example 2: Teleportation

c#
using System.Numerics;
using Plugify;
using static s2sdk.s2sdk;

public unsafe class Teleport : Plugin
{
    public void OnPluginStart()
    {
        AddConsoleCommand("teleport", "Teleport to coordinates", 
            ConVarFlag.LinkedConcommand | ConVarFlag.Release,
            Command_Teleport, HookMode.Post);
    }
    
    public ResultType Command_Teleport(int caller, int context, string[] arguments)
    {
        if (caller == -1) return ResultType.Handled;
        if (arguments.Length < 4)
        {
            PrintToServer("Usage: teleport <x> <y> <z>\n");
            return ResultType.Handled;
        }
        
        int player = PlayerSlotToEntHandle(caller);
        
        float x = float.Parse(arguments[1]);
        float y = float.Parse(arguments[2]);
        float z = float.Parse(arguments[3]);
        
        Vector3 newPosition = new Vector3(x, y, z);
        SetEntSchemaVector3D(player, "CBaseEntity", "m_vecAbsOrigin", newPosition, true, 0);
        
        // Also reset velocity to prevent momentum
        Vector3 zeroVelocity = new Vector3(0, 0, 0);
        SetEntSchemaVector3D(player, "CBaseEntity", "m_vecAbsVelocity", zeroVelocity, true, 0);
        
        PrintToServer($"Teleported to ({x}, {y}, {z})\n");
        
        return ResultType.Handled;
    }
}

Example 3: Change Player Model

c#
using Plugify;
using static s2sdk.s2sdk;

public unsafe class ModelChanger : Plugin
{
    public void OnPluginStart()
    {
        AddConsoleCommand("setmodel", "Change player model", 
            ConVarFlag.LinkedConcommand | ConVarFlag.Release,
            Command_SetModel, HookMode.Post);
    }
    
    public ResultType Command_SetModel(int caller, int context, string[] arguments)
    {
        if (caller == -1) return ResultType.Handled;
        if (arguments.Length < 2)
        {
            PrintToServer("Usage: setmodel <model_path>\n");
            return ResultType.Handled;
        }
        
        int player = PlayerSlotToEntHandle(caller);
        
        string modelPath = arguments[1];
        SetEntSchemaString(player, "CBaseModelEntity", "m_ModelName", modelPath, true, 0);
        
        PrintToServer($"Model changed to: {modelPath}\n");
        
        return ResultType.Handled;
    }
}

Example 4: Speed Modifier

c#
using Plugify;
using static s2sdk.s2sdk;

public unsafe class SpeedMod : Plugin
{
    public void OnPluginStart()
    {
        AddConsoleCommand("speed", "Change player speed", 
            ConVarFlag.LinkedConcommand | ConVarFlag.Release,
            Command_Speed, HookMode.Post);
    }
    
    public ResultType Command_Speed(int caller, int context, string[] arguments)
    {
        if (caller == -1) return ResultType.Handled;
        if (arguments.Length < 2)
        {
            PrintToServer("Usage: speed <multiplier> (1.0 = normal)\n");
            return ResultType.Handled;
        }
        
        int player = PlayerSlotToEntHandle(caller);
        
        // Get pawn entity from player
        int entityHandle = GetEntSchemaEnt(player, "CCSPlayerController", "m_hPlayerPawn");
        
        float speedMultiplier = float.Parse(arguments[1]);
        
        // Set speed on player pawn
        SetEntSchemaFloat(entityHandle, "CCSPlayerPawn", "m_flVelocityModifier", speedMultiplier, true, 0);
        
        PrintToServer($"Speed set to {speedMultiplier}x\n");
        
        return ResultType.Handled;
    }
}

Advanced: Using Raw Offsets

For performance-critical code, you can use raw offsets:

// Get offset once and cache it
int healthOffset = GetSchemaOffset("CBaseEntity", "m_iHealth");
int chainOffset = GetSchemaChainOffset("CBaseEntity");

// Use offset directly for better performance
SetEntData(entityHandle, healthOffset, 100, 4, true, chainOffset);
int health = (int)GetEntData(entityHandle, healthOffset, 4);

Using Pointer Versions

For non-entity schema objects (when handles aren't available):

// Example: Working with a non-entity schema object
nint objectPointer = GetSomeNonEntityPointer();

// Use the '2' version of functions
SetEntSchema2(objectPointer, "SomeClass", "m_someField", 42, true, 0);
int value = (int)GetEntSchema2(objectPointer, "SomeClass", "m_someField", 0);

Network State Updates

To manually trigger network updates:

// Update specific field
NetworkStateChanged(entityHandle, "CBaseEntity", "m_iHealth");

// Or using offset
int offset = GetSchemaOffset("CBaseEntity", "m_iHealth");
int chainOffset = GetSchemaChainOffset("CBaseEntity");
ChangeEntityState(entityHandle, offset, chainOffset);

Tips and Best Practices

  1. Use schema methods over data methods - They're more readable and maintainable
  2. Prefer entity handles over pointers - Only use pointer versions when necessary
  3. Cache offsets in performance-critical code - Avoid repeated GetSchemaOffset() calls
  4. Always set changeState appropriately - Use true for networked changes, false for server-only
  5. Refer to schema dumps - Check s2d-schema for available fields
  6. Validate entity handles - Ensure entities are valid before accessing schemas
  7. Use appropriate data types - Match the function to the schema field type (Int, Float, String, Vector, etc.)
  8. Test with clients - Verify that networked changes synchronize correctly

Common Schema Fields

Here are some frequently used schema fields:

CBaseEntity:

  • m_iHealth - Entity health
  • m_iMaxHealth - Maximum health
  • m_vecAbsOrigin - Position
  • m_angAbsRotation - Rotation angles
  • m_vecAbsVelocity - Velocity
  • m_hOwnerEntity - Owner entity handle
  • m_takedamage - Damage behavior (0=no, 2=yes)

CBasePlayerPawn:

  • m_iMaxHealth - Max health
  • m_ArmorValue - Armor amount
  • m_vecViewOffset - View height offset

CCSPlayerPawn:

  • m_flGravityScale - Gravity multiplier
  • m_flVelocityModifier - Speed multiplier
  • m_bIsScoped - Is player scoped

CCSPlayerController:

  • m_sSanitizedPlayerName - Player name
  • m_iPing - Player ping
  • m_iScore - Player score
  • m_hPlayerPawn - Player pawn handle

CBaseModelEntity:

  • m_ModelName - Model path
  • m_nSkin - Model skin
  • m_clrRender - Render color

Refer to schema dumps for the complete list of available classes and fields.