CS_Script Integration

How to integrate Valve's CS_Script system with Plugify plugins.

Overview

Counter-Strike 2 includes CS_Script (a JavaScript-based scripting system that provides access to game entities, events, and functionality. The s2sdk plugin enables Plugify language modules to integrate with CS_Script by exposing Valve's module resolver, allowing you to use both Plugify plugins and Valve's CS_Script system together.

For more information about Valve's CS_Script system, see:

The Timing Challenge

There's an important timing consideration when working with CS_Script:

  • Plugify plugins load early during server initialization
  • CS_Script modules become available later after the game rules object is created
  • s2sdk creates a point_script entity once Valve's scripting system is ready

This means you cannot import CS_Script modules at the top of your plugin file:

// ❌ This will NOT work - CS_Script isn't available yet
import { Instance } from "cs_script/point_script";

The Solution: Dynamic Imports

Instead, use the OnEntityCreated listener to detect when the point_script entity is created, then dynamically import CS_Script modules:

// ✅ This works - import after point_script is created
import("cs_script/point_script").then(point_script => {
    const { Instance } = point_script;
    // Now you can use CS_Script functionality
});

Complete Integration Example

js
import { Plugin } from 'plugify';
import * as s2sdk from ':s2sdk';

export class SamplePlugin extends Plugin {
    pluginStart() {
        console.log("Plugin started - waiting for CS_Script...");
        s2sdk.OnEntityCreated_Register(SamplePlugin.OnEntityCreated);
    }
    
    pluginEnd() {
        s2sdk.OnEntityCreated_Unregister(SamplePlugin.OnEntityCreated);
    }
    
    static OnEntityCreated(entityHandle) {
        const className = s2sdk.GetEntityClassname(entityHandle);
        
        if (className === "point_script") {
            console.log(`point_script entity created [handle: ${entityHandle}]`);
            
            // Small delay to ensure CS_Script is fully initialized (Optional)
            setTimeout(() => {
                // Dynamically import CS_Script modules
                import("cs_script/point_script").then(point_script => {
                    const { Instance } = point_script;
                    
                    console.log("✓ CS_Script system connected!");
                    Instance.Msg("✓ CS_Script system connected from Plugify!");
                    
                    // Example: Set up a think function
                    Instance.SetThink(() => {
                        Instance.Msg("Think function executing...");
                        return Instance.GetGameTime() + 5.0; // Run every 5 seconds
                    });
                    Instance.SetNextThink(Instance.GetGameTime());
                    
                }).catch(error => {
                    console.error("Failed to import CS_Script module:", error);
                });
            }, 100);
        }
    }
}

Detecting CS_Script Availability in Other Languages

While full CS_Script integration is JavaScript-specific, other languages can detect when CS_Script becomes available:

c#
python
c++
using Plugify;
using static s2sdk.s2sdk;

public unsafe class Sample : Plugin
{
    public void OnPluginStart()
    {
        OnEntityCreated_Register(OnEntityCreatedCallback);
    }
    
    public void OnPluginEnd()
    {
        OnEntityCreated_Unregister(OnEntityCreatedCallback);
    }
    
    private static void OnEntityCreatedCallback(int entityHandle)
    {
        string className = GetEntityClassname(entityHandle);
        
        if (className == "point_script")
        {
            PrintToServer("CS_Script system is now available!\n");
            // You can now interact with the point_script entity
        }
    }
}

Advanced Usage: Hybrid Plugins

You can create powerful hybrid plugins that combine Plugify's multi-language support with CS_Script's game integration:

import { Plugin } from 'plugify';
import * as s2sdk from ':s2sdk';

export class HybridPlugin extends Plugin {
    static pulseReady = false;
    static Instance = null;
    
    pluginStart() {
        // Register hooks using s2sdk
        s2sdk.OnEntityCreated_Register(HybridPlugin.OnEntityCreated);
        s2sdk.HookEvent("player_spawn", HybridPlugin.OnPlayerSpawn, s2sdk.HookMode.Post);
    }
    
    static OnEntityCreated(entityHandle) {
        const className = s2sdk.GetEntityClassname(entityHandle);
        
        if (className === "point_script") {
            setTimeout(() => {
                import("cs_script/point_script").then(point_script => {
                    HybridPlugin.Instance = point_script.Instance;
                    HybridPlugin.pulseReady = true;
                    console.log("✓ Hybrid mode active!");
                });
            }, 100);
        }
    }
    
    static OnPlayerSpawn(name, event, dontBroadcast) {
        const userid = s2sdk.GetEventInt(event, "userid");
        
        // Use both s2sdk and CS_Script together
        if (HybridPlugin.pulseReady) {
            HybridPlugin.Instance.Msg(`Player spawned: ${userid}`);
        }
        
        return s2sdk.ResultType.Continue;
    }
}

Important Notes

  • The Plugify V8 module provides Node.js-like features beyond basic JavaScript. See the official V8 module documentation for details.
  • Always use dynamic imports for CS_Script modules - static imports at the top of the file will fail.
  • Add a small timeout (e.g., 100ms) after detecting point_script to ensure CS_Script is fully initialized.
  • Handle import errors gracefully using .catch() on your import promises.
  • Only JavaScript plugins can fully integrate with CS_Script, but other languages can detect when it becomes available.
  • The point_script entity is automatically created by s2sdk once the game rules object exists.

Common Patterns

Waiting for CS_Script Before Executing Code

export class MyPlugin extends Plugin {
    static waitForPulse(callback) {
        const checkPulse = () => {
            import("cs_script/point_script")
                .then(point_script => callback(point_script.Instance))
                .catch(() => setTimeout(checkPulse, 100));
        };
        checkPulse();
    }
    
    pluginStart() {
        MyPlugin.waitForPulse((Instance) => {
            console.log("CS_Script ready, executing plugin logic...");
            Instance.Msg("Plugin initialized!");
        });
    }
}

Using Multiple CS_Script Modules

static async loadPulseModules() {
    try {
        const [pointScript, entities, events] = await Promise.all([
            import("cs_script/point_script"),
            import("cs_script/entities"),
            import("cs_script/events")
        ]);
        
        return {
            Instance: pointScript.Instance,
            entities: entities,
            events: events
        };
    } catch (error) {
        console.error("Failed to load CS_Script modules:", error);
        return null;
    }
}