Интеграция CS_Script

Как интегрировать систему CS_Script от Valve с плагинами Plugify.

Обзор

Counter-Strike 2 включает CS_Script (систему скриптов на базе JavaScript, которая предоставляет доступ к игровым сущностям, событиям и функциональности. Плагин s2sdk позволяет языковым модулям Plugify интегрироваться с CS_Script, предоставляя доступ к резолверу модулей Valve, что позволяет использовать плагины Plugify и систему CS_Script от Valve одновременно.

Для получения дополнительной информации о системе CS_Script от Valve см.:

Проблема синхронизации

При работе с CS_Script важно учитывать следующий момент:

  • Плагины Plugify загружаются рано во время инициализации сервера
  • Модули CS_Script становятся доступны позже после создания объекта игровых правил
  • s2sdk создаёт сущность point_script когда скриптовая система Valve готова

Это означает, что вы не можете импортировать модули CS_Script в начале файла плагина:

// ❌ Это НЕ будет работать - CS_Script ещё недоступен
import { Instance } from "cs_script/point_script";

Решение: динамические импорты

Вместо этого используйте слушатель OnEntityCreated для определения момента создания сущности point_script, а затем динамически импортируйте модули CS_Script:

// ✅ Это работает - импорт после создания point_script
import("cs_script/point_script").then(point_script => {
    const { Instance } = point_script;
    // Теперь вы можете использовать функциональность CS_Script
});

Полный пример интеграции

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}]`);

            // Небольшая задержка для полной инициализации CS_Script (необязательно)
            setTimeout(() => {
                // Динамический импорт модулей CS_Script
                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!");

                    // Пример: настройка функции think
                    Instance.SetThink(() => {
                        Instance.Msg("Think function executing...");
                        return Instance.GetGameTime() + 5.0; // Запуск каждые 5 секунд
                    });
                    Instance.SetNextThink(Instance.GetGameTime());

                }).catch(error => {
                    console.error("Failed to import CS_Script module:", error);
                });
            }, 100);
        }
    }
}

Определение доступности CS_Script на других языках

Хотя полная интеграция с CS_Script специфична для JavaScript, другие языки могут определять момент, когда CS_Script становится доступным:

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");
            // Теперь вы можете взаимодействовать с сущностью point_script
        }
    }
}

Расширенное использование: гибридные плагины

Вы можете создавать мощные гибридные плагины, сочетающие многоязыковую поддержку Plugify с игровой интеграцией CS_Script:

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

export class HybridPlugin extends Plugin {
    static pulseReady = false;
    static Instance = null;

    pluginStart() {
        // Регистрация хуков с использованием 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 playerSlot = s2sdk.GetEventPlayerSlot(event, "userid");

        // Совместное использование s2sdk и CS_Script
        if (HybridPlugin.pulseReady) {
            HybridPlugin.Instance.Msg(`Player spawned: ${userid}`);
        }

        return s2sdk.ResultType.Continue;
    }
}

Важные замечания

  • Модуль Plugify V8 предоставляет функции в стиле Node.js помимо базового JavaScript. См. официальную документацию модуля V8 для подробностей.
  • Всегда используйте динамические импорты для модулей CS_Script - статические импорты в начале файла не будут работать.
  • Добавьте небольшую задержку (например, 100мс) после обнаружения point_script, чтобы убедиться в полной инициализации CS_Script.
  • Обрабатывайте ошибки импорта корректно, используя .catch() для промисов импорта.
  • Только плагины на JavaScript могут полностью интегрироваться с CS_Script, но другие языки могут определять момент его доступности.
  • Сущность point_script автоматически создаётся s2sdk после создания объекта игровых правил.

Общие паттерны

Ожидание CS_Script перед выполнением кода

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!");
        });
    }
}

Использование нескольких модулей CS_Script

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;
    }
}