openvibers/source.openvibe.games Commits
Stub Windows server QuickJS runtime for DLL build
Changes
nnew file mode 100644@@@ -0,0 +1,18 @@++# Windows server QuickJS stub build++++The Windows GitHub Actions build is currently used to produce patched Windows++`client.dll` and `server.dll` artifacts for Proton/runtime validation.++++The vendored QuickJS C runtime builds as a static library with `clang-cl`, but++including QuickJS's C-facing headers from MSVC C++ inside the Source SDK server++project still trips C++ parser issues around C compound literals and designated++initializers.++++To keep the Windows DLL pipeline moving, the server build uses a Windows-only++`GAME_DLL` stub for the OpenVibe JS runtime. The stub preserves the public++OpenVibe server concommands and strings (`ov_js_status`, `ov_js_cmd`,++`ov_js_reload`, `ov_js_fire`, `OpenVibe`) so the produced DLL is patched and++usable for smoke tests. Native Linux builds can still use the real QuickJS path.++++Define `OPENVIBE_WINDOWS_SERVER_REAL_QUICKJS` later when the QuickJS C++ header++compatibility work is complete.@@@ -1,3 +1,87 @@++// OPENVIBE_WINDOWS_SERVER_QUICKJS_STUB++#if defined(_WIN32) && defined(GAME_DLL) && !defined(OPENVIBE_WINDOWS_SERVER_REAL_QUICKJS)++++#include "cbase.h"++#include "hl2mp_player.h"++#include "openvibe_js_server.h"++++#include "tier0/memdbgon.h"++++static ConVar ov_mode(++ "ov_mode",++ "hub",++ FCVAR_GAMEDLL,++ "OpenVibe mode: hub, prophunt, deathrun, fortwars, traitortown." );++++static ConVar ov_js_enabled(++ "ov_js_enabled",++ "0",++ FCVAR_GAMEDLL,++ "Enable OpenVibe JavaScript runtime. Windows server CI uses a stub runtime." );++++void OpenVibeJS_ServerInit()++{++ Msg("[OV JS] Windows server CI stub active; runtime disabled, mode=%s\n", ov_mode.GetString());++}++++void OpenVibeJS_ServerShutdown() {}++void OpenVibeJS_ServerThink() {}++void OpenVibeJS_Server_PlayerInitialSpawn(CHL2MP_Player *player) {}++void OpenVibeJS_Server_PlayerSpawn(CHL2MP_Player *player) {}++void OpenVibeJS_Server_PlayerDeath(CHL2MP_Player *victim, CBaseEntity *attacker, CBaseEntity *inflictor) {}++void OpenVibeJS_Server_PlayerDisconnected(CHL2MP_Player *player) {}++bool OpenVibeJS_Server_PlayerSay(CHL2MP_Player *player, const char *text) { return false; }++++static void OV_JSReload_f()++{++ Msg("[OV JS] Windows server CI stub: ov_js_reload ignored, mode=%s\n", ov_mode.GetString());++}++++static ConCommand ov_js_reload(++ "ov_js_reload",++ OV_JSReload_f,++ "Reload OpenVibe JavaScript runtime.",++ FCVAR_GAMEDLL++);++++static void OV_JSStatus_f()++{++ Msg("[OV JS] enabled=%d started=0 running=0 mode=%s backend=windows-server-stub\n",++ ov_js_enabled.GetBool() ? 1 : 0,++ ov_mode.GetString() );++}++++static ConCommand ov_js_status(++ "ov_js_status",++ OV_JSStatus_f,++ "Print OpenVibe JavaScript runtime status.",++ FCVAR_GAMEDLL++);++++static void OV_JSFire_f(const CCommand &args)++{++ Msg("[OV JS] Windows server CI stub: ov_js_fire ignored. Usage: ov_js_fire <HookName>\n");++}++++static ConCommand ov_js_fire(++ "ov_js_fire",++ OV_JSFire_f,++ "Fire an OpenVibe JavaScript hook with no arguments.",++ FCVAR_GAMEDLL++);++++static void OV_JSCmd_f(const CCommand &args)++{++ Msg("[OV JS] Windows server CI stub: ov_js_cmd %s\n", args.ArgS());++}++++static ConCommand ov_js_cmd(++ "ov_js_cmd",++ OV_JSCmd_f,++ "Send a ConsoleCommand event into the embedded OpenVibe JavaScript runtime.",++ FCVAR_GAMEDLL++);++#else#include "cbase.h"#include "hl2mp_player.h"#include "openvibe_js_server.h"@@@ -265,3 +349,4 @@ static ConCommand ov_js_cmd("Send a ConsoleCommand event into the embedded OpenVibe JavaScript runtime.",FCVAR_GAMEDLL);++#endif@@@ -1,3 +1,13 @@++// OPENVIBE_WINDOWS_SERVER_QUICKJS_STUB++#if defined(_WIN32) && defined(GAME_DLL) && !defined(OPENVIBE_WINDOWS_SERVER_REAL_QUICKJS)++++#include "cbase.h"++#include "ov_js_bindings.h"++++#include "tier0/memdbgon.h"++++void OVJS_RegisterNativeBindings(JSContext *ctx, COpenVibeJSRuntime *runtime) {}++#else#include "cbase.h"#include "ov_js_bindings.h"#include "ov_js_runtime.h"@@@ -168,3 +178,4 @@ void OVJS_RegisterNativeBindings(JSContext *ctx, COpenVibeJSRuntime *runtime)JS_FreeValue(ctx, global);}++#endif@@@ -1,7 +1,15 @@#pragma once++// OPENVIBE_WINDOWS_SERVER_QUICKJS_STUB++#if defined(_WIN32) && defined(GAME_DLL) && !defined(OPENVIBE_WINDOWS_SERVER_REAL_QUICKJS)++++typedef void JSContext;++class COpenVibeJSRuntime;++void OVJS_RegisterNativeBindings(JSContext *ctx, COpenVibeJSRuntime *runtime);++#else#include "openvibe/third_party/quickjs/quickjs.h"class COpenVibeJSRuntime;void OVJS_RegisterNativeBindings(JSContext *ctx, COpenVibeJSRuntime *runtime);++#endif@@@ -1,3 +1,17 @@++// OPENVIBE_WINDOWS_SERVER_QUICKJS_STUB++#if defined(_WIN32) && defined(GAME_DLL) && !defined(OPENVIBE_WINDOWS_SERVER_REAL_QUICKJS)++++#include "cbase.h"++#include "ov_js_player.h"++#include "hl2mp_player.h"++++#include "tier0/memdbgon.h"++++void OVJS_RegisterPlayerClass(JSContext *ctx) {}++JSValue OVJS_NewPlayer(JSContext *ctx, CHL2MP_Player *player) { return JS_NULL; }++CHL2MP_Player *OVJS_ResolvePlayerByUserId(int userId) { return nullptr; }++CHL2MP_Player *OVJS_GetPlayerFromThis(JSContext *ctx, JSValueConst thisVal) { return nullptr; }++#else#include "cbase.h"#include "hl2mp_player.h"#include "player.h"@@@ -208,3 +222,4 @@ JSValue OVJS_NewPlayer(JSContext *ctx, CHL2MP_Player *player)JS_SetOpaque(obj, handle);return obj;}++#endif@@@ -1,5 +1,15 @@#pragma once++// OPENVIBE_WINDOWS_SERVER_QUICKJS_STUB++#if defined(_WIN32) && defined(GAME_DLL) && !defined(OPENVIBE_WINDOWS_SERVER_REAL_QUICKJS)++++#include "openvibe/ov_js_runtime.h"++class CHL2MP_Player;++void OVJS_RegisterPlayerClass(JSContext *ctx);++JSValue OVJS_NewPlayer(JSContext *ctx, CHL2MP_Player *player);++CHL2MP_Player *OVJS_ResolvePlayerByUserId(int userId);++CHL2MP_Player *OVJS_GetPlayerFromThis(JSContext *ctx, JSValueConst thisVal);++#else#include "openvibe/third_party/quickjs/quickjs.h"class CHL2MP_Player;@@@ -8,3 +18,4 @@ void OVJS_RegisterPlayerClass(JSContext *ctx);JSValue OVJS_NewPlayer(JSContext *ctx, CHL2MP_Player *player);CHL2MP_Player *OVJS_ResolvePlayerByUserId(int userId);CHL2MP_Player *OVJS_GetPlayerFromThis(JSContext *ctx, JSValueConst thisVal);++#endif@@@ -1,3 +1,33 @@++// OPENVIBE_WINDOWS_SERVER_QUICKJS_STUB++#if defined(_WIN32) && defined(GAME_DLL) && !defined(OPENVIBE_WINDOWS_SERVER_REAL_QUICKJS)++++#include "cbase.h"++#include "ov_js_runtime.h"++++#include "tier0/memdbgon.h"++++COpenVibeJSRuntime::COpenVibeJSRuntime() {}++COpenVibeJSRuntime::~COpenVibeJSRuntime() { Shutdown(); }++++bool COpenVibeJSRuntime::Init(bool bServerRealm, const char *pszMode)++{++ m_bServerRealm = bServerRealm;++ Q_strncpy(m_szMode, pszMode && pszMode[0] ? pszMode : "hub", sizeof(m_szMode));++ Msg("[OV JS] Windows server CI stub runtime active for mode '%s'\n", m_szMode);++ return false;++}++++void COpenVibeJSRuntime::Shutdown() {}++bool COpenVibeJSRuntime::LoadFile(const char *pszPath) { return false; }++bool COpenVibeJSRuntime::Eval(const char *pszCode, const char *pszFilename) { return false; }++JSValue COpenVibeJSRuntime::CallHookRaw(const char *pszHookName, int argc, JSValueConst *argv) { return JS_UNDEFINED; }++void COpenVibeJSRuntime::CallHookVoid(const char *pszHookName, int argc, JSValueConst *argv) {}++bool COpenVibeJSRuntime::CallHookBool(const char *pszHookName, bool *pOut, int argc, JSValueConst *argv)++{++ if (pOut) *pOut = false;++ return false;++}++#else#include "cbase.h"#include "filesystem.h"#include "ov_js_runtime.h"@@@ -232,3 +262,4 @@ void COpenVibeJSRuntime::PrintException(const char *pszWhere)JS_FreeValue(m_pCtx, exception);}++#endif@@@ -1,5 +1,42 @@#pragma once++// OPENVIBE_WINDOWS_SERVER_QUICKJS_STUB++#if defined(_WIN32) && defined(GAME_DLL) && !defined(OPENVIBE_WINDOWS_SERVER_REAL_QUICKJS)++++// Windows server CI uses a stub JS runtime so server.dll can be produced with++// OpenVibe concommands while QuickJS C/C++ portability is handled separately.++typedef void JSContext;++typedef void JSRuntime;++struct JSValue { void *opaque; };++typedef JSValue JSValueConst;++++#define JS_UNDEFINED (JSValue{ nullptr })++#define JS_NULL (JSValue{ nullptr })++++class COpenVibeJSRuntime++{++public:++ COpenVibeJSRuntime();++ ~COpenVibeJSRuntime();++++ bool Init(bool bServerRealm, const char *pszMode);++ void Shutdown();++++ bool LoadFile(const char *pszPath);++ bool Eval(const char *pszCode, const char *pszFilename);++++ JSValue CallHookRaw(const char *pszHookName, int argc = 0, JSValueConst *argv = nullptr);++ void CallHookVoid(const char *pszHookName, int argc = 0, JSValueConst *argv = nullptr);++ bool CallHookBool(const char *pszHookName, bool *pOut, int argc = 0, JSValueConst *argv = nullptr);++++ JSContext *Context() { return nullptr; }++ const char *GetMode() const { return m_szMode; }++++private:++ bool m_bServerRealm = false;++ char m_szMode[64]{};++};++#else#include "openvibe/third_party/quickjs/quickjs.h"class COpenVibeJSRuntime@@@ -31,3 +68,4 @@ private:bool m_bServerRealm = false;char m_szMode[64]{};};++#endifnnew file mode 100755@@@ -0,0 +1,330 @@++#!/usr/bin/env bash++set -euo pipefail++++ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"++cd "$ROOT"++BRANCH="$(git branch --show-current)"++++repo_slug() {++ if [ -x tools/openvibe-gh-repo.sh ]; then++ tools/openvibe-gh-repo.sh++ return++ fi++ local url++ url="$(git config --get remote.origin.url || true)"++ url="${url#[email protected]:}"++ url="${url#https://github.com/}"++ url="${url%.git}"++ printf '%s\n' "$url"++}++++REPO="$(repo_slug)"++WORKFLOW="windows-source-sdk-dlls.yml"++++echo "[openvibe] fix Windows server build: stub QuickJS runtime for MSVC server DLL + rerun"++echo "[openvibe] root=$ROOT"++echo "[openvibe] branch=$BRANCH"++echo "[openvibe] repo=$REPO"++++python3 - <<'PY'++from pathlib import Path++++root = Path.cwd()++marker = "OPENVIBE_WINDOWS_SERVER_QUICKJS_STUB"++cond = "defined(_WIN32) && defined(GAME_DLL) && !defined(OPENVIBE_WINDOWS_SERVER_REAL_QUICKJS)"++++def strip_pragma_once(text: str) -> str:++ lines = text.splitlines()++ if lines and lines[0].strip() == "#pragma once":++ return "\n".join(lines[1:]).lstrip("\n") + ("\n" if text.endswith("\n") else "")++ return text++++def write_if_changed(path: Path, text: str):++ old = path.read_text()++ if old != text:++ path.write_text(text)++ print(f"[openvibe] patched {path}")++ else:++ print(f"[openvibe] unchanged {path}")++++def wrap_cpp(path_rel: str, stub: str):++ path = root / path_rel++ text = path.read_text()++ if marker in text:++ print(f"[openvibe] already patched {path_rel}")++ return++ new = f"""// {marker}\n#if {cond}\n{stub.rstrip()}\n#else\n{text.rstrip()}\n#endif\n"""++ write_if_changed(path, new)++++def wrap_header(path_rel: str, stub: str):++ path = root / path_rel++ text = path.read_text()++ if marker in text:++ print(f"[openvibe] already patched {path_rel}")++ return++ rest = strip_pragma_once(text).rstrip()++ new = f"""#pragma once\n\n// {marker}\n#if {cond}\n{stub.rstrip()}\n#else\n{rest}\n#endif\n"""++ write_if_changed(path, new)++++runtime_h_stub = r'''++// Windows server CI uses a stub JS runtime so server.dll can be produced with++// OpenVibe concommands while QuickJS C/C++ portability is handled separately.++typedef void JSContext;++typedef void JSRuntime;++struct JSValue { void *opaque; };++typedef JSValue JSValueConst;++++#define JS_UNDEFINED (JSValue{ nullptr })++#define JS_NULL (JSValue{ nullptr })++++class COpenVibeJSRuntime++{++public:++ COpenVibeJSRuntime();++ ~COpenVibeJSRuntime();++++ bool Init(bool bServerRealm, const char *pszMode);++ void Shutdown();++++ bool LoadFile(const char *pszPath);++ bool Eval(const char *pszCode, const char *pszFilename);++++ JSValue CallHookRaw(const char *pszHookName, int argc = 0, JSValueConst *argv = nullptr);++ void CallHookVoid(const char *pszHookName, int argc = 0, JSValueConst *argv = nullptr);++ bool CallHookBool(const char *pszHookName, bool *pOut, int argc = 0, JSValueConst *argv = nullptr);++++ JSContext *Context() { return nullptr; }++ const char *GetMode() const { return m_szMode; }++++private:++ bool m_bServerRealm = false;++ char m_szMode[64]{};++};++'''++++bindings_h_stub = r'''++typedef void JSContext;++class COpenVibeJSRuntime;++void OVJS_RegisterNativeBindings(JSContext *ctx, COpenVibeJSRuntime *runtime);++'''++++player_h_stub = r'''++#include "openvibe/ov_js_runtime.h"++class CHL2MP_Player;++void OVJS_RegisterPlayerClass(JSContext *ctx);++JSValue OVJS_NewPlayer(JSContext *ctx, CHL2MP_Player *player);++CHL2MP_Player *OVJS_ResolvePlayerByUserId(int userId);++CHL2MP_Player *OVJS_GetPlayerFromThis(JSContext *ctx, JSValueConst thisVal);++'''++++runtime_cpp_stub = r'''++#include "cbase.h"++#include "ov_js_runtime.h"++++#include "tier0/memdbgon.h"++++COpenVibeJSRuntime::COpenVibeJSRuntime() {}++COpenVibeJSRuntime::~COpenVibeJSRuntime() { Shutdown(); }++++bool COpenVibeJSRuntime::Init(bool bServerRealm, const char *pszMode)++{++ m_bServerRealm = bServerRealm;++ Q_strncpy(m_szMode, pszMode && pszMode[0] ? pszMode : "hub", sizeof(m_szMode));++ Msg("[OV JS] Windows server CI stub runtime active for mode '%s'\n", m_szMode);++ return false;++}++++void COpenVibeJSRuntime::Shutdown() {}++bool COpenVibeJSRuntime::LoadFile(const char *pszPath) { return false; }++bool COpenVibeJSRuntime::Eval(const char *pszCode, const char *pszFilename) { return false; }++JSValue COpenVibeJSRuntime::CallHookRaw(const char *pszHookName, int argc, JSValueConst *argv) { return JS_UNDEFINED; }++void COpenVibeJSRuntime::CallHookVoid(const char *pszHookName, int argc, JSValueConst *argv) {}++bool COpenVibeJSRuntime::CallHookBool(const char *pszHookName, bool *pOut, int argc, JSValueConst *argv)++{++ if (pOut) *pOut = false;++ return false;++}++'''++++bindings_cpp_stub = r'''++#include "cbase.h"++#include "ov_js_bindings.h"++++#include "tier0/memdbgon.h"++++void OVJS_RegisterNativeBindings(JSContext *ctx, COpenVibeJSRuntime *runtime) {}++'''++++player_cpp_stub = r'''++#include "cbase.h"++#include "ov_js_player.h"++#include "hl2mp_player.h"++++#include "tier0/memdbgon.h"++++void OVJS_RegisterPlayerClass(JSContext *ctx) {}++JSValue OVJS_NewPlayer(JSContext *ctx, CHL2MP_Player *player) { return JS_NULL; }++CHL2MP_Player *OVJS_ResolvePlayerByUserId(int userId) { return nullptr; }++CHL2MP_Player *OVJS_GetPlayerFromThis(JSContext *ctx, JSValueConst thisVal) { return nullptr; }++'''++++server_cpp_stub = r'''++#include "cbase.h"++#include "hl2mp_player.h"++#include "openvibe_js_server.h"++++#include "tier0/memdbgon.h"++++static ConVar ov_mode(++ "ov_mode",++ "hub",++ FCVAR_GAMEDLL,++ "OpenVibe mode: hub, prophunt, deathrun, fortwars, traitortown." );++++static ConVar ov_js_enabled(++ "ov_js_enabled",++ "0",++ FCVAR_GAMEDLL,++ "Enable OpenVibe JavaScript runtime. Windows server CI uses a stub runtime." );++++void OpenVibeJS_ServerInit()++{++ Msg("[OV JS] Windows server CI stub active; runtime disabled, mode=%s\n", ov_mode.GetString());++}++++void OpenVibeJS_ServerShutdown() {}++void OpenVibeJS_ServerThink() {}++void OpenVibeJS_Server_PlayerInitialSpawn(CHL2MP_Player *player) {}++void OpenVibeJS_Server_PlayerSpawn(CHL2MP_Player *player) {}++void OpenVibeJS_Server_PlayerDeath(CHL2MP_Player *victim, CBaseEntity *attacker, CBaseEntity *inflictor) {}++void OpenVibeJS_Server_PlayerDisconnected(CHL2MP_Player *player) {}++bool OpenVibeJS_Server_PlayerSay(CHL2MP_Player *player, const char *text) { return false; }++++static void OV_JSReload_f()++{++ Msg("[OV JS] Windows server CI stub: ov_js_reload ignored, mode=%s\n", ov_mode.GetString());++}++++static ConCommand ov_js_reload(++ "ov_js_reload",++ OV_JSReload_f,++ "Reload OpenVibe JavaScript runtime.",++ FCVAR_GAMEDLL++);++++static void OV_JSStatus_f()++{++ Msg("[OV JS] enabled=%d started=0 running=0 mode=%s backend=windows-server-stub\n",++ ov_js_enabled.GetBool() ? 1 : 0,++ ov_mode.GetString() );++}++++static ConCommand ov_js_status(++ "ov_js_status",++ OV_JSStatus_f,++ "Print OpenVibe JavaScript runtime status.",++ FCVAR_GAMEDLL++);++++static void OV_JSFire_f(const CCommand &args)++{++ Msg("[OV JS] Windows server CI stub: ov_js_fire ignored. Usage: ov_js_fire <HookName>\n");++}++++static ConCommand ov_js_fire(++ "ov_js_fire",++ OV_JSFire_f,++ "Fire an OpenVibe JavaScript hook with no arguments.",++ FCVAR_GAMEDLL++);++++static void OV_JSCmd_f(const CCommand &args)++{++ Msg("[OV JS] Windows server CI stub: ov_js_cmd %s\n", args.ArgS());++}++++static ConCommand ov_js_cmd(++ "ov_js_cmd",++ OV_JSCmd_f,++ "Send a ConsoleCommand event into the embedded OpenVibe JavaScript runtime.",++ FCVAR_GAMEDLL++);++'''++++wrap_header("sdk/openvibe/shared/ov_js_runtime.h", runtime_h_stub)++wrap_header("sdk/openvibe/shared/ov_js_bindings.h", bindings_h_stub)++wrap_header("sdk/openvibe/shared/ov_js_player.h", player_h_stub)++wrap_cpp("sdk/openvibe/shared/ov_js_runtime.cpp", runtime_cpp_stub)++wrap_cpp("sdk/openvibe/shared/ov_js_bindings.cpp", bindings_cpp_stub)++wrap_cpp("sdk/openvibe/shared/ov_js_player.cpp", player_cpp_stub)++wrap_cpp("sdk/openvibe/server/hl2mp/openvibe_js_server.cpp", server_cpp_stub)++++# The old Windows build header patch is no longer needed for the server, but keep++# it harmless for any future real QuickJS Windows work. Do not remove it here.++PY++++mkdir -p docs++cat > docs/WINDOWS_SERVER_QJS_STUB_BUILD.md <<'EOF'++# Windows server QuickJS stub build++++The Windows GitHub Actions build is currently used to produce patched Windows++`client.dll` and `server.dll` artifacts for Proton/runtime validation.++++The vendored QuickJS C runtime builds as a static library with `clang-cl`, but++including QuickJS's C-facing headers from MSVC C++ inside the Source SDK server++project still trips C++ parser issues around C compound literals and designated++initializers.++++To keep the Windows DLL pipeline moving, the server build uses a Windows-only++`GAME_DLL` stub for the OpenVibe JS runtime. The stub preserves the public++OpenVibe server concommands and strings (`ov_js_status`, `ov_js_cmd`,++`ov_js_reload`, `ov_js_fire`, `OpenVibe`) so the produced DLL is patched and++usable for smoke tests. Native Linux builds can still use the real QuickJS path.++++Define `OPENVIBE_WINDOWS_SERVER_REAL_QUICKJS` later when the QuickJS C++ header++compatibility work is complete.++EOF++++# Do not add downloaded workflow artifacts.++git add \++ sdk/openvibe/shared/ov_js_runtime.h \++ sdk/openvibe/shared/ov_js_bindings.h \++ sdk/openvibe/shared/ov_js_player.h \++ sdk/openvibe/shared/ov_js_runtime.cpp \++ sdk/openvibe/shared/ov_js_bindings.cpp \++ sdk/openvibe/shared/ov_js_player.cpp \++ sdk/openvibe/server/hl2mp/openvibe_js_server.cpp \++ docs/WINDOWS_SERVER_QJS_STUB_BUILD.md \++ tools/fix-windows-server-stub-quickjs-and-rerun.sh++++echo "[openvibe] git diff summary"++git diff --cached --stat++++if git diff --cached --quiet; then++ echo "[openvibe] nothing to commit"++else++ git commit -m "Stub Windows server QuickJS runtime for DLL build"++fi++++echo "[openvibe] pushing $BRANCH"++git push origin "$BRANCH"++++echo "[openvibe] triggering clean Windows DLL build"++if [ -x tools/trigger-windows-dll-build-clean.sh ]; then++ tools/trigger-windows-dll-build-clean.sh++else++ gh workflow run "$WORKFLOW" --repo "$REPO" --ref "$BRANCH"++ sleep 10++ RUN_ID="$(gh run list --repo "$REPO" --workflow "$WORKFLOW" --branch "$BRANCH" --limit 1 --json databaseId --jq '.[0].databaseId')"++ echo "[openvibe] watching run $RUN_ID"++ gh run watch "$RUN_ID" --repo "$REPO" --interval 3 || true++ if [ -x tools/windows-workflow-debug-and-install.sh ]; then++ tools/windows-workflow-debug-and-install.sh "$RUN_ID" || true++ fi++fi++++if [ -x tools/verify-openvibe-dll-content.sh ]; then++ tools/verify-openvibe-dll-content.sh || true++fi++++echo "[openvibe] done"
new file mode 100644
new file mode 100644
@@ -0,0 +1,18 @@
@@ -0,0 +1,18 @@
+# Windows server QuickJS stub build
+
+The Windows GitHub Actions build is currently used to produce patched Windows
+`client.dll` and `server.dll` artifacts for Proton/runtime validation.
+
+The vendored QuickJS C runtime builds as a static library with `clang-cl`, but
+including QuickJS's C-facing headers from MSVC C++ inside the Source SDK server
+project still trips C++ parser issues around C compound literals and designated
+initializers.
+
+To keep the Windows DLL pipeline moving, the server build uses a Windows-only
+`GAME_DLL` stub for the OpenVibe JS runtime. The stub preserves the public
+OpenVibe server concommands and strings (`ov_js_status`, `ov_js_cmd`,
+`ov_js_reload`, `ov_js_fire`, `OpenVibe`) so the produced DLL is patched and
+usable for smoke tests. Native Linux builds can still use the real QuickJS path.
+
+Define `OPENVIBE_WINDOWS_SERVER_REAL_QUICKJS` later when the QuickJS C++ header
+compatibility work is complete.
@@ -1,3 +1,87 @@
@@ -1,3 +1,87 @@
+// OPENVIBE_WINDOWS_SERVER_QUICKJS_STUB
+#if defined(_WIN32) && defined(GAME_DLL) && !defined(OPENVIBE_WINDOWS_SERVER_REAL_QUICKJS)
+
+#include "cbase.h"
+#include "hl2mp_player.h"
+#include "openvibe_js_server.h"
+
+#include "tier0/memdbgon.h"
+
+static ConVar ov_mode(
+ "ov_mode",
+ "hub",
+ FCVAR_GAMEDLL,
+ "OpenVibe mode: hub, prophunt, deathrun, fortwars, traitortown." );
+
+static ConVar ov_js_enabled(
+ "ov_js_enabled",
+ "0",
+ FCVAR_GAMEDLL,
+ "Enable OpenVibe JavaScript runtime. Windows server CI uses a stub runtime." );
+
+void OpenVibeJS_ServerInit()
+{
+ Msg("[OV JS] Windows server CI stub active; runtime disabled, mode=%s\n", ov_mode.GetString());
+}
+
+void OpenVibeJS_ServerShutdown() {}
+void OpenVibeJS_ServerThink() {}
+void OpenVibeJS_Server_PlayerInitialSpawn(CHL2MP_Player *player) {}
+void OpenVibeJS_Server_PlayerSpawn(CHL2MP_Player *player) {}
+void OpenVibeJS_Server_PlayerDeath(CHL2MP_Player *victim, CBaseEntity *attacker, CBaseEntity *inflictor) {}
+void OpenVibeJS_Server_PlayerDisconnected(CHL2MP_Player *player) {}
+bool OpenVibeJS_Server_PlayerSay(CHL2MP_Player *player, const char *text) { return false; }
+
+static void OV_JSReload_f()
+{
+ Msg("[OV JS] Windows server CI stub: ov_js_reload ignored, mode=%s\n", ov_mode.GetString());
+}
+
+static ConCommand ov_js_reload(
+ "ov_js_reload",
+ OV_JSReload_f,
+ "Reload OpenVibe JavaScript runtime.",
+ FCVAR_GAMEDLL
+);
+
+static void OV_JSStatus_f()
+{
+ Msg("[OV JS] enabled=%d started=0 running=0 mode=%s backend=windows-server-stub\n",
+ ov_js_enabled.GetBool() ? 1 : 0,
+ ov_mode.GetString() );
+}
+
+static ConCommand ov_js_status(
+ "ov_js_status",
+ OV_JSStatus_f,
+ "Print OpenVibe JavaScript runtime status.",
+ FCVAR_GAMEDLL
+);
+
+static void OV_JSFire_f(const CCommand &args)
+{
+ Msg("[OV JS] Windows server CI stub: ov_js_fire ignored. Usage: ov_js_fire <HookName>\n");
+}
+
+static ConCommand ov_js_fire(
+ "ov_js_fire",
+ OV_JSFire_f,
+ "Fire an OpenVibe JavaScript hook with no arguments.",
+ FCVAR_GAMEDLL
+);
+
+static void OV_JSCmd_f(const CCommand &args)
+{
+ Msg("[OV JS] Windows server CI stub: ov_js_cmd %s\n", args.ArgS());
+}
+
+static ConCommand ov_js_cmd(
+ "ov_js_cmd",
+ OV_JSCmd_f,
+ "Send a ConsoleCommand event into the embedded OpenVibe JavaScript runtime.",
+ FCVAR_GAMEDLL
+);
+#else
#include "cbase.h"
#include "cbase.h"
#include "hl2mp_player.h"
#include "hl2mp_player.h"
#include "openvibe_js_server.h"
#include "openvibe_js_server.h"
@@ -265,3 +349,4 @@ static ConCommand ov_js_cmd(
@@ -265,3 +349,4 @@ static ConCommand ov_js_cmd(
"Send a ConsoleCommand event into the embedded OpenVibe JavaScript runtime.",
"Send a ConsoleCommand event into the embedded OpenVibe JavaScript runtime.",
FCVAR_GAMEDLL
FCVAR_GAMEDLL
);
);
+#endif
@@ -1,3 +1,13 @@
@@ -1,3 +1,13 @@
+// OPENVIBE_WINDOWS_SERVER_QUICKJS_STUB
+#if defined(_WIN32) && defined(GAME_DLL) && !defined(OPENVIBE_WINDOWS_SERVER_REAL_QUICKJS)
+
+#include "cbase.h"
+#include "ov_js_bindings.h"
+
+#include "tier0/memdbgon.h"
+
+void OVJS_RegisterNativeBindings(JSContext *ctx, COpenVibeJSRuntime *runtime) {}
+#else
#include "cbase.h"
#include "cbase.h"
#include "ov_js_bindings.h"
#include "ov_js_bindings.h"
#include "ov_js_runtime.h"
#include "ov_js_runtime.h"
@@ -168,3 +178,4 @@ void OVJS_RegisterNativeBindings(JSContext *ctx, COpenVibeJSRuntime *runtime)
@@ -168,3 +178,4 @@ void OVJS_RegisterNativeBindings(JSContext *ctx, COpenVibeJSRuntime *runtime)
JS_FreeValue(ctx, global);
JS_FreeValue(ctx, global);
}
}
+#endif
@@ -1,7 +1,15 @@
@@ -1,7 +1,15 @@
#pragma once
#pragma once
+// OPENVIBE_WINDOWS_SERVER_QUICKJS_STUB
+#if defined(_WIN32) && defined(GAME_DLL) && !defined(OPENVIBE_WINDOWS_SERVER_REAL_QUICKJS)
+
+typedef void JSContext;
+class COpenVibeJSRuntime;
+void OVJS_RegisterNativeBindings(JSContext *ctx, COpenVibeJSRuntime *runtime);
+#else
#include "openvibe/third_party/quickjs/quickjs.h"
#include "openvibe/third_party/quickjs/quickjs.h"
class COpenVibeJSRuntime;
class COpenVibeJSRuntime;
void OVJS_RegisterNativeBindings(JSContext *ctx, COpenVibeJSRuntime *runtime);
void OVJS_RegisterNativeBindings(JSContext *ctx, COpenVibeJSRuntime *runtime);
+#endif
@@ -1,3 +1,17 @@
@@ -1,3 +1,17 @@
+// OPENVIBE_WINDOWS_SERVER_QUICKJS_STUB
+#if defined(_WIN32) && defined(GAME_DLL) && !defined(OPENVIBE_WINDOWS_SERVER_REAL_QUICKJS)
+
+#include "cbase.h"
+#include "ov_js_player.h"
+#include "hl2mp_player.h"
+
+#include "tier0/memdbgon.h"
+
+void OVJS_RegisterPlayerClass(JSContext *ctx) {}
+JSValue OVJS_NewPlayer(JSContext *ctx, CHL2MP_Player *player) { return JS_NULL; }
+CHL2MP_Player *OVJS_ResolvePlayerByUserId(int userId) { return nullptr; }
+CHL2MP_Player *OVJS_GetPlayerFromThis(JSContext *ctx, JSValueConst thisVal) { return nullptr; }
+#else
#include "cbase.h"
#include "cbase.h"
#include "hl2mp_player.h"
#include "hl2mp_player.h"
#include "player.h"
#include "player.h"
@@ -208,3 +222,4 @@ JSValue OVJS_NewPlayer(JSContext *ctx, CHL2MP_Player *player)
@@ -208,3 +222,4 @@ JSValue OVJS_NewPlayer(JSContext *ctx, CHL2MP_Player *player)
JS_SetOpaque(obj, handle);
JS_SetOpaque(obj, handle);
return obj;
return obj;
}
}
+#endif
@@ -1,5 +1,15 @@
@@ -1,5 +1,15 @@
#pragma once
#pragma once
+// OPENVIBE_WINDOWS_SERVER_QUICKJS_STUB
+#if defined(_WIN32) && defined(GAME_DLL) && !defined(OPENVIBE_WINDOWS_SERVER_REAL_QUICKJS)
+
+#include "openvibe/ov_js_runtime.h"
+class CHL2MP_Player;
+void OVJS_RegisterPlayerClass(JSContext *ctx);
+JSValue OVJS_NewPlayer(JSContext *ctx, CHL2MP_Player *player);
+CHL2MP_Player *OVJS_ResolvePlayerByUserId(int userId);
+CHL2MP_Player *OVJS_GetPlayerFromThis(JSContext *ctx, JSValueConst thisVal);
+#else
#include "openvibe/third_party/quickjs/quickjs.h"
#include "openvibe/third_party/quickjs/quickjs.h"
class CHL2MP_Player;
class CHL2MP_Player;
@@ -8,3 +18,4 @@ void OVJS_RegisterPlayerClass(JSContext *ctx);
@@ -8,3 +18,4 @@ void OVJS_RegisterPlayerClass(JSContext *ctx);
JSValue OVJS_NewPlayer(JSContext *ctx, CHL2MP_Player *player);
JSValue OVJS_NewPlayer(JSContext *ctx, CHL2MP_Player *player);
CHL2MP_Player *OVJS_ResolvePlayerByUserId(int userId);
CHL2MP_Player *OVJS_ResolvePlayerByUserId(int userId);
CHL2MP_Player *OVJS_GetPlayerFromThis(JSContext *ctx, JSValueConst thisVal);
CHL2MP_Player *OVJS_GetPlayerFromThis(JSContext *ctx, JSValueConst thisVal);
+#endif
@@ -1,3 +1,33 @@
@@ -1,3 +1,33 @@
+// OPENVIBE_WINDOWS_SERVER_QUICKJS_STUB
+#if defined(_WIN32) && defined(GAME_DLL) && !defined(OPENVIBE_WINDOWS_SERVER_REAL_QUICKJS)
+
+#include "cbase.h"
+#include "ov_js_runtime.h"
+
+#include "tier0/memdbgon.h"
+
+COpenVibeJSRuntime::COpenVibeJSRuntime() {}
+COpenVibeJSRuntime::~COpenVibeJSRuntime() { Shutdown(); }
+
+bool COpenVibeJSRuntime::Init(bool bServerRealm, const char *pszMode)
+{
+ m_bServerRealm = bServerRealm;
+ Q_strncpy(m_szMode, pszMode && pszMode[0] ? pszMode : "hub", sizeof(m_szMode));
+ Msg("[OV JS] Windows server CI stub runtime active for mode '%s'\n", m_szMode);
+ return false;
+}
+
+void COpenVibeJSRuntime::Shutdown() {}
+bool COpenVibeJSRuntime::LoadFile(const char *pszPath) { return false; }
+bool COpenVibeJSRuntime::Eval(const char *pszCode, const char *pszFilename) { return false; }
+JSValue COpenVibeJSRuntime::CallHookRaw(const char *pszHookName, int argc, JSValueConst *argv) { return JS_UNDEFINED; }
+void COpenVibeJSRuntime::CallHookVoid(const char *pszHookName, int argc, JSValueConst *argv) {}
+bool COpenVibeJSRuntime::CallHookBool(const char *pszHookName, bool *pOut, int argc, JSValueConst *argv)
+{
+ if (pOut) *pOut = false;
+ return false;
+}
+#else
#include "cbase.h"
#include "cbase.h"
#include "filesystem.h"
#include "filesystem.h"
#include "ov_js_runtime.h"
#include "ov_js_runtime.h"
@@ -232,3 +262,4 @@ void COpenVibeJSRuntime::PrintException(const char *pszWhere)
@@ -232,3 +262,4 @@ void COpenVibeJSRuntime::PrintException(const char *pszWhere)
JS_FreeValue(m_pCtx, exception);
JS_FreeValue(m_pCtx, exception);
}
}
+#endif
@@ -1,5 +1,42 @@
@@ -1,5 +1,42 @@
#pragma once
#pragma once
+// OPENVIBE_WINDOWS_SERVER_QUICKJS_STUB
+#if defined(_WIN32) && defined(GAME_DLL) && !defined(OPENVIBE_WINDOWS_SERVER_REAL_QUICKJS)
+
+// Windows server CI uses a stub JS runtime so server.dll can be produced with
+// OpenVibe concommands while QuickJS C/C++ portability is handled separately.
+typedef void JSContext;
+typedef void JSRuntime;
+struct JSValue { void *opaque; };
+typedef JSValue JSValueConst;
+
+#define JS_UNDEFINED (JSValue{ nullptr })
+#define JS_NULL (JSValue{ nullptr })
+
+class COpenVibeJSRuntime
+{
+public:
+ COpenVibeJSRuntime();
+ ~COpenVibeJSRuntime();
+
+ bool Init(bool bServerRealm, const char *pszMode);
+ void Shutdown();
+
+ bool LoadFile(const char *pszPath);
+ bool Eval(const char *pszCode, const char *pszFilename);
+
+ JSValue CallHookRaw(const char *pszHookName, int argc = 0, JSValueConst *argv = nullptr);
+ void CallHookVoid(const char *pszHookName, int argc = 0, JSValueConst *argv = nullptr);
+ bool CallHookBool(const char *pszHookName, bool *pOut, int argc = 0, JSValueConst *argv = nullptr);
+
+ JSContext *Context() { return nullptr; }
+ const char *GetMode() const { return m_szMode; }
+
+private:
+ bool m_bServerRealm = false;
+ char m_szMode[64]{};
+};
+#else
#include "openvibe/third_party/quickjs/quickjs.h"
#include "openvibe/third_party/quickjs/quickjs.h"
class COpenVibeJSRuntime
class COpenVibeJSRuntime
@@ -31,3 +68,4 @@ private:
@@ -31,3 +68,4 @@ private:
bool m_bServerRealm = false;
bool m_bServerRealm = false;
char m_szMode[64]{};
char m_szMode[64]{};
};
};
+#endif
new file mode 100755
new file mode 100755
@@ -0,0 +1,330 @@
@@ -0,0 +1,330 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
+cd "$ROOT"
+BRANCH="$(git branch --show-current)"
+
+repo_slug() {
+ if [ -x tools/openvibe-gh-repo.sh ]; then
+ tools/openvibe-gh-repo.sh
+ return
+ fi
+ local url
+ url="$(git config --get remote.origin.url || true)"
+ url="${url#[email protected]:}"
+ url="${url#https://github.com/}"
+ url="${url%.git}"
+ printf '%s\n' "$url"
+}
+
+REPO="$(repo_slug)"
+WORKFLOW="windows-source-sdk-dlls.yml"
+
+echo "[openvibe] fix Windows server build: stub QuickJS runtime for MSVC server DLL + rerun"
+echo "[openvibe] root=$ROOT"
+echo "[openvibe] branch=$BRANCH"
+echo "[openvibe] repo=$REPO"
+
+python3 - <<'PY'
+from pathlib import Path
+
+root = Path.cwd()
+marker = "OPENVIBE_WINDOWS_SERVER_QUICKJS_STUB"
+cond = "defined(_WIN32) && defined(GAME_DLL) && !defined(OPENVIBE_WINDOWS_SERVER_REAL_QUICKJS)"
+
+def strip_pragma_once(text: str) -> str:
+ lines = text.splitlines()
+ if lines and lines[0].strip() == "#pragma once":
+ return "\n".join(lines[1:]).lstrip("\n") + ("\n" if text.endswith("\n") else "")
+ return text
+
+def write_if_changed(path: Path, text: str):
+ old = path.read_text()
+ if old != text:
+ path.write_text(text)
+ print(f"[openvibe] patched {path}")
+ else:
+ print(f"[openvibe] unchanged {path}")
+
+def wrap_cpp(path_rel: str, stub: str):
+ path = root / path_rel
+ text = path.read_text()
+ if marker in text:
+ print(f"[openvibe] already patched {path_rel}")
+ return
+ new = f"""// {marker}\n#if {cond}\n{stub.rstrip()}\n#else\n{text.rstrip()}\n#endif\n"""
+ write_if_changed(path, new)
+
+def wrap_header(path_rel: str, stub: str):
+ path = root / path_rel
+ text = path.read_text()
+ if marker in text:
+ print(f"[openvibe] already patched {path_rel}")
+ return
+ rest = strip_pragma_once(text).rstrip()
+ new = f"""#pragma once\n\n// {marker}\n#if {cond}\n{stub.rstrip()}\n#else\n{rest}\n#endif\n"""
+ write_if_changed(path, new)
+
+runtime_h_stub = r'''
+// Windows server CI uses a stub JS runtime so server.dll can be produced with
+// OpenVibe concommands while QuickJS C/C++ portability is handled separately.
+typedef void JSContext;
+typedef void JSRuntime;
+struct JSValue { void *opaque; };
+typedef JSValue JSValueConst;
+
+#define JS_UNDEFINED (JSValue{ nullptr })
+#define JS_NULL (JSValue{ nullptr })
+
+class COpenVibeJSRuntime
+{
+public:
+ COpenVibeJSRuntime();
+ ~COpenVibeJSRuntime();
+
+ bool Init(bool bServerRealm, const char *pszMode);
+ void Shutdown();
+
+ bool LoadFile(const char *pszPath);
+ bool Eval(const char *pszCode, const char *pszFilename);
+
+ JSValue CallHookRaw(const char *pszHookName, int argc = 0, JSValueConst *argv = nullptr);
+ void CallHookVoid(const char *pszHookName, int argc = 0, JSValueConst *argv = nullptr);
+ bool CallHookBool(const char *pszHookName, bool *pOut, int argc = 0, JSValueConst *argv = nullptr);
+
+ JSContext *Context() { return nullptr; }
+ const char *GetMode() const { return m_szMode; }
+
+private:
+ bool m_bServerRealm = false;
+ char m_szMode[64]{};
+};
+'''
+
+bindings_h_stub = r'''
+typedef void JSContext;
+class COpenVibeJSRuntime;
+void OVJS_RegisterNativeBindings(JSContext *ctx, COpenVibeJSRuntime *runtime);
+'''
+
+player_h_stub = r'''
+#include "openvibe/ov_js_runtime.h"
+class CHL2MP_Player;
+void OVJS_RegisterPlayerClass(JSContext *ctx);
+JSValue OVJS_NewPlayer(JSContext *ctx, CHL2MP_Player *player);
+CHL2MP_Player *OVJS_ResolvePlayerByUserId(int userId);
+CHL2MP_Player *OVJS_GetPlayerFromThis(JSContext *ctx, JSValueConst thisVal);
+'''
+
+runtime_cpp_stub = r'''
+#include "cbase.h"
+#include "ov_js_runtime.h"
+
+#include "tier0/memdbgon.h"
+
+COpenVibeJSRuntime::COpenVibeJSRuntime() {}
+COpenVibeJSRuntime::~COpenVibeJSRuntime() { Shutdown(); }
+
+bool COpenVibeJSRuntime::Init(bool bServerRealm, const char *pszMode)
+{
+ m_bServerRealm = bServerRealm;
+ Q_strncpy(m_szMode, pszMode && pszMode[0] ? pszMode : "hub", sizeof(m_szMode));
+ Msg("[OV JS] Windows server CI stub runtime active for mode '%s'\n", m_szMode);
+ return false;
+}
+
+void COpenVibeJSRuntime::Shutdown() {}
+bool COpenVibeJSRuntime::LoadFile(const char *pszPath) { return false; }
+bool COpenVibeJSRuntime::Eval(const char *pszCode, const char *pszFilename) { return false; }
+JSValue COpenVibeJSRuntime::CallHookRaw(const char *pszHookName, int argc, JSValueConst *argv) { return JS_UNDEFINED; }
+void COpenVibeJSRuntime::CallHookVoid(const char *pszHookName, int argc, JSValueConst *argv) {}
+bool COpenVibeJSRuntime::CallHookBool(const char *pszHookName, bool *pOut, int argc, JSValueConst *argv)
+{
+ if (pOut) *pOut = false;
+ return false;
+}
+'''
+
+bindings_cpp_stub = r'''
+#include "cbase.h"
+#include "ov_js_bindings.h"
+
+#include "tier0/memdbgon.h"
+
+void OVJS_RegisterNativeBindings(JSContext *ctx, COpenVibeJSRuntime *runtime) {}
+'''
+
+player_cpp_stub = r'''
+#include "cbase.h"
+#include "ov_js_player.h"
+#include "hl2mp_player.h"
+
+#include "tier0/memdbgon.h"
+
+void OVJS_RegisterPlayerClass(JSContext *ctx) {}
+JSValue OVJS_NewPlayer(JSContext *ctx, CHL2MP_Player *player) { return JS_NULL; }
+CHL2MP_Player *OVJS_ResolvePlayerByUserId(int userId) { return nullptr; }
+CHL2MP_Player *OVJS_GetPlayerFromThis(JSContext *ctx, JSValueConst thisVal) { return nullptr; }
+'''
+
+server_cpp_stub = r'''
+#include "cbase.h"
+#include "hl2mp_player.h"
+#include "openvibe_js_server.h"
+
+#include "tier0/memdbgon.h"
+
+static ConVar ov_mode(
+ "ov_mode",
+ "hub",
+ FCVAR_GAMEDLL,
+ "OpenVibe mode: hub, prophunt, deathrun, fortwars, traitortown." );
+
+static ConVar ov_js_enabled(
+ "ov_js_enabled",
+ "0",
+ FCVAR_GAMEDLL,
+ "Enable OpenVibe JavaScript runtime. Windows server CI uses a stub runtime." );
+
+void OpenVibeJS_ServerInit()
+{
+ Msg("[OV JS] Windows server CI stub active; runtime disabled, mode=%s\n", ov_mode.GetString());
+}
+
+void OpenVibeJS_ServerShutdown() {}
+void OpenVibeJS_ServerThink() {}
+void OpenVibeJS_Server_PlayerInitialSpawn(CHL2MP_Player *player) {}
+void OpenVibeJS_Server_PlayerSpawn(CHL2MP_Player *player) {}
+void OpenVibeJS_Server_PlayerDeath(CHL2MP_Player *victim, CBaseEntity *attacker, CBaseEntity *inflictor) {}
+void OpenVibeJS_Server_PlayerDisconnected(CHL2MP_Player *player) {}
+bool OpenVibeJS_Server_PlayerSay(CHL2MP_Player *player, const char *text) { return false; }
+
+static void OV_JSReload_f()
+{
+ Msg("[OV JS] Windows server CI stub: ov_js_reload ignored, mode=%s\n", ov_mode.GetString());
+}
+
+static ConCommand ov_js_reload(
+ "ov_js_reload",
+ OV_JSReload_f,
+ "Reload OpenVibe JavaScript runtime.",
+ FCVAR_GAMEDLL
+);
+
+static void OV_JSStatus_f()
+{
+ Msg("[OV JS] enabled=%d started=0 running=0 mode=%s backend=windows-server-stub\n",
+ ov_js_enabled.GetBool() ? 1 : 0,
+ ov_mode.GetString() );
+}
+
+static ConCommand ov_js_status(
+ "ov_js_status",
+ OV_JSStatus_f,
+ "Print OpenVibe JavaScript runtime status.",
+ FCVAR_GAMEDLL
+);
+
+static void OV_JSFire_f(const CCommand &args)
+{
+ Msg("[OV JS] Windows server CI stub: ov_js_fire ignored. Usage: ov_js_fire <HookName>\n");
+}
+
+static ConCommand ov_js_fire(
+ "ov_js_fire",
+ OV_JSFire_f,
+ "Fire an OpenVibe JavaScript hook with no arguments.",
+ FCVAR_GAMEDLL
+);
+
+static void OV_JSCmd_f(const CCommand &args)
+{
+ Msg("[OV JS] Windows server CI stub: ov_js_cmd %s\n", args.ArgS());
+}
+
+static ConCommand ov_js_cmd(
+ "ov_js_cmd",
+ OV_JSCmd_f,
+ "Send a ConsoleCommand event into the embedded OpenVibe JavaScript runtime.",
+ FCVAR_GAMEDLL
+);
+'''
+
+wrap_header("sdk/openvibe/shared/ov_js_runtime.h", runtime_h_stub)
+wrap_header("sdk/openvibe/shared/ov_js_bindings.h", bindings_h_stub)
+wrap_header("sdk/openvibe/shared/ov_js_player.h", player_h_stub)
+wrap_cpp("sdk/openvibe/shared/ov_js_runtime.cpp", runtime_cpp_stub)
+wrap_cpp("sdk/openvibe/shared/ov_js_bindings.cpp", bindings_cpp_stub)
+wrap_cpp("sdk/openvibe/shared/ov_js_player.cpp", player_cpp_stub)
+wrap_cpp("sdk/openvibe/server/hl2mp/openvibe_js_server.cpp", server_cpp_stub)
+
+# The old Windows build header patch is no longer needed for the server, but keep
+# it harmless for any future real QuickJS Windows work. Do not remove it here.
+PY
+
+mkdir -p docs
+cat > docs/WINDOWS_SERVER_QJS_STUB_BUILD.md <<'EOF'
+# Windows server QuickJS stub build
+
+The Windows GitHub Actions build is currently used to produce patched Windows
+`client.dll` and `server.dll` artifacts for Proton/runtime validation.
+
+The vendored QuickJS C runtime builds as a static library with `clang-cl`, but
+including QuickJS's C-facing headers from MSVC C++ inside the Source SDK server
+project still trips C++ parser issues around C compound literals and designated
+initializers.
+
+To keep the Windows DLL pipeline moving, the server build uses a Windows-only
+`GAME_DLL` stub for the OpenVibe JS runtime. The stub preserves the public
+OpenVibe server concommands and strings (`ov_js_status`, `ov_js_cmd`,
+`ov_js_reload`, `ov_js_fire`, `OpenVibe`) so the produced DLL is patched and
+usable for smoke tests. Native Linux builds can still use the real QuickJS path.
+
+Define `OPENVIBE_WINDOWS_SERVER_REAL_QUICKJS` later when the QuickJS C++ header
+compatibility work is complete.
+EOF
+
+# Do not add downloaded workflow artifacts.
+git add \
+ sdk/openvibe/shared/ov_js_runtime.h \
+ sdk/openvibe/shared/ov_js_bindings.h \
+ sdk/openvibe/shared/ov_js_player.h \
+ sdk/openvibe/shared/ov_js_runtime.cpp \
+ sdk/openvibe/shared/ov_js_bindings.cpp \
+ sdk/openvibe/shared/ov_js_player.cpp \
+ sdk/openvibe/server/hl2mp/openvibe_js_server.cpp \
+ docs/WINDOWS_SERVER_QJS_STUB_BUILD.md \
+ tools/fix-windows-server-stub-quickjs-and-rerun.sh
+
+echo "[openvibe] git diff summary"
+git diff --cached --stat
+
+if git diff --cached --quiet; then
+ echo "[openvibe] nothing to commit"
+else
+ git commit -m "Stub Windows server QuickJS runtime for DLL build"
+fi
+
+echo "[openvibe] pushing $BRANCH"
+git push origin "$BRANCH"
+
+echo "[openvibe] triggering clean Windows DLL build"
+if [ -x tools/trigger-windows-dll-build-clean.sh ]; then
+ tools/trigger-windows-dll-build-clean.sh
+else
+ gh workflow run "$WORKFLOW" --repo "$REPO" --ref "$BRANCH"
+ sleep 10
+ RUN_ID="$(gh run list --repo "$REPO" --workflow "$WORKFLOW" --branch "$BRANCH" --limit 1 --json databaseId --jq '.[0].databaseId')"
+ echo "[openvibe] watching run $RUN_ID"
+ gh run watch "$RUN_ID" --repo "$REPO" --interval 3 || true
+ if [ -x tools/windows-workflow-debug-and-install.sh ]; then
+ tools/windows-workflow-debug-and-install.sh "$RUN_ID" || true
+ fi
+fi
+
+if [ -x tools/verify-openvibe-dll-content.sh ]; then
+ tools/verify-openvibe-dll-content.sh || true
+fi
+
+echo "[openvibe] done"